OSChinaclient源代码学习(3)--轮询机制的实现

主要以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

时间: 2024-08-04 13:14:33

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

golang+sse+angular的心跳机制、angullar的轮询机制、time.Duration和time.NewTicker的学习

长连接断开的原因 连接超时,浏览器自动断开连接 进程被杀死 不可抗拒因素 根据不同情况,高效保活的方式 连接超时:心跳机制 进程保活 断线重连 重点心跳机制 产物 心跳包 心跳应答 轮询与心跳区别 轮询一次相当于:建立一次TCP连接+断开连接 心跳:在已有的连接上进行保活 心跳设计要点 心跳包的规格(内容&大小) 心跳发送间隔时间(按照项目的特性进行判断) 断线重连机制(核心= 如何判断长连接的有效性) 心跳具体实现(基于sse的长连接) 客户端做心跳机制:客户端长时间没有反应,使用心跳机制,证

OSChina客户端源码学习(3)--轮询机制的实现

主要以OSChina Android客户端源码中Notice的轮询机制进行解读. 一.基础知识 一般IM(即使通讯)的实现有两种方式:推送和轮询,推送就是服务器主动向客户端发送消息,用特定的协议比如XMPP.MQTT.另一种是轮询,实时性并不高,而且比较耗电.这种有分为两种情况:一段时间发起一次查询和死循环进行查询. 参考: http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0401/1609.html 远端Service调用: a.服

golang轮询机制select的理解

func main() { c := make(chan int) quit := make(chan int) go func() { for i := 0; i < 10; i++ { fmt.Println(<-c) } quit <- 0 }() fibonacci(c, quit) } //select的轮询机制 func fibonacci(c chan int, quit chan int) { x, y := 0, 1 for { select { // select轮询

Keepalived 之 双主模式+DNS轮询机制 实现高负载

一.Keepalived双主模式+DNS轮询机制作用 作用:在单主模式下,备机通常会以等待状态放着,不接受任何数据,导致所有数据请求只往主机-负载均衡发送,做成资源浪费:而双主模式,即创造两个VIP,两个VIP分别放在两台负载均衡的机器上,同时两台主机均为对方的备机,以作VIP的漂移,服务接管作用,加入DNS轮询机制,使客户端的域名分别依次解释到两个VIP上,形成两台负载均衡主机同时对外提供服务.同时也解决了单主模式下的单机性能屏颈. 二.网络拓扑图 三.两台负载均衡主机的Keepalived

3. 闭包_对象组合继承模式_事件轮询机制

1.谈谈闭包? (产生条件.是什么.在哪里.作用.生命周期.缺点) 产生闭包的三个条件: 函数嵌套 内部函数引用外部函数的局部变量 执行外部函数 包含被引用的局部变量的一个"对象",  通过 chrome 开发者工具可以调试查看到,就是 closure,它存在嵌套的内部函数中 作用: 延长了局部变量的存活时间, 让函数外部可以操作(读写)到函数内部的数据(变量/函数) 闭包的生命周期: 产生 :  在嵌套内部函数定义执行完时就产生了(不是在调用) 死亡 :  在嵌套的内部函数成为垃圾对

Linux中的DNS 正向解析与反向解析,轮询机制

1.正向解析 (域名转换为IP地址) 1.vim /etc/named.conf 修改如下 2.编辑dns的副配置文件vim /etc/named.rfc1912.zones 3.cd var/named/cp -p named.localhost bruce.com.zone 复制其文件包括权限属性原文件修改vim bruce.com.zone 4.修改完成后正向解析的记录加好重启服务systemctl restart named 重启服务systemctl stop firewalld 关闭

node.js的事件轮询机制

借助libuv库实现的 概括事件轮询机制:分为六个阶段1.timers 定时器阶段计时和执行到点的定时器回调函数 2.pending callbacks某些系统操作(例如TCP错误类型) 3.idle,prepare 4.poll轮询阶段(轮询队列)如果轮询队列不为空,依次同步取出轮询队列中第一个回调函数,直到轮询队列为空或者达到系统最大限制如果轮询队列为空 如果之前设置过setImmediate函数,直接进入下一个check阶段,如果之前没有设置过setImmediate函数,在当前 poll

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

一.JS为何是单线程的? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊.(在JAVA和c#中的异步均是通过多线程实现的,没有循环队列一说,直接在子线程中完成相关的操作) JavaScript的单线程,与它的用途有关.作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM.这决定了它只能是单线程,否则会带来很复杂的同步问题.比如,假定JavaScript同时有两个线程,一个

轮询与心跳机制

上一篇内容参见() 在上一篇文章中提到的缓存内网服务端会话信息中,外网服务端保存内网服务端会话的有效性以及平台上监控所有内网服务端的网络状况,模仿心跳机制实现,这里在做一点叙诉,关于思路和具体实现. 在很多的平台应用中,都有这样的需求,平台内包括多个子系统或者属于其管控范围内的其他平台,需要对这些系统进行统一的监控,来查看当前的运行状态或者其他运行信息,我们的应用也有这样的一个情况,需要再外网服务端(平台)上监控,其下运行的多个内网服务端的网络状况,查阅了写资料后确立了2种可实现的方式. 1:轮