天天看点

Android绑定多个aidl,android aidl 多`module`版的实现

多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?未完待续吧,或者没有下文了。