前面几篇总结了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 statements
interface 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;
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind: " );
return mStub;
}
IMyAidlInterface.Stub mStub = new IMyAidlInterface.Stub() {
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public void setName(String name) throws RemoteException {
Log.e(TAG, "setName: 收到other说的name= "+name );
}
};
}
清单文件注册:
<service
android: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() {
@Override
public 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();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
Log.e(TAG, "onServiceDisconnected: " );
}
@Override
public void onBindingDied(ComponentName name) {
Log.e(TAG, "onBindingDied: " );
}
@Override
public 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"
才疏学浅,如有错误,欢迎指正,多谢。