前面幾篇總結了Service的使用和源碼執行流程,這里再簡單分析一下如果需要Service跨進程通信該怎樣做。AIDL(Android Interface Definition Language)Android接口定義語言,用于實現?Android 兩個進程之間進行進程間通信(IPC)。
AIDL技術跨進程通信可以理解為是服務端和客戶端之間的通信(IPC),定義Service的進程稱為服務端,調用服務的進程就是客戶端。
分析一下服務端生成aidl、定義Service已經再客戶端調用服務。本文使用的兩個APP實現,服務端是app,客戶端是otherapp。
1、首先準備兩個Android工程
我這里就是建一個project然后建兩個module,您也可以建兩個project反正最后都是安裝到同一個手機的兩個APP。
2、服務端工程新建aidl文件
建議直接通過鼠標右鍵-> New -> AIDL -> AIDL File新建一個?adil 文件,build 后生成對應的 java 類。
AIDL文件,setName是我定義的方法:
// IMyAidlInterface.aidl
package com.example.testdemo;// Declare any non-default types here with import statementsinterface IMyAidlInterface {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,double aDouble, String aString);/*** 自定義方法*/void setName(String name);
}
自動生成的java文件:
這里內容不少,里面有個內部類名字叫Stub ,
public static abstract class Stub extends android.os.Binder implements com.example.testdemo.IMyAidlInterface{
}
Stub 類可以看到我們定義的setName方法。
注意不管你aidl文件名字叫什么編譯后的java文件都是在Stub 類定義你的方法。
3、服務端定義Service并創建對應的 Stub 對象;
/*** AIDL的服務端*/
public class MyService extends Service {public static final String TAG = "MyService";private boolean setServiceRunning = true;@Overridepublic void onCreate() {super.onCreate();}@Overridepublic IBinder onBind(Intent intent) {Log.e(TAG, "onBind: " );return mStub;}IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {@Overridepublic void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {}@Overridepublic void setName(String name) throws RemoteException {Log.e(TAG, "setName: 收到other說的name= "+name );}};
}
清單文件注冊:
<serviceandroid:name="com.example.testdemo.service.MyService"android:enabled="true"android:exported="true"><intent-filter><category android:name="android.intent.category.DEFAULT"></category></intent-filter>
</service>
4、客戶端要將服務端這個aidl文件拷貝過來,準備和服務端一模一樣的生活環境,?兩端的aidl 文件和所在包名都必須一致。
Intent intentService = new Intent();
intentService.setComponent(new ComponentName("com.example.testdemo","com.example.testdemo.service.MyService"));
boolean b = bindAidl(intentService);
private boolean bindAidl(Intent intent) {ServiceConnection serviceConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName componentName, IBinder iBinder) {Log.e(TAG, "onServiceConnected: " );IMyAidlInterface iMyAidlInterface = IMyAidlInterface.Stub.asInterface(iBinder);//連接成功,調用綁定的service中的方法try {iMyAidlInterface.setName("Hello 服務端,我是Server端");} catch (RemoteException e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName componentName) {Log.e(TAG, "onServiceDisconnected: " );}@Overridepublic void onBindingDied(ComponentName name) {Log.e(TAG, "onBindingDied: " );}@Overridepublic void onNullBinding(ComponentName name) {Log.e(TAG, "onNullBinding: " );}};boolean b = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);Log.e(TAG, "onClick: start-bind 結果="+b);return b;
}
注意:
第一:上面代碼intentService.setComponent(new ComponentName("com.example.testdemo","com.example.testdemo.service.MyService"));中創建ComponentName實例的第一個參數是應用的包名,不攜帶類的上層路徑;第二個參數是你定義的服務這個Java文件的全類名
第二:bindService方法如果返回報錯,需要在androidManifest,xml中加入
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
????tools:ignore="QueryAllPackagesPermission" />
?<queries>
?????<package android:name="com.example.testdemo"/>
?</queries>
第三:如果加了上面第二點這些導致項目編譯失敗,報錯"manifest merger failed xxxx " ,需要把根工程里的build.gradle中的classPath升級到3.5.4或以上,比如classpath "com.android.tools.build:gradle:3.5.4"
才疏學淺,如有錯誤,歡迎指正,多謝。