OSChinaclient源代碼學習(3)--輪詢機制的實現
主要以OSChina Androidclient源代碼中Notice的輪詢機制進行解讀。
一、基礎知識
一般IM(即使通訊)的實現有兩種方式:推送和輪詢,推送就是server主動向client發送消息,用特定的協議比方XMPP、MQTT。
還有一種是輪詢,實時性並不高。並且比較耗電。這樣的有分為兩種情況:一段時間發起一次查詢和死循環進行查詢。
參考: http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/0401/1609.html遠端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的由來。
三、總結
- 通過閱讀這一部分的源代碼,我大致理清了client通過輪詢的方式實現IM的同步的過程。
- 遠端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)--輪詢機制的實現