Android service介绍——跨进程之Messenger方式介绍和源码初步梳理(2)
问题背景 前一篇文章介绍了service的基本使用(参考 https://blog.51cto.com/baorant24/6091264 ),包括使用startService和bindService两种启动方式,其中介绍通过bindService()方法去绑定启动一个进程内部的service。如果要跨进程(IPC)的方式去绑定服务,可以有几种方式呢?一般来说,跨进程绑定服务有Messenager和AIDL两种,本文将进一步介绍通过Messenager跨进程绑定service的使用。 问题分析 (1)new一个service,作为我们要跨进程启动的远程服务,代码如下:
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.*
import android.widget.Toast
import java.lang.ref.WeakReference
class MessengerService : Service() {
// 构造Messenger对象
val mMessenger: Messenger = Messenger(IncomingHandler(this, Looper.myLooper()!!))
// 绑定服务后返回mMessenger对应的binder对象
override fun onBind(intent: Intent): IBinder? {
Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT)
.show()
return mMessenger.binder
}
companion object {
const val MSG_SAY_HELLO = 1
class IncomingHandler constructor(context: Context, looper: Looper): Handler(looper) {
var weakReference = WeakReference(context)
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_SAY_HELLO -> Toast.makeText(
weakReference.get(), "hello!",
Toast.LENGTH_SHORT
).show()
else -> super.handleMessage(msg)
}
}
}
}
}
(2)manifest配置文件中,对service进行配置,配置远程服务,代码如下:
<service
android:name="composer.service.MessengerService"
android:process=":remote"
android:exported="true"
android:enabled="true" />
(3)新建一个activity,去绑定刚才的远程服务,代码如下:
import android.app.Activity
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.*
import android.view.LayoutInflater
import android.view.View
import composer.databinding.ActivityTestMessengerBinding
import composer.service.MessengerService
import kotlinx.android.synthetic.main.activity_test_messenger.*
class TestMessengerActivity : Activity() {
var mService: Messenger? = null
var mBound = false
lateinit var binding: ActivityTestMessengerBinding
private val mConnection: ServiceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
mService = Messenger(service)
mBound = true
}
override fun onServiceDisconnected(className: ComponentName) {
mService = null
mBound = false
}
}
fun sayHello(v: View?) {
if (!mBound) return
val msg: Message = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0)
try {
mService!!.send(msg)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTestMessengerBinding.inflate(LayoutInflater.from(this))
setContentView(binding.root)
bindMessengerService.setOnClickListener {
bindService(Intent(this, MessengerService::class.java), mConnection, Context.BIND_AUTO_CREATE)
}
}
override fun onStop() {
super.onStop()
if (mBound) {
unbindService(mConnection)
mBound = false
}
}
}
(4)activity对应的layout布局文件,代码如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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_width="match_parent"
android:layout_height="match_parent"
tools:context=".view.TestStartServiceActivity">
<Button
android:id="@+id/bindMessengerService"
android:text="bind MessengerService"
app:layout_constraintTop_toTopOf="parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:text="say hello"
app:layout_constraintTop_toBottomOf="@id/bindMessengerService"
android:layout_width="match_parent"
android:onClick="sayHello"
android:layout_height="wrap_content"/>
</androidx.constraintlayout.widget.ConstraintLayout>
运行项目代码,先点击bindService按钮不,再点击下面的sayHello按钮,效果如下: 问题解决 上面可以看到,通过使用Messenger,我们可以跨进程绑定远程service,我们可以进一步看看相关代码流程。 (1)远程服务中构造Messenger对象
// 构造Messenger对象
val mMessenger: Messenger = Messenger(IncomingHandler(this, Looper.myLooper()!!))
(2)查看Messenger对应的构造方法 android.os.Messenger#Messenger(android.os.Handler)
public final class Messenger implements Parcelable {
private final IMessenger mTarget;
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
(3)android.os.Handler#getIMessenger
@UnsupportedAppUsage
final IMessenger getIMessenger() {
synchronized (mQueue) {
if (mMessenger != null) {
return mMessenger;
}
mMessenger = new MessengerImpl();
return mMessenger;
}
}
(4)android.os.Handler.MessengerImpl
private final class MessengerImpl extends IMessenger.Stub {
public void send(Message msg) {
msg.sendingUid = Binder.getCallingUid();
Handler.this.sendMessage(msg);
}
}
(5)service被绑定成功后
// 绑定服务后返回mMessenger对应的binder对象
override fun onBind(intent: Intent): IBinder? {
Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT)
.show()
return mMessenger.binder
}
(6)android.os.Messenger#getBinder
public IBinder getBinder() {
return mTarget.asBinder();
}
可以看到,Messenger的底层还是走的AIDL这一套逻辑。 问题总结 前一篇文章介绍了service的基本使用(参考 https://blog.51cto.com/baorant24/6091264 )其中介绍通过bindService()方法去绑定启动一个进程内部的service。如果要跨进程(IPC)的方式去绑定服务,可以有几种方式呢?一般来说,跨进程绑定服务有Messenager和AIDL两种,本文进一步介绍了通过Messenager跨进程绑定service的使用。后面会继续介绍AIDL方式的使用。有兴趣的同学可以进一步深入研究。