Android中的IPC进程通信方式第二篇
Posted 小二玩编程
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中的IPC进程通信方式第二篇相关的知识,希望对你有一定的参考价值。
本文系转载文章,阅读原文可获取源码,文章末尾有原文链接
ps:本文讲的是 使用 Messenger 进行进程间通信,demo 是用 kotlin 语言写的
1、使用 Messenger
Messenger 可以理解为信使,它可以在不同进程中传递 Message 对象,一般它是在客户端进程和服务器端进程通信,当我们在客户端发送一个 Message 给服务端时,在服务端的 Handler 中会接收到客户端的消息,然后进行对应的处理,处理完成后,再将结果等数据封装成 Message,发送给客户端,客户端的 Handler 中会接收到处理的结果。
我们在写 Messenger 进程间通信的时候不需要写 AIDL 文件,因为 Messenger 封装了 AIDL,我们这边先不分析源码,先写 demo 演示,然后再分析源码。
下面我们举个例子:
(1)新建一个 kotlin 类 MyService 并继承于 Service,它的包名是 com.xe.demo.ipcdemo2.ipcserver:
class MyService: Service() {
var mHandler: MessengerServiceHandler? = null;
var TAG: String = "MyService";
var num: Int = 0
var mServiceMesstenger: Messenger? = null;
override fun onBind(intent: Intent?): IBinder {
return mServiceMesstenger!!.binder
}
override fun onCreate() {
super.onCreate()
mHandler = MessengerServiceHandler()
mServiceMesstenger = Messenger(mHandler)
}
inner class MessengerServiceHandler: Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
if (msg!!.what == 1) {
num++
var content: String = msg!!.data.getString("msg")
Log.d(TAG,content)
var clientMessenger: Messenger = msg.replyTo
var message: Message = Message.obtain(null,2)
var replyContent: String = "发送给客户端" + num + "条消息"
var bundle: Bundle = Bundle()
bundle.putString("replyContent",replyContent)
message.setData(bundle);
try {
clientMessenger.send(message);
} catch (e: RemoteException) {
e.printStackTrace()
}
}
}
}
override fun onDestroy() {
super.onDestroy()
if (mHandler != null) {
mHandler!!.removeCallbacksAndMessages(null)
}
}
}
(2)在 androidManifest.xml 文件配置一下,开一下多进程:
<service android:name="com.xe.demo.ipcdemo2.ipcserver.MyService"
android:process=":remote">
</service>
(3)新建一个 kotlin 类 MainActivity 并继承于 AppCompatActivity,它包名为:com.xe.demo.ipcdemo2.ipcdemo2
class MainActivity: AppCompatActivity() {
var mTv: TextView? = null
var mMyServiceConnection: MyServiceConnection? = null
var mMessengerClientHandler: MessengerClientHandler? = null
var mClientMesstenger: Messenger? = null
var mMessenger: Messenger? = null
var num: Int = 0
inner class MyServiceConnection: ServiceConnection {
override fun onServiceDisconnected(name: ComponentName?) {
}
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mMessenger = Messenger(service)
}
}
inner class MessengerClientHandler: Handler() {
override fun handleMessage(msg: Message?) {
super.handleMessage(msg)
if (msg!!.what == 2) {
var fromServiceContent: String = msg.data.getString("replyContent");
mTv!!.setText(fromServiceContent)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mTv = findViewById(R.id.tv);
mMyServiceConnection = MyServiceConnection()
mMessengerClientHandler = MessengerClientHandler()
mClientMesstenger = Messenger(mMessengerClientHandler)
bindService()
}
fun bindService() {
var intent: Intent = Intent(this, MyService::class.java)
bindService(intent,mMyServiceConnection, Context.BIND_AUTO_CREATE);
}
fun onClick(v: View) {
sendMessage()
}
fun sendMessage() {
num++
var message: Message = Message.obtain(null,1)
var bundle: Bundle = Bundle()
bundle.putCharSequence("msg","这是客户端发送给服务器端的第" + num + "条消息")
message.data = bundle
message.replyTo = mClientMesstenger;
try {
mMessenger!!.send(message);
} catch (e: RemoteException) {
e.printStackTrace();
}
}
}
(4)新建一个 xml 文件 activity_main:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_
android:layout_
android:orientation="vertical"
tools:context="com.xe.demo.ipcdemo2.ipcdemo2.MainActivity">
<Button
android:layout_
android:text="发送消息给客户端"
android:onClick="onClick"
android:layout_ />
<TextView
android:id="@+id/tv"
android:layout_
android:gravity="center"
android:layout_ />
</LinearLayout>
程序一开始运行时,显示如下界面:
图片
点击“发送消息给客户端”按钮后,界面变化如下所示:
图片
随后日志打印如下所示:
图片
到这里,我们总结一下,这里开启多进程是成功了的,至于如何查看多进程的进程名,可以看Android中的IPC进程通信方式第一篇这篇文章;首先客户端这边的 MainActivity 类调用 bindService(intent,mMyServiceConnection, Context.BIND_AUTO_CREATE) 方法来启动服务,intent 包含了目标服务 MyService,mMyServiceConnection 则是 ServiceConnection 类型的对象;连接成功后 MyService 的 onBind 方法就会返回 Messenger 的 Binder 对象,客户端的 ServiceConnection 实现类从 onServiceConnected 方法中拿到回调的 IBinder 类型的对象 service,然后通过 service 作为参数去构造一个 Messenger 类型的对象 mMessenger,这个时候的 mMessenger 是服务 MyService 的 mMessenger;我们再创建一个 Messenger 类型的对象 mClientMesstenger,这个 mClientMesstenger 是属于客户端的,当我们客户端用 mMessenger 发送数据给服务的时候,顺便把 mClientMesstenger 传送过去,这样服务就能用 mClientMesstenger 发送数据给客户端了。
2、源码分析
我们来看服务 MyService 的 onBind 方法
override fun onBind(intent: Intent?): IBinder {
return mServiceMesstenger!!.binder
}
再点 mServiceMesstenger!!.binder 进去看看,其实它是调用了 Messenger 的 getBinder 方法
public IBinder getBinder() {
return mTarget.asBinder();
}
mTarget 是 IMessenger 接口,是通过前面我们写的 MyService 中的 onCreate 方法构造出来的,如下所示
override fun onCreate() {
super.onCreate()
mHandler = MessengerServiceHandler()
mServiceMesstenger = Messenger(mHandler)
}
点击 Handler 为参数的 Messenger 构造器,发现是 Handler 对象的 getIMessenger 方法返回
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
我们查看 Handler 的 getIMessenger 方法
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
点击 MessengerImpl 类代码往下看
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
从 IMessenger.Stub 这里可以看出使用 AIDL,所以 Messenter 封装了 AIDL。
我们看一下客户端中 ServiceConnection 的实现类中 onServiceConnected 方法是怎么拿到 Messenter 对象的
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mMessenger = Messenger(service)
}
点击进去 Messenter 的构造器看看
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
从客户端这里可以看出,创建一个 Messenter 使用IMessenger.Stub.asInterface(target) 拿到接口实例进行调用,这里它构造 Messenter 对象用 IBinder 作为参数,它的底层也是用 AIDL 来实现的,这里客户端 mMessenter 和服务端的 mServiceMesstenger 并非是同一个对象,它们能够通信,是因为它们能拿到同一个对象 Binder。
以上是关于Android中的IPC进程通信方式第二篇的主要内容,如果未能解决你的问题,请参考以下文章