借助AIDL实现IPC通信
一、代码实操—与远端进程的Service绑定
上面的代码都是在当前进程内跟Service通信,现在我们来实现一下,不同进程内Service如何绑定。
AIDL:Android Interface Definition Language,即Android接口定义语言。
Service跨进程传递数据需要借助aidl,主要步骤是这样的:
- 编写aidl文件,AS自动生成的java类实现IPC通信的代理
- 继承自己的aidl类,实现里面的方法
- 在onBind()中返回我们的实现类,暴露给外界
- 需要跟Service通信的对象通过bindService与Service绑定,并在ServiceConnection接收数据。
我们通过代码来实现一下:
1、首先我们需要新建一个Service
代码语言:javascript复制public class MyRemoteService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("MyRemoteService", "MyRemoteService thread id = " Thread.currentThread().getId());
return null;
}
}
2、在manifest文件中声明我们的Service同时指定运行的进程名,这里并是不只能写remote进程名,你想要进程名都可以
代码语言:javascript复制<service
android:name=".service.MyRemoteService"
android:process=":remote" /
3、新建一个aidl文件用户进程间传递数据。
AIDL支持的类型:八大基本数据类型、String类型、CharSequence、List、Map、自定义类型。List、Map、自定义类型放到下文讲解。
里面会有一个默认的实现方法,删除即可,这里我们新建的文件如下:
代码语言:javascript复制package xxxx;//aidl所在的包名
//interface之前不能有修饰符
interface IProcessInfo {
//你想要的通信用的方法都可以在这里添加
int getProcessId();
}
4、实现我们的aidl类
代码语言:javascript复制public class IProcessInfoImpl extends IProcessInfo.Stub {
@Override
public int getProcessId() throws RemoteException {
return android.os.Process.myPid();
}
}
5、在Service的onBind()中返回
代码语言:javascript复制public class MyRemoteService extends Service {
IProcessInfoImpl mProcessInfo = new IProcessInfoImpl();
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.e("MyRemoteService", "MyRemoteService thread id = " Thread.currentThread().getId());
return mProcessInfo;
}
}
6、绑定Service
代码语言:javascript复制 mTvRemoteBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(MainActivity.this, MyRemoteService.class);
bindService(intent, mRemoteServiceConnection, BIND_AUTO_CREATE);
}
});
mRemoteServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("MainActivity", "MyRemoteService onServiceConnected");
// 通过aidl取出数据
IProcessInfo processInfo = IProcessInfo.Stub.asInterface(service);
try {
Log.e("MainActivity", "MyRemoteService process id = " processInfo.getProcessId());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("MainActivity", "MyRemoteService onServiceDisconnected");
}
};
只要绑定成功就能在有log打印成MyRemoteService所在进程的进程id。这样我们就完成了跟不同进程的Service通信的过程。
二、代码实操—调用其他app的Service
跟调同app下不同进程下的Service相比,调用其他的app定义的Service有一些细微的差别
1、由于需要其他app访问,所以之前的bindService()使用的隐式调用不在合适,需要在Service定义时定义action 我们在定义的线程的App A 中定义如下Service:
代码语言:javascript复制<service android:name=".service.ServerService"
<intent-filter
//这里的action自定义
<action android:name="com.jxx.server.service.bind" /
<category android:name="android.intent.category.DEFAULT" /
</intent-filter
</service
2、我们在需要bindService的App B 中需要做这些处理
- 首先要将A中定义的aidl文件复制到B中,比如我们在上面定义的IProcessInfo.aidl这个文件,包括路径在内需要原封不动的复制过来。
- 在B中调用Service通过显式调用
mTvServerBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("com.jxx.server.service.bind");//Service的action
intent.setPackage("com.jxx.server");//App A的包名
bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE);
}
});
aidl中自定义对象的传递
主要步骤如下:
- 定义自定对象,需要实现Parcelable接口
- 新建自定义对象的aidl文件
- 在传递数据的aidl文件中引用自定义对象
- 将自定义对象以及aidl文件拷贝到需要bindService的app中,主要路径也要原封不动
我们来看一下具体的代码:
1、定义自定义对象,并实现Parcelable接口
代码语言:javascript复制public class ServerInfo implements Parcelable {
public ServerInfo() {
}
String mPackageName;
public String getPackageName() {
return mPackageName;
}
public void setPackageName(String packageName) {
mPackageName = packageName;
}
protected ServerInfo(Parcel in) {
mPackageName = in.readString();
}
public static final Creator<ServerInfo CREATOR = new Creator<ServerInfo () {
@Override
public ServerInfo createFromParcel(Parcel in) {
return new ServerInfo(in);
}
@Override
public ServerInfo[] newArray(int size) {
return new ServerInfo[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(mPackageName);
}
//使用out或者inout修饰时需要自己添加这个方法
public void readFromParcel(Parcel dest) {
mPackageName = dest.readString();
}
}
2、新建自定义对象的aidl文件
代码语言:javascript复制package com.jxx.server.aidl;
//注意parcelable 是小写的
parcelable ServerInfo;
3、引用自定义对象
代码语言:javascript复制package com.jxx.server.aidl;
//就算在同一包下,这里也要导包
import com.jxx.server.aidl.ServerInfo;
interface IServerServiceInfo {
ServerInfo getServerInfo();
void setServerInfo(inout ServerInfo serverinfo);
}
注意这里的set方法,这里用了inout,一共有3种修饰符 – in:客户端写入,服务端的修改不会通知到客户端 – out:服务端修改同步到客户端,但是服务端获取到的对象可能为空 – inout:修改都收同步的
当使用out和inout时,除了要实现Parcelable外还要手动添加readFromParcel(Parcel dest)
4、拷贝自定义对象以及aidl文件到在要引用的App中即可。
5、引用
代码语言:javascript复制mServerServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IServerServiceInfo serverServiceInfo = IServerServiceInfo.Stub.asInterface(service);
try {
ServerInfo serviceInfo = serverServiceInfo.getServerInfo();
Log.e("MainActivity", "ServerService packageName = " serviceInfo.getPackageName());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e("MainActivity", "ServerService onServiceDisconnected");
}
};
List、Map中引用的对象也应该是符合上面要求的自定义对象,或者其他的几种数据类型。
使用Messenger实现IPC通信
步骤是这样的:
- 在Server端新建一个Messenger对象,用于响应Client端的注册操作,并在onBind()中传递出去
- 在Client端的ServiceConnection中,将Server端传递过来的Messenger对象进行保存
- 同时Client端也新建一个Messenger对象,通过Server传递过来的Messenger注册到Server端,保持通信用。
- 不管是否进行unbindService()操作,只要Client保有Server端的Messenger对象,仍然能和Server端进行通信。
一、Server端代码
代码语言:javascript复制public class MessengerService extends Service {
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_SET_VALUE = 3;
//这个是给client端接收参数用的
static final int MSG_CLIENT_SET_VALUE = 4;
static class ServiceHandler extends Handler {
private final List<Messenger mMessengerList = new ArrayList< ();
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_REGISTER_CLIENT:
mMessengerList.add(msg.replyTo);
break;
case MSG_UNREGISTER_CLIENT:
mMessengerList.remove(msg.replyTo);
break;
case MSG_SET_VALUE:
int value = msg.arg1;
for (Messenger messenger : mMessengerList) {
try {
messenger.send(Message.obtain(null, MSG_CLIENT_SET_VALUE, value, 0));
} catch (RemoteException e) {
e.printStackTrace();
}
}
break;
default:
super.handleMessage(msg);
}
}
}
private Messenger mMessenger = new Messenger(new ServiceHandler());
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mMessenger.getBinder();
}
}
二、Client端代码
代码语言:javascript复制public class MessengerClientActivity extends AppCompatActivity {
//这些类型要和Server端想对应
static final int MSG_REGISTER_CLIENT = 1;
static final int MSG_UNREGISTER_CLIENT = 2;
static final int MSG_SET_VALUE = 3;
static final int MSG_CLIENT_SET_VALUE = 4;
class ClientHandler extends Handler {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_CLIENT_SET_VALUE) {
mTvValue.setText(msg.arg1 "");
} else {
super.handleMessage(msg);
}
}
}
TextView mTvServerBind;
TextView mTvServerUnbind;
TextView mTvValue;
TextView mTvSend;
ServiceConnection mServerServiceConnection;
Messenger mServerMessenger;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_messenger);
mTvServerBind = findViewById(R.id.tv_server_bind);
mTvServerUnbind = findViewById(R.id.tv_server_unbind);
mTvValue = findViewById(R.id.tv_value);
mTvSend = findViewById(R.id.tv_send);
mTvServerBind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
intent.setAction("jxx.com.server.service.messenger");
intent.setPackage("jxx.com.server");
bindService(intent, mServerServiceConnection, BIND_AUTO_CREATE);
}
});
mTvServerUnbind.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//就算这里我们unbindService,只要我们还保留有mServerMessenger对象,
//我们就能继续与Server通信
unbindService(mServerServiceConnection);
}
});
mTvSend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mServerMessenger != null) {
try {
//测试一下能否设置数据
Message test = Message.obtain(null, MSG_SET_VALUE, new Random().nextInt(100), 0);
mServerMessenger.send(test);
} catch (RemoteException e) {
e.printStackTrace();
}
}
}
});
mServerServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//服务端的messenger
mServerMessenger = new Messenger(service);
//现在开始构client用来传递和接收消息的messenger
Messenger clientMessenger = new Messenger(new ClientHandler());
try {
//将client注册到server端
Message register = Message.obtain(null, MSG_REGISTER_CLIENT);
register.replyTo = clientMessenger;//这是注册的操作,我们可以在上面的Server代码看到这个对象被取出
mServerMessenger.send(register);
Toast.makeText(MessengerClientActivity.this, "绑定成功", Toast.LENGTH_SHORT).show();
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
}
以上就是本文的全部内容,希望对大家的学习有所帮助。