1. 程式人生 > >Android Service完全解析,關於服務你所需知道的一切(上和下)

Android Service完全解析,關於服務你所需知道的一切(上和下)

在上一篇文章中,我們學習了Android Service相關的許多重要內容,包括Service的基本用法、Service和Activity進行通訊、Service的銷燬方式、Service與Thread的關係、以及如何建立前臺Service。以上所提到的這些知識點,基本上涵蓋了大部分日常開發工作當中可能使用到的Service技術。不過關於Service其實還有一個更加高階的使用技巧沒有介紹,即遠端Service的用法。使用遠端Service甚至可以實現Android跨程序通訊的功能,下面就讓我們具體地學習一下。

如果你還沒有看過前面一篇文章,建議先去閱讀一下 Android Service完全解析,關於服務你所需知道的一切(上)

 ,因為本篇文章中涉及到的程式碼是在上篇文章的基礎上進行修改的。

在上篇文章中我們知道了,Service其實是執行在主執行緒裡的,如果直接在Service中處理一些耗時的邏輯,就會導致程式ANR。

讓我們來做個實驗驗證一下吧,修改上一篇文章中建立的ServiceTest專案,在MyService的onCreate()方法中讓執行緒睡眠60秒,如下所示:

  1. publicclass MyService extends Service {  
  2.     ......  
  3.     @Override
  4.     publicvoid onCreate() {  
  5.         super
    .onCreate();  
  6.         Log.d(TAG, "onCreate() executed");  
  7.         try {  
  8.             Thread.sleep(60000);  
  9.         } catch (InterruptedException e) {  
  10.             e.printStackTrace();  
  11.         }  
  12.     }  
  13.     ......  
  14. }  

重新執行後,點選一下Start Service按鈕或Bind Service按鈕,程式就會阻塞住並無法進行任何其它操作,過一段時間後就會彈出ANR的提示框,如下圖所示。

                                                        

之前我們提到過,應該在Service中開啟執行緒去執行耗時任務,這樣就可以有效地避免ANR的出現。

那麼本篇文章的主題是介紹遠端Service的用法,如果將MyService轉換成一個遠端Service,還會不會有ANR的情況呢?讓我們來動手嘗試一下吧。

將一個普通的Service轉換成遠端Service其實非常簡單,只需要在註冊Service的時候將它的android:process屬性指定成:remote就可以了,程式碼如下所示:

  1. <?xmlversion="1.0"encoding="utf-8"?>
  2. <manifestxmlns:android="http://schemas.android.com/apk/res/android"
  3.     package="com.example.servicetest"
  4.     android:versionCode="1"
  5.     android:versionName="1.0">
  6.     ......  
  7.     <service
  8.         android:name="com.example.servicetest.MyService"
  9.         android:process=":remote">
  10.     </service>
  11. </manifest>
現在重新執行程式,並點選一下Start Service按鈕,你會看到控制檯立刻列印了onCreate() executed的資訊,而且主介面並沒有阻塞住,也不會出現ANR。大概過了一分鐘後,又會看到onStartCommand() executed列印了出來。

為什麼將MyService轉換成遠端Service後就不會導致程式ANR了呢?這是由於,使用了遠端Service後,MyService已經在另外一個程序當中運行了,所以只會阻塞該程序中的主執行緒,並不會影響到當前的應用程式。
為了證實一下MyService現在確實已經執行在另外一個程序當中了,我們分別在MainActivity的onCreate()方法和MyService的onCreate()方法里加入一行日誌,打印出各自所在的程序id,如下所示:

  1. Log.d("TAG""process id is " + Process.myPid());  
再次重新執行程式,然後點選一下Start Service按鈕,列印結果如下圖所示:

                       

可以看到,不僅僅是程序id不同了,就連應用程式包名也不一樣了,MyService中列印的那條日誌,包名後面還跟上了:remote標識。

那既然遠端Service這麼好用,乾脆以後我們把所有的Service都轉換成遠端Service吧,還省得再開啟執行緒了。其實不然,遠端Service非但不好用,甚至可以稱得上是較為難用。一般情況下如果可以不使用遠端Service,就儘量不要使用它。

下面就來看一下它的弊端吧,首先將MyService的onCreate()方法中讓執行緒睡眠的程式碼去除掉,然後重新執行程式,並點選一下Bind Service按鈕,你會發現程式崩潰了!為什麼點選Start Service按鈕程式就不會崩潰,而點選Bind Service按鈕就會崩潰呢?這是由於在Bind Service按鈕的點選事件裡面我們會讓MainActivity和MyService建立關聯,但是目前MyService已經是一個遠端Service了,Activity和Service執行在兩個不同的程序當中,這時就不能再使用傳統的建立關聯的方式,程式也就崩潰了。

那麼如何才能讓Activity與一個遠端Service建立關聯呢?這就要使用AIDL來進行跨程序通訊了(IPC)。

AIDL(Android Interface Definition Language)是Android介面定義語言的意思,它可以用於讓某個Service與多個應用程式元件之間進行跨程序通訊,從而可以實現多個應用程式共享同一個Service的功能。

下面我們就來一步步地看一下AIDL的用法到底是怎樣的。首先需要新建一個AIDL檔案,在這個檔案中定義好Activity需要與Service進行通訊的方法。新建MyAIDLService.aidl檔案,程式碼如下所示:

  1. package com.example.servicetest;  
  2. interface MyAIDLService {  
  3.     int plus(int a, int b);  
  4.     String toUpperCase(String str);  
  5. }  
點選儲存之後,gen目錄下就會生成一個對應的Java檔案,如下圖所示:

                                             

然後修改MyService中的程式碼,在裡面實現我們剛剛定義好的MyAIDLService介面,如下所示:

  1. publicclass MyService extends Service {  
  2.     ......  
  3.     @Override
  4.     public IBinder onBind(Intent intent) {  
  5.         return mBinder;  
  6.     }  
  7.     MyAIDLService.Stub mBinder = new Stub() {  
  8.         @Override
  9.         public String toUpperCase(String str) throws RemoteException {  
  10.             if (str != null) {  
  11.                 return str.toUpperCase();  
  12.             }  
  13.             returnnull;  
  14.         }  
  15.         @Override
  16.         publicint plus(int a, int b) throws RemoteException {  
  17.             return a + b;  
  18.         }  
  19.     };  
  20. }  
這裡先是對MyAIDLService.Stub進行了實現,重寫裡了toUpperCase()和plus()這兩個方法。這兩個方法的作用分別是將一個字串全部轉換成大寫格式,以及將兩個傳入的整數進行相加。然後在onBind()方法中將MyAIDLService.Stub的實現返回。這裡為什麼可以這樣寫呢?因為Stub其實就是Binder的子類,所以在onBind()方法中可以直接返回Stub的實現。

接下來修改MainActivity中的程式碼,如下所示:

  1. publicclass MainActivity extends Activity implements OnClickListener {  
  2.     private Button startService;  
  3.     private Button stopService;  
  4.     private Button bindService;  
  5.     private Button unbindService;  
  6.     private MyAIDLService myAIDLService;  
  7.     private ServiceConnection connection = new ServiceConnection() {  
  8.         @Override
  9.         publicvoid onServiceDisconnected(ComponentName name) {  
  10.         }  
  11.         @Override
  12.         publicvoid onServiceConnected(ComponentName name, IBinder service) {  
  13.             myAIDLService = MyAIDLService.Stub.asInterface(service);  
  14.             try {  
  15.                 int result = myAIDLService.plus(35);  
  16.                 String upperStr = myAIDLService.toUpperCase("hello world");  
  17.                 Log.d("TAG""result is " + result);  
  18.                 Log.d("TAG""upperStr is " + upperStr);  
  19.             } catch (RemoteException e) {  
  20.                 e.printStackTrace();  
  21.             }  
  22.         }  
  23.     };  
  24.     ......  
  25. }  
我們只是修改了ServiceConnection中的程式碼。可以看到,這裡首先使用了MyAIDLService.Stub.asInterface()方法將傳入的IBinder物件傳換成了MyAIDLService物件,接下來就可以呼叫在MyAIDLService.aidl檔案中定義的所有介面了。這裡我們先是呼叫了plus()方法,並傳入了3和5作為引數,然後又呼叫了toUpperCase()方法,並傳入hello world字串作為引數,最後將呼叫方法的返回結果打印出來。

現在重新執行程式,並點選一下Bind Service按鈕,可以看到列印日誌如下所示:

                               

由此可見,我們確實已經成功實現跨程序通訊了,在一個程序中訪問到了另外一個程序中的方法。

不過你也可以看出,目前的跨程序通訊其實並沒有什麼實質上的作用,因為這只是在一個Activity裡呼叫了同一個應用程式的Service裡的方法。而跨程序通訊的真正意義是為了讓一個應用程式去訪問另一個應用程式中的Service,以實現共享Service的功能。那麼下面我們自然要學習一下,如何才能在其它的應用程式中呼叫到MyService裡的方法。

在上一篇文章中我們已經知道,如果想要讓Activity與Service之間建立關聯,需要呼叫bindService()方法,並將Intent作為引數傳遞進去,在Intent裡指定好要繫結的Service,示例程式碼如下:

  1. 相關推薦

    Android Service完全解析關於服務知道一切()

    並且 無法 數據類型 界面 其它 wid logcat listen 程序崩潰 文章轉載至:http://blog.csdn.net/guolin_blog/article/details/9797169 這是郭霖寫的.......就是寫 "第一行代碼"的那個厲害人物,大

    Android Service完全解析關於服務知道一切()(筆記)

    參考原文:Android Service完全解析,關於服務你所需知道的一切(上) Service的基本用法 然後新建一個MyService繼承自Service,並重寫父類的onCreate()、onStartCommand()和onDestroy()方法, 可以看到,在Sta

    Android Service完全解析關於服務知道一切()

    相信大多數朋友對Service這個名詞都不會陌生,沒錯,一個老練的Android程式設計師如果連Service都沒聽說過的話,那確實也太遜了。Service作為Android四大元件之一,在每一個應用程式中都扮演著非常重要的角色。它主要用於在後臺處理一些耗時的邏輯,或者去執行

    Android Service完全解析關於服務知道一切()

    在上一篇文章中,我們學習了Android Service相關的許多重要內容,包括Service的基本用法、Service和Activity進行通訊、Service的銷燬方式、Service與Thread的關係、以及如何建立前臺Service。以上所提到的這些知識點,基本上涵蓋了大部分日常開發工作當

    Android事件分發機制完全解析從原始碼的角度徹底理解()-郭霖

    記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。 還未閱讀過的朋友,請先參考 Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上) 。 那麼今天我們將繼

    Android事件分發機制完全解析從原始碼的角度徹底理解()

    記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。那麼今天我們將繼續上次未完成的話題,從原始碼的角度分析ViewGroup的事件分發。首先我們來探討一下,什麼是ViewGro

    Android事件分發機制完全解析從原始碼的角度徹底理解() (出自郭林老師)

    記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。 那麼今天我們將繼續上次未完成的話題,從原始碼的角度分析ViewGroup的事件分發。 首先我們來探討一下,什

    Android AsyncTask完全解析從原始碼的角度徹底理解

    我們都知道,Android UI是執行緒不安全的,如果想要在子執行緒裡進行UI操作,就需要藉助Android的非同步訊息處理機制。之前我也寫過了一篇文章從原始碼層面分析了Android的非同步訊息處理機制,感興趣的朋友可以參考 Android Handler、Message完

    Android Fragment完全解析關於碎片知道一切

    我們都知道,Android上的介面展示都是通過Activity實現的,Activity實在是太常用了,我相信大家都已經非常熟悉了,這裡就不再贅述。但是Activity也有它的侷限性,同樣的介面在手機上顯示可能很好看,在平板上就未必了,因為平板的螢幕非常大,手機的介面放在平板上

    Android Scroller完全解析關於Scroller知道一切

    轉載請註明出處:http://blog.csdn.net/guolin_blog/article/details/48719871 2016大家新年好!這是今年的第一篇文章,那麼應CSDN工作人員的建議,為了能給大家帶來更好的閱讀體驗,我也是將部落格換成了寬屏

    Android Context完全解析知道的Context的各種細節

    前幾篇文章,我也是費勁心思寫了一個ListView系列的三部曲,雖然在內容上可以說是絕對的精華,但是很多朋友都表示看不懂。好吧,這

    Android檢視繪製流程完全解析一步步深入瞭解View(二)

    在上一篇文章中,我帶著大家一起剖析了一下LayoutInflater的工作原理,可以算是對View進行深入瞭解的第一步吧。那麼本篇文章中,我們將繼續對View進行深入探究,看一看它的繪製流程到底是什麼樣的。如果你還沒有看過我的上一篇文章,可以先去閱讀 Android Layo

    Android事件分發機制完全解析從原始碼的角度徹底理解()-郭霖

    其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入了一

    Android ListView工作原理完全解析從原始碼的角度徹底理解

    在Android所有常用的原生控制元件當中,用法最複雜的應該就是ListView了,它專門用於處理那種內容元素很多,手機螢幕無法展示出所有內容的情況。ListView可以使用列表的形式來展示內容,超出螢幕部分的內容只需要通過手指滑動就可以移動到螢幕內了。另外ListView還

    Android事件分發機制完全解析從原始碼的角度徹底理解()

    其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入

    Android雙向滑動選單完全解析如何一分鐘實現雙向滑動特效

    記得在很早之前,我寫了一篇關於Android滑動選單的文章,其中有一個朋友在評論中留言,希望我可以幫他將這個滑動選單改成雙向滑動的方式。當時也沒想花太多時間,簡單修改了一下就發給了他,結果沒想到後來卻有一大批的朋友都來問我要這份雙向滑動選單的程式碼。由於這份程式碼寫得很不用心

    Android事件分發機制完全解析從原始碼的角度徹底理解() (出自郭霖老師)

    其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入了

    Android DiskLruCache完全解析硬碟快取的最佳方案

    概述記得在很早之前,我有寫過一篇文章Android高效載入大圖、多圖解決方案,有效避免程式OOM,這篇文章是翻譯自Android Doc的,其中防止多圖OOM的核心解決思路就是使用LruCache技術。但LruCache只是管理了記憶體中圖片的儲存與釋放,如果圖片從記憶體中被

    Android ActionBar完全解析使用官方推薦的最佳導航欄()

    本篇文章主要內容來自於Android Doc,我翻譯之後又做了些加工,英文好的朋友也可以直接去讀原文。限於篇幅的原因,在上篇文章中我們只學習了ActionBar基礎部分的知識,那麼本篇文章我們將接著上一章的內容繼續學習,探究一下ActionBar更加高階的知識。如果你還沒有看

    Millet穀倉揭祕知道的區塊鏈電商

      區塊鏈是一種按照時間順序將資料區塊以順序相連的方式組合成的一種鏈式資料結構, 並以密碼學方式保證的不可篡改和不可偽造的分散式賬本。   區塊鏈技術是利用塊鏈式資料結構來驗證與儲存資料、利用分散式節點共識演算法來生成和更新資料、利用密碼學的方式保證資料傳輸和訪問的安