本文主要介绍通过跨进程通信(aidl)完成宿主与插件数据通信。
1.分别在宿主与插件中新增IService.aidl文件,文件名与包名要一致。IService.aidl中增加2个方法
interface IService {
void init(String str, int i);//宿主将数据传入插件中
String getString();//从插件中获取数据
}
2.接着在宿主中新建ProxyService,然后在onBind方法中实现方法转发
public IBinder onBind(Intent intent) {
fillService();
return pluginService.onBind(intent);
}
pluginService即为插件中的Service,pluginService是有fillService方法获取而来,具体实现实现如下
3.首先加载插件apk,构建出插件的DexClassLoader
String dexOutputPath = this.getDir("plugin", 0).getAbsolutePath();
String libOutputPath = this.getDir("plugin_lib", 0).getAbsolutePath();
String pluginApkPath = getDestPath();
File file=new File((pluginApkPath));
if(!file.exists()){
return;
}
//获取插件dexClassLoader,pluginApkPath为插件apk的目录
DexClassLoader pluginDexClassLoader = new DexClassLoader(pluginApkPath, dexOutputPath, libOutputPath, this.getClassLoader());
然后通过DexClassLoader新建插件中的Service
pluginService = (Service) pluginDexClassLoader.loadClass("com.afun.plugin.MyService").newInstance();
最后反射调用插件Service的attach方法,构建出插件Service的上下文与基础参数。
先看下attach方法需要的参数
public final void attach(
Context context,
ActivityThread thread, String className, IBinder token,
Application application, Object activityManager)
然后通过反射获取各个参数,执行插件Service的attach方法
Object[] objArr = new Object[6];
objArr[0] = this.getBaseContext();
Class service = this.getClass().getSuperclass();
Field field = service.getDeclaredField("mThread");
field.setAccessible(true);
objArr[1] = field.get(this);
objArr[2] = getClass().getName();
Field field1 = service.getDeclaredField("mToken");
field1.setAccessible(true);
objArr[3] = field1.get(this);
objArr[4] = getApplication();
Field field2 = service.getDeclaredField("mActivityManager");
field2.setAccessible(true);
objArr[5] = field2.get(this);
Method attach = pluginService.getClass().getMethod("attach", Context.class, (objArr[1]).getClass(), String.class, IBinder.class, Application.class, Object.class);
attach.setAccessible(true);
attach.invoke(pluginService, objArr);
4.接下来在MainActivity中完成bindService,关键代码如下
private void bindService() {
ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
}
public void onServiceConnected(ComponentName name, IBinder service) {
//iService为Binder的Proxy
iService = IService.Stub.asInterface(service);
try {
iService.init("test", 123);
Toast.makeText(MainActivity.this, iService.getString(), Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
};
Intent intent = new Intent();
intent.setPackage(this.getPackageName());
bindService(new Intent(this, ProxyService.class), serviceConnection, Context.BIND_AUTO_CREATE);
}
有了iService可以调用iService.getString()方法从插件中获取数据。
至此,宿主apk调用IService中的init方法将数据传递到了插件中,也调用了getString方法从插件中获取数据。
5.下面看下插件中的com.afun.plugin.MyService的关键代码
class IServiceImpl extends com.sdk.service.IService.Stub {
@Override
public void init(String str, int i) throws RemoteException {
Log.e("in plugin", "init success");
Log.e("in plugin", "str:" + str + "," + "i:" + i);
Toast.makeText(getBaseContext(), "str:" + str + "," + "i:" + i, Toast.LENGTH_SHORT).show();
}
@Override
public String getString() throws RemoteException {
return "you get data from plugin service";
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return new IServiceImpl();
}
最后将插件plugin.apk push到手机中运行宿主apk查看结果
01-24 20:16:59.534 12977-12977/? E/in plugin: init success
01-24 20:16:59.535 12977-12977/? E/in plugin: str:test,i:123
01-24 20:16:59.542 12977-12977/? E/in app: str:you get data from plugin service
可以看出,在宿主获取到了插件中的数据,在插件中获取到了宿主传入的数据。
感兴趣的可以看下源码
本文Demo