多module版,pojo类型数据的双向传递 [service client]
android中如何进行跨进程通信的?
aidl是什么?
android中如何通过aidl实现跨进程通信?
最简单的aidl如何实现?
多module之间如何通过aidl进行通信?
单module实现aidl和多module实现aidl有何区别?
多App之间如何通过aidl进行通信?
以上这些问题,我不打算全部回答。因为我也不知道怎么回答。
关于第一个问题,和第二个问题,我相信官方文档以及一些大神的博客已经有了很详尽的介绍,这里我就赘述了。
第三个问题 android中如何通过aidl实现跨进程通信? 这个问题,还是太宽泛了,我也不回答了。
关于 如何实现最简单的aidl,请看这篇
好了,现在来本篇的重点 多module之间如何通过aidl进行通信?
那我们就一步步开始吧。
后面的问题呢?别急啊...慢慢来。
关于“多module之间如何通过aidl进行通信”我准备分3个小点来说明:
- 如何实现多`module`之间,基本类型的数据的单向传递 [service --> client]
- 如何实现多`module`之间,自定义类型的`pojo`对象的单向传递 [service --> client]
- 如何实现多`module`之间,自定义类型的`pojo`对象的双向传递 [service client]
从代码开始:
首先,我新建一个android project,由于我使用的是android studio,所以默认新建的项目就是一个安卓项目。
新建完成项目之后,默认会生成一个module名为app。
然后,我会再建两个module,名为service和aidl。
关于如何在android studio中新建一个module,这个大家都知道的吧。其中一种方法是:[菜单栏 --> File --> New --> New Module... --> Android Liarbry ]
好了,现在准备工作完成了。多module已经建立起来了。
解释一下service是用来作为服务的,让app去调用的。aidl是专门用来存放.aidl文件以及对应的pojo类的。
接下来,我们去实现一下服务吧,不然跨进程通信是没法建立起来的。
1.在service所在的module中,我新建一个类继承android.app.Service,比如RemoteService。看起来差不多这个样子:
public class RemoteService extends Service {
public RemoteService() {
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
}
2.现在我们去修改一下清单文件。让RemoteService处在一个单独的进程。为什么要这么干?因为如果不修改,那么无论是在哪个module实现的Service都是在主进程的。 好了,现在我们看一下修改之后的当前服务所在的节点的样子:
android:name=".RemoteService"
android:enabled="true"
android:exported="true"
android:process=":remote" />
好了,我们为实现跨进程通信又跨出了一步。因为现在的确有多个进程了。而且有多个module了。
3.现在呢,我们就得去定义.aidl文件了。
为什么现在就要定义.aidl文件呢? 因为我们知道,跨进程通信本质上是通过bindService的方法绑定一个远程服务,以便实现数据的交互。而跨进程的,内存又无法共享,可以通过已知的共享内存IBinder去实现跨进程的数据交互。ps:这个解说可能存在错误,请大家不要深信。
4.那么,我们准备通过跨进程传递什么呢?假设,我现在想通过aidl传递一个500以内的随机数给调用者。那么,.aidl差不多长这样的:
// RemoteBinder.aidl
package com.pythoncat.aidl;
// Declare any non-default types here with import statements
interface RemoteBinder {
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int getResult();
}
**注意:这个.aidl文件是在aidl所在module中定义的。**至于能不能直接在sevcie所在module中定义,我猜测也是可以的,但是没有去验证。放在一个单独的module中的好处就是,以后查阅起来方便。
5.现在,我们定义好了RemoteBinder.aidl,然后rebuild一下工程。之后,会自动生成一些类,这些我就不去深究了,因为和主题无关。
6.rebuild完成之后,我们去完善刚才定义的RemoteService。重写onBind()方法。完善之后的RemoteService差不多长这样的:
public class RemoteService extends Service {
public RemoteService() {
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
private class MyBinder extends RemoteBinder.Stub{
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int getResult() throws RemoteException {
return new Random().nextInt(500);
}
}
}
现在差不多大功告成了,就等着调用者来调用了。
7.现在我们去app所在module。这里面默认给我们提供了一个MainActivity。但是我不打算对它做什么。我给它改名为LauncherActivity。并且在里面添加两个按钮,准备让他们分别实现界面的跳转。第二个按钮点击之后就跳转到AidlActivity。当然这个AidlActivity也是在app所在module的,而且是刚刚新建的。
8.在AidlActivity里面呢,我准备弄一个textview,用于显示,将来通过aidl获取的500以内的随机数。,和一个按钮,按钮的点击就是触发获取的条件。
9.好了,UI部分算是完成了。现在呢,我们先让我们的AidlActivity去绑定服务。怎么去绑定一个服务,相信大家都会的。现在呢,我们就来看一下,绑定服务之后,AidiActivity长什么样子的:
public class AidlActivity extends AppCompatActivity {
private ServiceConnection mConn;
private RemoteBinder mRemoteBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
TextView tvShowResult = (TextView) findViewById(R.id.aidl_show_tv);
TextView tvParcelable = (TextView) findViewById(R.id.aidl_show_parcelable_tv);
tvParcelable.setText(getString(R.string.show_about_parcelable_service, ""));
tvShowResult.setText(getString(R.string.show_about_normal_service, ""));
Button btnExecute = (Button) findViewById(R.id.aidl_ui_btn);
btnExecute.setOnClickListener(v -> {
if (mRemoteBinder != null) {
try {
mRemoteBinder.setResult(1024);
int result = mRemoteBinder.getResult();
tvShowResult.setText(getString(R.string.show_about_normal_service, result));
LogUtils.e("aidl result===" + result);
} catch (RemoteException e) {
LogUtils.e("bind remote service fail...");
LogUtils.e(e);
}
}
});
mConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteBinder = RemoteBinder.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mRemoteBinder = null;
}
};
bindService(new Intent(get(), RemoteService.class), mConn, Context.BIND_AUTO_CREATE);
}
public AppCompatActivity get() {
return this;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mConn != null) {
unbindService(mConn);
mConn = null;
}
}
}
需要注意的是IBinder对象的获取方式:mRemoteBinder = RemoteBinder.Stub.asInterface(service);
ok,似乎一切已经完成了。那么我们就跑起来吧。... 果不其然,我们从LaunchrActivity界面进到AidlActivity先,然后,我们每点一次按钮,textview上就出现一个500以内的随机数,几乎每次都不相同。
这么看来,我们已经实现了多module之间的aidl通信。
-的确,刚才已经实现了。但是刚才的实现仅仅是数据的单向传递[service-->client],而且是基本类型的数据的单向传递。
-现在呢,我们开始第二小节的说明,也就是如何实现多module之间的pojo类型的数据的单向传递 [service --> client]
还是从代码开始:
首先在aidl所在module中定义一个pojo。比如这个样子:
public class Duck {
public String name;
public long id;
}
为什么是在aidl所在module中定义pojo,而不是service所在module? 因为,在这里定义是为了让.aidl能够找到对应的pojo类。
如果在其他module中定义,就要让其他module成为当前moudle的依赖。ps:关于此段的说明,我不能保证正确性,需要通过代码验证。
现在呢,就是要让Duck实现Parcelable接口。至于怎么实现,我只说一句:android studio有相关的插件,自动生成Parcelable接口的实现。
然后,定义该pojo对应的.aidl文件。差不多长这样子的:
// Duck.aidl
package com.pythoncat.aidl.bean;
// Declare any non-default types here with import statements
parcelable Duck;
注意包的对应
接下来,就是定义传输pojo的接口方法了。比如:Duck getDuck();。那么,我们去新建一个.aidl文件,映射一个Ibinder。里面有传输pojo的方法。ok,定义之后的新的.aidl差不多长这样的:
// RemoteBinder.aidl
package com.pythoncat.aidl;
// Declare any non-default types here with import statements
import com.pythoncat.aidl.bean.Duck;
interface RemoteBinder {
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int getResult();
Duck getDuck();
}
再次rebuild项目。
之后,会提示你报错了。因为你的RemoteService$MyBinder不是抽象类,并且有未实现的方法。那么我们就去实现该抽象方法。实现之后,Mybinder差不多长这样子:
public class MyBinder extends RemoteBinder.Stub {
private Duck duck;
private int result;
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int getResult() throws RemoteException {
if (result == 0) {
return new Random().nextInt(500);
} else {
return this.result;
}
}
@Override
public Duck getDuck() throws RemoteException {
if (this.duck == null) {
Duck d = new Duck();
d.id = 10086;
d.name = "DuckTang";
return d;
} else {
this.duck.name = this.duck.name + "&Jerry";
return this.duck;
}
}
}
ok,我们的服务算是搞定了,接下来就是看调用者AidlActivity了。那么,我们就去调用者中添加获取pojo对象的方法,并显示在textview中。差不多就是这样子的:
Duck d = mRemoteBinder.getDuck();
tvParcelable.setText(getString(R.string.show_about_parcelable_service, d.toString()));
当然我又新加了一个textview,这个也要告诉你吗?
现在呢,完整的AidlActivity是这样的:
package com.pythoncat.ipcorservice2.activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.widget.Button;
import android.widget.TextView;
import com.apkfuns.logutils.LogUtils;
import com.pythoncat.aidl.RemoteBinder;
import com.pythoncat.aidl.bean.Duck;
import com.pythoncat.ipcorservice2.R;
import com.pythoncat.service.RemoteService;
public class AidlActivity extends AppCompatActivity {
private ServiceConnection mConn;
private RemoteBinder mRemoteBinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_aidl);
TextView tvShowResult = (TextView) findViewById(R.id.aidl_show_tv);
TextView tvParcelable = (TextView) findViewById(R.id.aidl_show_parcelable_tv);
tvParcelable.setText(getString(R.string.show_about_parcelable_service, ""));
tvShowResult.setText(getString(R.string.show_about_normal_service, ""));
Button btnExecute = (Button) findViewById(R.id.aidl_ui_btn);
btnExecute.setOnClickListener(v -> {
if (mRemoteBinder != null) {
try {
int result = mRemoteBinder.getResult();
tvShowResult.setText(getString(R.string.show_about_normal_service, result));
LogUtils.e("aidl result===" + result);
Duck d = mRemoteBinder.getDuck();
tvParcelable.setText(getString(R.string.show_about_parcelable_service, d.toString()));
LogUtils.e("aidl parcelable===" + d.toString());
} catch (RemoteException e) {
LogUtils.e("bind remote service fail...");
LogUtils.e(e);
}
}
});
mConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemoteBinder = RemoteBinder.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mRemoteBinder = null;
}
};
bindService(new Intent(get(), RemoteService.class), mConn, Context.BIND_AUTO_CREATE);
}
public AppCompatActivity get() {
return this;
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mConn != null) {
unbindService(mConn);
mConn = null;
}
}
}
似乎已经结束了,那就跑起来吧。run app...。果不其然,我们的界面上看到了在Mybinder中定义的new Duck()对象。
现在,我们就完成第二小节的说明。完成了多module之间通过aidl进行pojo类型的数据的单向传递 [service --> client]
但是,最后一个问题还没有解决,就是双向的传递 [service client]
好了,那我们就去实现一下吧。
首先,在RemoteBinder中新添加一个方法void setDuck(Duck d);。添加之后,差不多长这样的:
// RemoteBinder.aidl
package com.pythoncat.aidl;
// Declare any non-default types here with import statements
import com.pythoncat.aidl.bean.Duck;
interface RemoteBinder {
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int getResult();
void setResult(in int result);
Duck getDuck();
void setDuck(in Duck duck); // in is ok
}
至于上面为什么要加上in,这个涉及到aidl中定向tag相关的知识点,这里我不多介绍了。
再次rebuild项目。结束之后,发现又报错了,说你的MyBinder不是抽象类,而且有没有实现的抽象方法void setDuck(Duck d)。于是,就实现一下。那么,现在。MyBinder长这样子:
public class MyBinder extends RemoteBinder.Stub {
private Duck duck;
private int result;
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int getResult() throws RemoteException {
if (result == 0) {
return new Random().nextInt(500);
} else {
return this.result;
}
}
@Override
public void setResult(int result) throws RemoteException {
this.result = result;
}
@Override
public Duck getDuck() throws RemoteException {
if (this.duck == null) {
Duck d = new Duck();
d.id = 10086;
d.name = "DuckTang";
return d;
} else {
this.duck.name = this.duck.name + "&Jerry";
return this.duck;
}
}
@Override
public void setDuck(Duck duck) throws RemoteException {
this.duck = duck;
}
}
ok,服务这边算是结束了。接下来,去看调用者AidlActivity是如何去传递一个pojo对象给服务。看起来应该是这样子的:
if (mRemoteBinder != null) {
try {
mRemoteBinder.setResult(1024);
int result = mRemoteBinder.getResult();
tvShowResult.setText(getString(R.string.show_about_normal_service, result));
LogUtils.e("aidl result===" + result);
Duck duck = new Duck();
duck.name = "pythonCat";
duck.id = 12306;
mRemoteBinder.setDuck(duck);
Duck d = mRemoteBinder.getDuck();
tvParcelable.setText(getString(R.string.show_about_parcelable_service, d.toString()));
LogUtils.e("aidl parcelable===" + d.toString());
} catch (RemoteException e) {
LogUtils.e("bind remote service fail...");
LogUtils.e(e);
}
}
好了,现在一切结束了。跑起来吧。... 果然,和我们预想的一样,现在界面显示的我们传递过去的Duck对象。而不是,先前在服务中定义的Duck对象了。
那么,毫无疑问,我们完成了之前的问题。多module之间如何通过aidl实现数据的双向传递。
哈哈哈哈哈,我们完成了。
然后呢?
多App之间如何进行aidl?未完待续吧,或者没有下文了。