



版權說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權,請進行舉報或認領
文檔簡介
1、Androidpn 消息推送總結網(wǎng)上關于 Androidpn 的文章不少,但是大都是基于應用層面來介紹這個開源項目的,今天我?guī)Т蠹覐脑创a層面深入的分析 Androidpn 的起看代碼!結構,也算是對最近工作的一個總結吧,不多說,跟我一一、Androidpn 開源項目Androidpn 開源項目托管地址:Androidpn 開源項目自身描述:This is an open source project to provide push notification supportfor Android, a xmpp based notification server and a c nt tool
2、kit.二、源碼分析在程序的DemoAppActivity 中設置通知的 icon 并開啟消息接收服務,代碼如下:Number:1-1ServiceManager serviceManager = new ServiceManager(this);serviceManager.setNotificationIcon(R.drawable.notification); serviceManager.startService();在上面的代碼中可以看到程序對 ServiceManager 進行了初始化操作,在 ServiceManager 類的可以看到程序對傳遞過來的 context 進行了判斷,
3、如果這個 context 是一個 Activity構造函數(shù)中實例,緊接著會獲取對應的包名和類名。之后再去加載 res/raw/perties 配置文件中的參數(shù)信息,并將到的信息和之前從 context 中獲取的包名和類名一起存入首選項中。Number:2-1public ServiceManager(Context context) this.context = context;if (context instanceof Activity) Activity callbackActivity =callbackActivityPackageName(Activity) context;= c
4、allbackActivity.getPackageName(); callbackActivity.getClass().getName();callbackActivityClaame =props = loroperties();apiKey = xmppHostxmppPortprops.getProperty(apiKey, );= props.getProperty(xmppHost,= props.getProperty(xmppPort,);5222);sharedPrefs context.getSharedPreferenContext.MODE_PRIVATE);=(Co
5、nstants.SHARED_PREFERENCE_NAME,Editor editor = sharedPrefs.edit();editor.putString(Constants.API_KEY, apiKey);editor.putString(Constants.VER, ver);editor.putString(Constants.XMPP_HOST, xmppHost);editor.put(Constants.XMPP_PORT,egarse(xmppPort);editor.putString(Constants.CALLBACK_ACTIVITY_PACKAGE_NAME
6、, callbackActivityPackageName);editor.putString(Constants.CALLBACK_ACTIVITY_CLASS_NAME,callbackActivityClamit();ame);完成上述操作之后, 緊接著調用 ServiceManager.startService() 方法來開啟服務, 實際上ServiceManager 只是一個普通的類,方法 ServiceManager.startService() 只是開啟一個子線程來開啟真正的服務類 NotificationService ,許多人認為開一個線程不停的去開啟服務會不會消耗相當是不會
7、的,因為服務的生命周期決定了 onCreate() 方法在服務被創(chuàng)建時調用,該一部分資源?方法只會被調用一次,無論調用多少次 startService() 方法,服務也只被創(chuàng)建一次,細心的讀者會發(fā)現(xiàn) Androidpn 的作者在 NotificationService 類的 onStart(entent,startId) 方法中沒有做任何事,而是在 onCreate() 方法中完成了諸多操作。Number:3-1public void startService() Thread serviceThread = new Thread(new Runnable() public void run(
8、) entent = NotificationService.getent();context.startService(ent););serviceThread.start();下面來看 NotificationService 類的 onCreate() 方法中都完成什么操作?Number:4-1public void onCreate() ephonyManagergetSystemService(Context.=EPHONY_SERVICE);(ephonyManager)sharedPrefs=getSharedPreferen(Constants.SHARED_PREFERENCE
9、_NAME,Context.MODE_PRIVATE);deviceId =ephonyManager.getDeviceId(); Editor editor = sharedPrefs.edit();editor.putString(Constants.DEVICE_ID, deviceId);mit();if(deviceId=null|deviceId.trim().length()=0|deviceId.matches(0+) if (sharedPrefs.contains(EMULATOR_DEVICE_ID) deviceId = sharedPrefs.getString(C
10、onstants.EMULATOR_DEVICE_ID, ); else deviceId=(newStringBuilder(EMU).append(newRandom(System.currentTimeMillis().nextLong().toString(); editor.putString(Constants.EMULATOR_DEVICE_ID, deviceId);mit();xmppManager = new XmppManager(this); taskSubmitter.submit(new Runnable() public void run() Notificati
11、onService.this.start(););在上面的方法中作者獲取了設備號并將設備號存入了首選項中,同時還對在模擬器下運行的情況做了的操作是taskSubmitter 里調用了 處理,這些操作是次要的。真正的 NotificationService.this.start(),這里的 NotificationService.this 完成了 NotificationService 的實例可以看到 NotificationService 類的構造方法中完成了 NotificationReceiver 、化, ConnectivityReceiver、PhoneSeChangeListener
12、、Executors、TaskSubmitter、TaskTracker等類的實例化。Number:5-1public NotificationService() notificationReceiverconnectivityReceiver= new NotificationReceiver();= new ConnectivityReceiver(this);phoneSeListener =new PhoneSeChangeListener(this);executorService = Executors.newSingleThreadExecutor();taskSubmitter
13、taskTracker = new TaskSubmitter(this);new TaskTracker(this);NotificationService的 實 例 化 完 成 后 調 用 的 start() 方法 中了一個廣播接 收 者NotificationReceiver 用來處理從服務器推送過來的消息;同時還了一個廣播接收者來網(wǎng)絡連接狀況, 如果有網(wǎng)絡連接, 則執(zhí)行 xmppManager.connect() ,如果沒有網(wǎng)絡連接, 則執(zhí)行xmppManager.disconnect()。但是在 start() 方法中最終還是會執(zhí)行 xmppManager.connect()。Num
14、ber:6-1private void start() registerNotificationReceiver(); registerConnectivityReceiver(); xmppManager.connect();再來看看 xmppManager.connect() 方法中都做了些什么?程序在這個方法中提交了一個登錄任務:submitLogask(),在提交的登錄任務中又提交了一個任務:submitRegisterTask(),同時將新建的登錄任務添加到任務集合中并交由 TaskTracker 來對添加的任務進行監(jiān)視,此時 TaskTracker的計數(shù)加一。Number:7-1p
15、ublic void connect() submitLogask();Number:7-2private void submitLogask() submitRegisterTask();addTask(new Logask();下面繼續(xù)來看新添加的登錄任務 new Logask() 具體做了什么?看 Number:8-2 代碼,程序在登錄任務線程的 run() 方法中首先去判斷當前客戶端是否已經(jīng)經(jīng)過Number:8-1 。驗證,驗證的代碼請看如果沒有通過驗證:xmppManager 會獲取當前連并接攜帶著從首選項中的 username和 password 執(zhí)行登錄操作,登錄成功后 xmpp
16、Manager 會在登錄成功的連接上添加連接器 PersistentConnectionListener,這個器可以連接關閉和和連接錯誤,并在連接錯誤的情況下執(zhí)行重連操作。接下來會在當前連接上添加濾器 PacketFiltacketFilter 和器 NotificationPacketListenacketListener,包濾器用來校驗從服務器發(fā)送過來的數(shù)據(jù)包是否符合 NotificationIQ 格式,打開 NotificationIQ 類了數(shù)據(jù)包中需要封裝的信息:id、apiKey、title、message、uri。包可以看到這個類中定義器則是用來真正處理從服務器發(fā)過來的數(shù)據(jù)。請看
17、Number : 8-3 代碼,在 NotificationPacketListener 類的prosPacket(Packet packet) 方法中程序首先會判斷獲得的數(shù)據(jù)包是否是 NotificationIQ的一個實例,如果是程序會調用 NotificationIQ 的 getChildElementXML() 方法將數(shù)據(jù)包中攜帶的信息拼裝為一個字符串進行判斷動作是否為發(fā)送廣播,如果動作為發(fā)送廣播,程序會將數(shù)據(jù)包的信息填充到ent 中并發(fā)送廣播, 注意這個廣播中填充到ent 的動作名稱ConstantION_SHOW_NOTIFICATION 為顯示廣播, 這個動作被另一個廣播接收者No
18、tificationReceiver (該廣播接收者在之前的 NotificationService 的 start() 方法中已經(jīng)注冊)所。另 外 需 要 注 意 的 是 , 如 果 客 戶 端 在 登 錄 過 程 中 出 現(xiàn)INVALID_CREDENTIALS_ERROR_CODE = 401 錯誤,在 Number:8-2 的代碼中我們 可 以 看 到 程 序 執(zhí) 行 了xmppManager.reregisterAccount()操 作 和xmppManager.startReconnectionThread() 操作。在 xmppManager.reregisterAccount(
19、)操作中程序會刪除保存在首選項中的 username 和 password 并重新提交登錄任務submitLogask(),在這個登錄任務中依次再嵌套執(zhí)行、連接任務。這些任務執(zhí)行完畢之后程序繼續(xù)調用 xmppManager.startReconnectionThread() 執(zhí)行重連操作。如果客戶端在登錄過程中出現(xiàn)不可預知的錯誤,在 Number:8-2 的代碼中接調用 xmppManager.startReconnectionThread() 來執(zhí)行重連操作??梢钥吹匠绦驁?zhí)直如果已經(jīng)通過驗證:意味著客戶端已經(jīng)登錄成功,程序直接調用 xmppManager.runTask()方法來執(zhí)行之前添加
20、到任務集合中的任務 new Logask(),同時 TaskTracker 的計數(shù)減一。Number:8-1 privatereturnisAuthenticated() connection!=null&connection.isConnected()&connection.isAuthenticated();Number:8-2private class Logask implements Runnable final XmppManager xmppManager;privaogask() this.xmppManager = XmppManager.this;public void r
21、un() if (!xmppManager.isAuthenticated() Log.d(LOGTAG, username= + username); Log.d(LOGTAG, password= + password); try xmppManager.getConnection().login(xmppManager.getUsername(),xmppManager.getPassword(), XMPP_RESOURCE_NAME);Log.d(LOGTAG, Loggedn in sucsfully);if (xmppManager.getConnectionListener()
22、 != null) xmppManager.getConnection().addConnectionListener(xmppManager.getConnectionL istener();PacketFiltacketFilter=newPacketTypeFilter(NotificationIQ.class);PacketListen xmppManager.getNotificationPacketListener();acketListener=connection.addPacketListen xmppManager.runTask(); catch (XMPPExcepti
23、on e) acketListener, packetFilter);Log.e(LOGTAG, Logask.run(). xmpp error);Log.e(LOGTAG, Failed to loge.getMessage();o xmpp server. Caused by: +String INVALID_CREDENTIALS_ERROR_CODE = 401;String errorMessage = e.getMessage();if(errorMessage!=null&errorMessage.contains(INVALID_CREDENTIALS_ERROR_CODE)
24、 xmppManager.reregisterAccount(); return;xmppManager.startReconnectionThread(); catch (Exception e) Log.e(LOGTAG, Logask.run(). other error);Log.e(LOGTAG, Failed to loge.getMessage();o xmpp server. Causedby:+xmppManager.startReconnectionThread(); else Log.i(LOGTAG, Logged in already); xmppManager.ru
25、nTask();Number:8-3public claotificationPacketListener implements PacketListener privatesicfinalStringLOGTAG=LogUtil.makeLogTag(NotificationPacketListener.class);private final XmppManager xmppManager;public NotificationPacketListener(XmppManager xmppManager) this.xmppManager = xmppManager;public void
26、 pro Log.d(LOGTAG,Log.d(LOGTAG,sPacket(Packet packet) NotificationPacketListenrosPacket().);packet.toXML()= + packet.toXML();if (packet instanceof NotificationIQ) NotificationIQ notification = (NotificationIQ) packet;if(notification.getChildElementXML().contains(androidpn:iq:notification) String Str
27、ingStringnotificationId = notification.getId(); notificationApiKey = notification.getApiKey();notificationTitle = notification.getTitle();StringStringnotificationMessage = notification.getMessage();notificationUri = notification.getUri();entent = newent(ConstantION_SHOW_NOTIFICATION);ent.putExtra(Co
28、nstants.NOTIFICATION_ID, notificationId); ent.putExtra(Constants.NOTIFICATION_API_KEY,notificationApiKey);ent.putExtra(Constants.NOTIFICATION_TITLE, notificationTitle);ent.putExtra(Constants.NOTIFICATION_MESSAGE, notificationMessage);ent.putExtra(Constants.NOTIFICATION_URI, notificationUri);xmppMana
29、ger.getContext().sendBroadcast(ent);Number:8-4public void reregisterAccount() removeAccount();submitLogask(); runTask();NotificationReceiver 在接收到 NotificationPacketListener 中發(fā)出的廣播后,先判斷ent 中攜帶的動作和自己所收聽的動作是否一致,如果一致,則繼續(xù)從ent 中取出ent 所攜帶的信息并調用 Notifier 的 notify(String notificationId, String apiKey, String
30、 title, String message, String uri)來發(fā)送通知。Number:9-1public final claotificationReceiver extends BroadcastReceiver privatesicfinalStringLOGTAG=LogUtil.makeLogTag(NotificationReceiver.class);public NotificationReceiver() public void onReceive(Context context,entent) Log.d(LOGTAG, NotificationReceiver.o
31、nReceive().);String action =ent.getAction();Log.d(LOGTAG,action= + action);if (ConstantStringION_SHOW_NOTIFICATION.equalnotificationIdion)=ent.getStringExtra(Constants.NOTIFICATION_ID);StringnotificationApiKey=ent.getStringExtra(Constants.NOTIFICATION_API_KEY);StringnotificationTitle=ent.getStringEx
32、tra(Constants.NOTIFICATION_TITLE);StringnotificationMessage=ent.getStringExtra(Constants.NOTIFICATION_MESSAGE);StringnotificationUri=ent.getStringExtra(Constants.NOTIFICATION_URI);Notifier notifier = new Notifier(context); notifier.notify(notificationId,notificationTitle, notificationMessage, notifi
33、cationUri);notificationApiKey,Notifier 在 發(fā)送通知之 前會先去首 選項中 Constants.SETTINGS_NOTIFICATION_ENABLED用戶的配置 信息,如果 配置信息中 的值為 true,然后開始組裝通知并為通知進行參數(shù)配置,這些操作完成后再調用 NotificationManager 將組裝好發(fā)送出去。至此,在客戶端已經(jīng)的前提下,執(zhí)行的登錄、接收服務器數(shù)據(jù)包、發(fā)送廣播、發(fā)送通知的流程就結束了,添加在當前連接上的 NotificationPacketListener 會一直從服務器發(fā)送過來的數(shù)據(jù)包并重復執(zhí)行數(shù)據(jù)包、發(fā)送廣播、發(fā)送通知的操
34、作。但是需要注意的是從代碼 Number:7-1 至代碼 Number:9-1 的流程是以客戶端已經(jīng)完成提的;如果客戶端是第一次執(zhí)行消息推送的服務,顯然不會直接進入到登錄的邏輯中來,讓為前繼續(xù)任務跳到 Number : 7-2 中的岔路口, 程序在提交登錄任務的嵌套著提交了一個submitRegisterTask(),繼續(xù)來看這個任務做了什么操作。在這個任務中繼續(xù)將新建的任務添加到任務集合中并交由 TaskTracker 來對添加的任務進行監(jiān)視,此時 TaskTracker 的計數(shù)加一;與此同時內(nèi)嵌提交了接任務 submitConnectTask()。Number:10-1private vo
35、id submitRegisterTask() submitConnectTask(); addTask(new RegisterTask();先來看登錄任務中做了什么操作?參看代碼 Number:11-1。:則使用 UUID 生成2個隨機數(shù)作為 username 和 password,同時實例化如果沒有Registration,將創(chuàng)建的濾器和包器添加到當前連接上,然后使用 Registration 實例將生成的 username 和 password 作為屬性添加到 Registration 實例上,再由當前連接調用connection.sendPacket(registration) 向服
36、務器發(fā)送數(shù)據(jù)包執(zhí)行操作。創(chuàng)建的包器會監(jiān)聽并處理服務器會送的數(shù)據(jù)包,PacketListener 在接收到服務器會送的數(shù)據(jù)包后,同樣會判斷數(shù)據(jù)包的格式是否符合濾器中定義的格式,只有格式匹配的情況下進行后續(xù)處理。在格式匹配的情況下,程序繼續(xù)進行判斷:如果服務器返回信息的類型是 IQ.Type.ERROR 則進行報錯處理;如果服務器返回信息的類型是 IQ.Type.RESULT 證明在服務器成功,這時程序會將 username 和 password到首選項中,之后程序直接調用 xmppManager.runTask() 方法來執(zhí)行之前添加到任務集合中的任務 new Logask(),同時 TaskT
37、racker 的計數(shù)減一。:意味著首選項中已經(jīng)有了配置信息,程序直接調用 xmppManager.runTask() 方如果已經(jīng)法來執(zhí)行之前添加到任務集合中的任務 new Logask(),同時 TaskTracker 的計數(shù)減一。Number:11-1private class RegisterTask implements Runnable final XmppManager xmppManager; private RegisterTask() xmppManager = XmppManager.this;public void run() if (!xmppManager.isRegi
38、stered() final Stringfinal StringnewUsername = newRandomUUID();nessword = newRandomUUID();RegistrationPacketFiltregistration = new Registration();acketFilter=newAndFilter(newPacketIDFilter(registration.getPacketID(), necketTypeFilter(IQ.class);PacketListenacketListener = necketListener() public void
39、 prosPacket(Packet packet) Log.d(RegisterTask.PacketListener, sPacket().);Log.d(RegisterTask.PacketListener,propacket=+packet.toXML();if (packet instanceof IQ) IQ response = (IQ) packet;if (response.getType() = IQ.Type.ERROR) if (!response.getError().toString().contains(409) Log.e(LOGTAG, Unknown er
40、ror while registering XMPP account! + response.getError().getCondition(); else if (response.getType() = IQ.Type.RESULT) xmppManager.setUsername(newUsername);xmppManager.setPassword(nessword);newUsername);Log.d(LOGTAG, Log.d(LOGTAG,Editor editorusername= +password= + nessword);= sharedPrefs.edit();ed
41、itor.putString(Constants.XMPP_USERNAME,newUsername);editor.putString(Constants.XMPP_PASSWORD,nessword);mit();Log.i(LOGTAG, Account registered suc xmppManager.runTask();sfully);connection.addPacketListenacketListener, packetFilter);registration.setType(IQ.Type.SET);registration.addAttribute(username,
42、 newUsername);registration.addAttribute(password, ne connection.sendPacket(registration); else ssword);Log.i(LOGTAG, Account registered already);xmppManager.runTask();至此,在客戶端已經(jīng)連接到服務器的前提下,執(zhí)行的、登錄、接收服務器數(shù)據(jù)包、發(fā)送廣播、發(fā)送通知的流程就結束了,添加在當前連接上的 NotificationPacketListener 會一直從服務器發(fā)送過來的數(shù)據(jù)包并重復執(zhí)行數(shù)據(jù)包、發(fā)送廣播、發(fā)送通知的操作。同樣需要注意
43、的是從代碼 Number:10-1 至代碼 Number:11-1 的流程是以客戶端已經(jīng)連接到服務器為前提的;如果客戶端是第一次執(zhí)行消息推送的服務,顯然也不會直接進入到的邏輯中來,讓繼續(xù)跳到 Number:10-1 中的岔路口,程序在提交任務的嵌套著提交了接任務submitConnectTask(),繼續(xù)來看這個連接任務做了什么操作。在這個連接任務中程序直接將新建的連接任務添加到任務集合中并交由 TaskTracker 來對添加的任務進行監(jiān)視,此時 TaskTracker 的計數(shù)加一。Number:12-1private void submitConnectTask() addTask(new
44、 ConnectTask();繼續(xù)來看連接任務中做了什么操作?參看代碼 Number:13-1。如果沒有連接到服務器:程序會從首選項中 xmppHost 和 xmppPort 并使用XMPPConnection 通過配置信息實例化接,然后再由該連接執(zhí)行連接操作。連接成功后,程序調用 xmppManager.runTask() 方法來執(zhí)行之前添加到任務集合中的任務 newRegisterTask(),同時 TaskTracker 的計數(shù)減一。如果已經(jīng)連接到服務器:程序直接調用 xmppManager.runTask() 方法來執(zhí)行之前添加到任務集合中的任務 new RegisterTask(),同時 TaskTracker 的計數(shù)減一。Number:13-1private class ConnectTask implements Runnable final XmppManager xmppManager;private ConnectTask() this.xmppManager = XmppManager.this;public void run() if (!xmppManager.isConnected() / Create the configuration fo
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負責。
- 6. 下載文件中如有侵權或不適當內(nèi)容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025年度廣東省新型城鎮(zhèn)化背景下住宅租賃合同
- 2025年度幼兒園裝修工程保修服務協(xié)議
- 2025年度按揭房屋轉讓與貸款利率調整協(xié)議
- 2025年度養(yǎng)豬場養(yǎng)殖廢棄物處理設施運營管理合同
- 2025年度戶口分家及遺產(chǎn)繼承協(xié)議書模板
- 2025年度海洋資源資產(chǎn)托管與可持續(xù)發(fā)展服務協(xié)議
- 2025年度山林流轉與生態(tài)農(nóng)業(yè)開發(fā)合同
- 2025年度商業(yè)地產(chǎn)合租運營管理服務協(xié)議
- 辦公家具運輸簡易合同
- 2025年度房地產(chǎn)合伙人股權分配與項目開發(fā)協(xié)議
- 2025年國家林業(yè)和草原局管理干部學院招聘歷年高頻重點模擬試卷提升(共500題附帶答案詳解)
- 2025年春季開學典禮活動方案【哪吒版】少年無畏凌云志扶搖直上入云蒼
- 【安排表】2024-2025學年下學期學校升旗儀式安排表 主題班會安排表
- 醫(yī)藥零售行業(yè)數(shù)字化轉型-深度研究
- 2025年度老舊小區(qū)改造施工委托合同范本
- 現(xiàn)場施工人員安全責任協(xié)議書(2篇)
- 四川省自貢市、遂寧市、廣安市等2024-2025學年高一上學期期末考試語文試題 含解析
- 2025年安徽中醫(yī)藥高等專科學校高職單招職業(yè)適應性測試近5年??及鎱⒖碱}庫含答案解析
- 醫(yī)院感染與醫(yī)療器械消毒
- 第七章 力 達標測試卷(含答案)2024-2025學年度人教版物理八年級下冊
- 投行競爭格局-洞察分析
評論
0/150
提交評論