主要以OSChina Androidclient源代码中Notice的轮询机制进行解读。

一、基础知识

  1. 一般IM(即使通讯)的实现有两种方式:推送和轮询,推送就是server主动向client发送消息,用特定的协议比方XMPP、MQTT。

    还有一种是轮询,实时性并不高。并且比較耗电。这样的有分为两种情况:一段时间发起一次查询和死循环进行查询。

    參考: http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0401/1609.html

  2. 远端Service调用:

    a.服务端:

    0.自己定义接口 (AIDL定义接口文件),然后自己主动生成相应的java类。

    1.继承Stub类。复写接口中定义的方法

    2.将1中的类对象作为Service中onBind方法的返回值,也就是将来信息交流的使者。

    b.client

    3.在Activity或其它工具类中。创建ServiceConnection 对象。在onServiceConnected回调方法中,将第二个參数转化赋值给本地变量,通过这个变量进行与远端服务交互(通信)。

以上总结非常简陋,后面我会结合OSChina的Android源代码具体为大家解读。

參考:http://blog.csdn.net/guolin_blog/article/details/9797169

二、源代码解析

1 绑定了服务

首先在MainActivity中绑定了服务(开启了服务)

MainActivity初始化init()中

NoticeUtils.bindToService(this);

当中bindToService实现

public staticboolean bindToService(Context context,
ServiceConnection callback) {
//直接开启本地NoticeService服务,(注:startService方式并不能进行进行通信)
context.startService(new Intent(context, NoticeService.class));
//绑定远程NoticeService 服务,
//csp:为什么同一个服务用两种不同的方式开启?
//answer: 可能是先开启本地服务,然后把绑定本地服务当做远程服务来处理,目的可能是为了创造不同的进程。提高效率?
ServiceBinder sb = new ServiceBinder(callback);
sConnectionMap.put(context, sb); return context.bindService(
(newIntent()).setClass(context, NoticeService.class), sb, 0);
} //最后。总之开启了服务NoticeService

2 採用Service+AlarmManager+Thread方式轮询

在NoticeService中的onCreate方法中用AlarmManager的方式,每隔2分钟运行一次(轮询)请求。看是否有新的消息通知(这样的方式适合通信实时性不高的情况,比方论坛的回复,你并不须要立刻知道别人的回复,晚个1-2分钟是能够接受的。

mAlarmMgr = (AlarmManager)getSystemService(ALARM_SERVICE);
startRequestAlarm();
private voidstartRequestAlarm() {
cancelRequestAlarm();
// 从1秒后開始,每隔2分钟运行getOperationIntent()
mAlarmMgr.setRepeating(AlarmManager.RTC_WAKEUP,
System.currentTimeMillis()+ 1000, INTERVAL,
getOperationIntent());
}

当中getOperationIntent()实现例如以下:

  /**
* OSC採用轮询方式实现消息推送<br>
* 每次被调用都去运行一次{@link #AlarmReceiver}onReceive()方法
*
* @return
*/
privatePendingIntent getOperationIntent() {
Intent intent = new Intent(this, AlarmReceiver.class);
PendingIntent operation = PendingIntent.getBroadcast(this,0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
return operation;
}

在AlarmReceiver类中调用NoticeUtils.requestNotice方法例如以下

该方法首先推断远端Service的onBind返回来的sService对象是否为空。假设连接上了。不为空,则调用该sService对象的方法requestNotice(),否则发送广播,请求訪问server更新Notice

 publicstatic void requestNotice(Context context) {
if (sService != null) {
try {
TLog.log("requestNotice...");
//这里的sService。在以下进行具体的解读
sService.requestNotice();
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
context.sendBroadcast(new Intent(
NoticeService.INTENT_ACTION_REQUEST));
TLog.log("requestNotice,service is null");

最后就是requestNotice的实现了:

    publicstatic void getNotices(AsyncHttpResponseHandler handler) {
RequestParams params = new RequestParams();
params.put("uid", AppContext.getInstance().getLoginUid());
ApiHttpClient.get("action/api/user_notice", params, handler);
}

3 具体解读requestNotice中的sService

还是先上源代码,以下是调用远程Service的步骤:

(0)用AIDL自己定义一 个接口文件INoticeService.aidl

 package net.oschina.app.service;
interface INoticeService
{
void scheduleNotice();
void requestNotice();
void clearNotice(int uid,int type);
}

然后点击保存之后,gen文件夹下就会生成一个相应的Java文件:INoticeService.java

然后。我们打开看一下里面的代码:

/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: E:\\oschina-android-app-v2.2.1\\android-app\\osc-android-app\\src\\net\\oschina\\app\\service\\INoticeService.aidl
*/
package net.oschina.app.service;
public interface INoticeService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements net.oschina.app.service.INoticeService
{
...//Stub类的内容较长。我在此省略
}
public void scheduleNotice() throws android.os.RemoteException;
public void requestNotice() throws android.os.RemoteException;
public void clearNotice(int uid, int type) throws android.os.RemoteException;
}

ADT自带工具aidl.exe生成的代码的看点在于,生成了一个Stub类。该类1.继承了Binder(Binder是IBinder接口的一个实现类)因此将来能够作为Service的onBind方法的返回值,2.实现了自己定义的接口(INoticeService.aidl),将来能够复写或调用。

(1)定义一个Stub的子类:ServiceStub,复写自己定义接口中的三个方法。

 private static class ServiceStub extends INoticeService.Stub {
WeakReference<NoticeService> mService; ServiceStub(NoticeService service) {
mService = new WeakReference<NoticeService>(service);
} @Override
public void clearNotice(int uid, int type) throws RemoteException {
mService.get().clearNotice(uid, type);
} @Override
public void scheduleNotice() throws RemoteException {
mService.get().startRequestAlarm();
} @Override
public void requestNotice() throws RemoteException {
mService.get().requestNotice();
}
}

(2)在NoticeService中将(1)的对象作为onBind方法的返回值返回。


private final IBinder mBinder = new ServiceStub(this); @Override
public IBinder onBind(Intent intent) {
return mBinder;
}

(3)创建ServiceConnection 对象

源代码在NoticeUtils中定义了一个ServiceBinder 类实现ServiceConnection 接口,复写了两个回调函数(当连接远端服务成功和连接远端服务失败)


private static class ServiceBinder implements ServiceConnection {
ServiceConnection mCallback; ServiceBinder(ServiceConnection callback) {
mCallback = callback;
} @Override
public void onServiceConnected(ComponentName className,
android.os.IBinder service) {
//第二个參数service:获取远程Service的onBind方法返回的对象的代理
//以下一句是将代理转换为对象
sService = INoticeService.Stub.asInterface(service);
if (mCallback != null) {
mCallback.onServiceConnected(className, service);
}
} @Override
public void onServiceDisconnected(ComponentName className) {
if (mCallback != null) {
mCallback.onServiceDisconnected(className);
}
sService = null;
}
}

上面的代码告诉我们。假设连接远端服务(NoticeService)成功,则能够通过远端onBind方法返回的对象(即onServiceConnected方法的第二个參数)来进行通信,这里值得一说的是远端服务返回来的仅仅是对象的代理,这一点差别于绑定本地服务,所以要进行转换。转化的方法就是这一句: sService = INoticeService.Stub.asInterface(service)。

至此,我们已经彻底知道了sService的由来。

三、总结

  1. 通过阅读这一部分的源代码,我大致理清了client通过轮询的方式实现IM的同步的过程。
  2. 远端Service的用法。

    这里补充几点:

    a. MainAcitivy中开一个Service。实际上是在同一个线程中。所以不要将耗时操作直接写在Service的onCreate方法里。而应该另外开启一个线程去操作 。

    能够參考:http://blog.csdn.net/guolin_blog/article/details/11952435

    b. 所谓绑定“远端服务”(有的书上叫远程服务)。本质就是IPC(inter process communication)跨进程通信,Android提供了AIDL Service,底层是又Binder机制实现。注意:这里说的“远端”不是C/S中的Server,而是充当提供服务的Service,它能够用来共享,全部訪问远端Service的。都被统称为Client。

    所以,说白了。这里扮演C/S的,能够是两个不同的进程,能够是两个不同的应用程序,当中一个应用程序共享了自己的一个Service组件。充当还有一个应用程序的远端Service。还有一个应用程序则充当Client的角色,能够进行訪问远端Service

最后,欢迎拍砖。。。

作者:项昂之

时间:2015.7.20

转载注明出处:http://blog.csdn.net/csp277?viewmode=list

OSChinaclient源代码学习(3)--轮询机制的实现的更多相关文章

  1. 通过 PowerShell 支持 Azure Traffic Manager 外部端点和权重轮询机制

    Jonathan TulianiAzure网络 - DNS和 Traffic Manager高级项目经理 在北美 TechEd 大会上,我们宣布了 Azure Traffic Manager将支持 ...

  2. 12.nodejs事件轮询机制

    一:nodejs事件轮询机制  就是  函数的执行顺序 <script type="text/javascript"> setImmediate(function(){ ...

  3. nginx基本配置与参数说明以及Nginx中的upstream轮询机制介绍

    转自:http://blog.csdn.net/happydream_c/article/details/54943802 一.nginx简介 Nginx (发音为[engine x])专为性能优化而 ...

  4. 【python】-- RabbitMQ 安装、基本示例、轮询机制

    RabbitMQ MQ全称为Message Queue, 是一种分布式应用程序的的通信方法,它是消费-生产者模型的一个典型的代表,producer往消息队列中不断写入消息,而另一端consumer则可 ...

  5. 面试题: nodejs 的事件轮询机制

    setTimeout(function(){ console.log('setTimeout()执行了') },0) setImmediate(function(){ console.log('set ...

  6. kafka Poll轮询机制与消费者组的重平衡分区策略剖析

    注意本文采用最新版本进行Kafka的内核原理剖析,新版本每一个Consumer通过独立的线程,来管理多个Socket连接,即同时与多个broker通信实现消息的并行读取.这就是新版的技术革新.类似于L ...

  7. JS中的异步以及事件轮询机制

    一.JS为何是单线程的? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊.(在JAVA和c#中的异步 ...

  8. Nginx中的upstream轮询机制介绍

    Nginx中upstream有以下几种方式: 1.轮询(weight=1) 默认选项,当weight不指定时,各服务器weight相同, 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器d ...

  9. RabbitMQ基本示例,轮询机制,no_ack作用

    一.RabbitMQ简介: ''' RabbitMQ就是消息队列 之前不是学了Queue了吗,都是队列还学RabbitMQ干嘛? 干的事情是一样的 Python的Queue有两个, 一个线程Queue ...

随机推荐

  1. Android(java)学习笔记159:多线程断点下载的原理(Android实现)

    之前在Android(java)学习笔记215中,我们从JavaSE的角度去实现了多线程断点下载,下面从Android角度实现这个断点下载: 1. 新建一个Android工程: (1)其中我们先实现布 ...

  2. Solidity 智能合约开发

    需要专用浏览器或部署节点支持. Solidity (中文:固态,固体)是一种语法与Javascript相似的高级语言,它为Ethereum虚拟机(EVM)编译代码而设计. Solidity是静态类型的 ...

  3. WebDriverException: Message: unknown error: Chrome failed to start: crashed

    the last answer WebDriverException: Message: unknown error: Chrome failed to start: crashed

  4. Linux下使用vi命令后退出方式

    退出Vi         当编辑完文件,准备退出Vi返回到shell时,可以使用以下几种方法之一.         在命令模式中,连按两次大写字母Z,若当前编辑的文件曾被修改过,则Vi保存该文件后退出 ...

  5. linux shell 自动判断操作系统release 然后连接FTP yum源的脚本

    如何搭建本地yum源见附录① 如何搭建FTP yum源见附录② 脚本正文: #!/bin/sh# CenterOS config yumOSV=`rpm -q --qf %{version} cent ...

  6. ps---打开文件及图片保存格式

    1.打开图片,可以按Ctrl或者Shift来进行多张图片的选择或者用鼠标框选. 2.勾选图像序列,可以选择命名上有次序的多个图像. 3. PSD是ps里面的标准保存格式,包含颜色.图层.通道.路径.动 ...

  7. awk中RS,ORS,FS,OFS区别与联系

    学习awk时,一定要记得动手去实践,只有在实践中才能发现问题,以下就我在学习中和实践中的经验,总结一下RS,ORS,FS,OFS的区别和联系. 一.OS和ORS 1.RS是记录分隔符,默认的分隔符是\ ...

  8. 大数据学习——采集文件到HDFS

    采集需求:比如业务系统使用log4j生成的日志,日志内容不断增加,需要把追加到日志文件中的数据实时采集到hdfs 根据需求,首先定义以下3大要素 l  采集源,即source——监控文件内容更新 :  ...

  9. 使用using current logfile实现DG备库实时更新

    DG1是主库,DG2是备库:运行在最大可用模式. 实验一:未使用using current logfile参数时,备库在最大可用模式下,不能实时同步备库的更新 -此时需要等待主库进行归档---侧面证明 ...

  10. hdu 3879 最大权闭合图(裸题)

    /* 裸的最大权闭合图 解:参见胡波涛的<最小割模型在信息学竞赛中的应用 #include<stdio.h> #include<string.h> #include< ...