Android Service完全解析,關於服務你所需知道的一切(下)-程式間通訊
轉載請註冊出處:http://blog.csdn.net/guolin_blog/article/details/9797169
在上一篇文章中,我們學習了Android Service相關的許多重要內容,包括Service的基本用法、Service和Activity進行通訊、Service的銷燬方式、Service與Thread的關係、以及如何建立前臺Service。以上所提到的這些知識點,基本上涵蓋了大部分日常開發工作當中可能使用到的Service技術。不過關於Service其實還有一個更加高階的使用技巧沒有介紹,即遠端Service的用法。使用遠端Service甚至可以實現Android跨程式通訊的功能,下面就讓我們具體地學習一下。
如果你還沒有看過前面一篇文章,建議先去閱讀一下 Android Service完全解析,關於服務你所需知道的一切(上) ,因為本篇文章中涉及到的程式碼是在上篇文章的基礎上進行修改的。
在上篇文章中我們知道了,Service其實是執行在主執行緒裡的,如果直接在Service中處理一些耗時的邏輯,就會導致程式ANR。
讓我們來做個實驗驗證一下吧,修改上一篇文章中建立的ServiceTest專案,在MyService的onCreate()方法中讓執行緒睡眠60秒,如下所示:
- public class MyService extends Service {
- ......
- @Override
- public void onCreate() {
- super.onCreate();
- Log.d(TAG, "onCreate() executed");
- try {
- Thread.sleep(60000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- ......
- }
重新執行後,點選一下Start Service按鈕或Bind Service按鈕,程式就會阻塞住並無法進行任何其它操作,過一段時間後就會彈出ANR的提示框,如下圖所示。
之前我們提到過,應該在Service中開啟執行緒去執行耗時任務,這樣就可以有效地避免ANR的出現。
那麼本篇文章的主題是介紹遠端Service的用法,如果將MyService轉換成一個遠端Service,還會不會有ANR的情況呢?讓我們來動手嘗試一下吧。
將一個普通的Service轉換成遠端Service其實非常簡單,只需要在註冊Service的時候將它的android:process屬性指定成:remote就可以了,程式碼如下所示:
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.servicetest"
- android:versionCode="1"
- android:versionName="1.0" >
- ......
- <service
- android:name="com.example.servicetest.MyService"
- android:process=":remote" >
- </service>
- </manifest>
為什麼將MyService轉換成遠端Service後就不會導致程式ANR了呢?這是由於,使用了遠端Service後,MyService已經在另外一個程式當中執行了,所以只會阻塞該程式中的主執行緒,並不會影響到當前的應用程式。
為了證實一下MyService現在確實已經執行在另外一個程式當中了,我們分別在MainActivity的onCreate()方法和MyService的onCreate()方法里加入一行日誌,列印出各自所在的程式id,如下所示:
- Log.d("TAG", "process id is " + Process.myPid());
可以看到,不僅僅是程式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檔案,程式碼如下所示:
- package com.example.servicetest;
- interface MyAIDLService {
- int plus(int a, int b);
- String toUpperCase(String str);
- }
然後修改MyService中的程式碼,在裡面實現我們剛剛定義好的MyAIDLService介面,如下所示:
- public class MyService extends Service {
- ......
- @Override
- public IBinder onBind(Intent intent) {
- return mBinder;
- }
- MyAIDLService.Stub mBinder = new Stub() {
- @Override
- public String toUpperCase(String str) throws RemoteException {
- if (str != null) {
- return str.toUpperCase();
- }
- return null;
- }
- @Override
- public int plus(int a, int b) throws RemoteException {
- return a + b;
- }
- };
- }
接下來修改MainActivity中的程式碼,如下所示:
- public class MainActivity extends Activity implements OnClickListener {
- private Button startService;
- private Button stopService;
- private Button bindService;
- private Button unbindService;
- private MyAIDLService myAIDLService;
- private ServiceConnection connection = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- myAIDLService = MyAIDLService.Stub.asInterface(service);
- try {
- int result = myAIDLService.plus(3, 5);
- String upperStr = myAIDLService.toUpperCase("hello world");
- Log.d("TAG", "result is " + result);
- Log.d("TAG", "upperStr is " + upperStr);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- };
- ......
- }
現在重新執行程式,並點選一下Bind Service按鈕,可以看到列印日誌如下所示:
由此可見,我們確實已經成功實現跨程式通訊了,在一個程式中訪問到了另外一個程式中的方法。
不過你也可以看出,目前的跨程式通訊其實並沒有什麼實質上的作用,因為這只是在一個Activity裡呼叫了同一個應用程式的Service裡的方法。而跨程式通訊的真正意義是為了讓一個應用程式去訪問另一個應用程式中的Service,以實現共享Service的功能。那麼下面我們自然要學習一下,如何才能在其它的應用程式中呼叫到MyService裡的方法。
在上一篇文章中我們已經知道,如果想要讓Activity與Service之間建立關聯,需要呼叫bindService()方法,並將Intent作為引數傳遞進去,在Intent裡指定好要繫結的Service,示例程式碼如下:
- Intent bindIntent = new Intent(this, MyService.class);
- bindService(bindIntent, connection, BIND_AUTO_CREATE);
- <?xml version="1.0" encoding="utf-8"?>
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"
- package="com.example.servicetest"
- android:versionCode="1"
- android:versionName="1.0" >
- ......
- <service
- android:name="com.example.servicetest.MyService"
- android:process=":remote" >
- <intent-filter>
- <action android:name="com.example.servicetest.MyAIDLService"/>
- </intent-filter>
- </service>
- </manifest>
這就說明,MyService可以響應帶有com.example.servicetest.MyAIDLService這個action的Intent。
現在重新執行一下程式,這樣就把遠端Service端的工作全部完成了。然後建立一個新的Android專案,起名為ClientTest,我們就嘗試在這個程式中遠端呼叫MyService中的方法。
ClientTest中的Activity如果想要和MyService建立關聯其實也不難,首先需要將MyAIDLService.aidl檔案從ServiceTest專案中拷貝過來,注意要將原有的包路徑一起拷貝過來,完成後專案的結構如下圖所示:
然後開啟或新建activity_main.xml,在佈局檔案中也加入一個Bind Service按鈕:
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- >
- <Button
- android:id="@+id/bind_service"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Bind Service"
- />
- </LinearLayout>
- public class MainActivity extends Activity {
- private MyAIDLService myAIDLService;
- private ServiceConnection connection = new ServiceConnection() {
- @Override
- public void onServiceDisconnected(ComponentName name) {
- }
- @Override
- public void onServiceConnected(ComponentName name, IBinder service) {
- myAIDLService = MyAIDLService.Stub.asInterface(service);
- try {
- int result = myAIDLService.plus(50, 50);
- String upperStr = myAIDLService.toUpperCase("comes from ClientTest");
- Log.d("TAG", "result is " + result);
- Log.d("TAG", "upperStr is " + upperStr);
- } catch (RemoteException e) {
- e.printStackTrace();
- }
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- Button bindService = (Button) findViewById(R.id.bind_service);
- bindService.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View v) {
- Intent intent = new Intent("com.example.servicetest.MyAIDLService");
- bindService(intent, connection, BIND_AUTO_CREATE);
- }
- });
- }
- }
在當前Activity和MyService建立關聯之後,我們仍然是呼叫了plus()和toUpperCase()這兩個方法,遠端的MyService會對傳入的引數進行處理並返回結果,然後將結果列印出來。
這樣的話,ClientTest中的程式碼也就全部完成了,現在執行一下這個專案,然後點選Bind Service按鈕,此時就會去和遠端的MyService建立關聯,觀察LogCat中的列印資訊如下所示:
不用我說,大家都已經看出,我們的跨程式通訊功能已經完美實現了。
不過還有一點需要說明的是,由於這是在不同的程式之間傳遞資料,Android對這類資料的格式支援是非常有限的,基本上只能傳遞Java的基本資料型別、字串、List或Map等。那麼如果我想傳遞一個自定義的類該怎麼辦呢?這就必須要讓這個類去實現Parcelable介面,並且要給這個類也定義一個同名的AIDL檔案。這部分內容並不複雜,而且和Service關係不大,所以就不再詳細進行講解了,感興趣的朋友可以自己去查閱一下相關的資料。
好了,結合上下兩篇,這就是關於Service你所需知道的一切。
相關文章
- Android Service完全解析,關於服務你所需知道的一切(上)Android
- Android Fragment完全解析,關於碎片你所需知道的一切AndroidFragment
- 關於 Android 程式保活,你所需要知道的一切Android
- 關於 Linux 程式你所需要知道的一切Linux
- [譯] 關於 Yarn 和 npm 你所需要知道的一切YarnNPM
- Android Messenger完全解析 實現程式間通訊AndroidMessenger
- Android 程式間通訊 Service、MessengerAndroidMessenger
- 關於Android的.so檔案你所需要知道的Android
- Android關於Path你所知道的和不知道的一切Android
- Android關於Canvas你所知道的和不知道的一切AndroidCanvas
- Android關於Paint你所知道的和不知道的一切AndroidAI
- Android關於Color你所知道的和不知道的一切Android
- Android Service完全解析Android
- android native service編寫及兩個服務程式通訊Android
- [譯] 關於 Flutter 頁面路由過渡動畫,你所需要知道的一切Flutter路由動畫
- 關於在Android中使用CMake你所需要了解的一切(一)Android
- 關於在Android中使用CMake你所需要了解的一切(二)Android
- 關於在Android中使用CMake你所需要了解的一切(三)Android
- 關於字元編碼,你所需要知道的字元
- [譯]關於 Parcel 你所需要知道的一切:快速的 Web 應用打包工具Web
- Flutter關於TextField你能知道的一切Flutter
- 微服務的服務間通訊與服務治理微服務
- 建立 Android Library 所需要知道的一切Android
- 你需要知道的關於 Go 包的一切Go
- [譯] 關於 `ExpressionChangedAfterItHasBeenCheckedError` 錯誤你所需要知道的事情ExpressError
- Android程式間的通訊Android
- Android Service 服務(一)—— ServiceAndroid
- [Android]你不知道的Android程式化(6)--程式通訊Andromeda框架Android框架
- Android 程式間通訊Android
- Android中通過Messenger與Service實現程式間雙向通訊AndroidMessenger
- 關於Vue v-model你需要知道的一切Vue
- 關於 v-model 你需要知道的這一切!
- Android IntentService完全解析 當Service遇到HandlerAndroidIntent
- [Android]你不知道的Android程式化(4)--程式通訊AIDL框架AndroidAI框架
- [Android]你不知道的Android程式化(5)--程式通訊Messenger框架AndroidMessenger框架
- Android 活動(activity)和服務(service)進行通訊Android
- 微服務的程式間通訊(IPC)微服務
- Android SERVICE後臺服務程式的守護Android