前面几篇总结了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"
才疏学浅,如有错误,欢迎指正,多谢。