参考《Android开发艺术探索》
Binder学习指南
Android跨进程通信:图文详解 Binder机制 原理
gityuan的Binder系列
关于Binder咱也看不明白罗老师写的那个源代码分析,就先看看上面博客入个门吧。。。
概念
多进程开启
序列化
内存映射
进程内存空间划分
Android IPC通信方式
Binder原理 注册服务、获取服务、使用服务、线程池
AIDL/Messenger
IPC:进程间通信,两个进程之间进行数据交换的过程。因为进程有自己的用户内存空间和内核空间,进程之间相互隔离,互相不能访问对方的user space属于独有不可共享空间, 并且user space访问kernel space是需要系统调用 copy_from_user()和copy_to_user()进行数据传递的。
Android有通过以下几种方式进行进程间通信
-
Intent Bundle可以在两个APP进程中进行传值
-
通过ContentProvider(基于Binder)以表格的形式组织数据,对其他应用的数据进行写改删查
-
通过文件传递数据,比如传递对象,可以把对象序列化写入文件,另一个APP再通过读取文件反序列化获取对象。序列化对象
3.1 序列化对象,实现Serializable或者Parcelable接口
3.2 写入文件 val oos = ObjectOutputStream(FileOutputStream(file)) oos.write(user)
3.3 读取文件val ois = ObjectInputStream(FileInputStream(file)) ois.readObject()
3.4 写入多个对象,读取时与写入顺序一致,依次读取 -
使用Messenger(基于Binder)
-
使用AIDL(基于Binder)
-
使用Socket
Android应用开启多进程
在manifest文件中对四大组件指定android:process属性
<service android:name=".service.MyService"
android:process="com.app.id:remote"/> // : 代表私有进程,可以省略前面的包名。其他应用组件不允许在这个进程中运行。
<service android:name=".service.MyService"
android:process="com.app.id.remote"/> // . 代表全局进程,其他进程组件如果拥有相同shareUID和相同签名可以进入该进程,可以互相访问data及组件信息
开启多进程的影响
- application会创建多次
- 内存互相之间不可访问,互相线程无法同步,无法访问对方进程中的数据。
- 一个应用的多个进程访问同一个SharedPreference,导致可靠性下降,因为不支持并发访问
Messenger
在不同进程中传递Message对象,轻量级IPC方案,底层是AIDL方式,只能传递Bundle支持的数据类型的数据。支持发送多个消息,串行执行。
两个角色: Server/Client 发送消息,如果想要接收回信,要给Message对象设置replyTo(Messenger)
Server
class ServiceUseMessenger : Service() {
companion object {
const val MSG_SAY_HELLO = 0X001;
const val MSG_SEND_TO_ACTIVITY = 0X002;
const val MSG_START = 0X003;
}
//1-9是客户端(activity)发给服务端(service)的消息处理
//使用messenger进行通讯
private val TAG: String = "ServiceUseMessenger"
// 1.创建一个消息处理器
class IncomingHandler(
context: Context,
private val applicationContext: Context = context.applicationContext,
private val service: ServiceUseMessenger = context as ServiceUseMessenger
) : Handler() {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_SAY_HELLO -> {
Toast.makeText(applicationContext, "hello", Toast.LENGTH_SHORT).show()
service.sendBack( msg.replyTo)
}
else -> {
super.handleMessage(msg)
}
}
}
}
// 2. 声明一个用来通信的messenger
private lateinit var mMessenger: Messenger
// 3. 在onbind时activity使用messenger给service发消息
override fun onBind(intent: Intent?): IBinder? {
Log.d(TAG, "onBind: ================")
mMessenger = Messenger(IncomingHandler(this))
return mMessenger.binder
}
// 4.对应操作在mainActivity里
Client
public class MainActivity : BaseActivity() {
// 新增使用Messenger和service通信
// 4.messenger声明,用来和service通信
private var mService: Messenger? = null
// 5.service是否绑定的flag
private var bound = false
// 6.bindservie的参数,用来监听是否连接
private val mConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mService = Messenger(service)
bound = true
}
override fun onServiceDisconnected(name: ComponentName?) {
mService = null
bound = false
}
}
// 7.加个按钮用来给service发送消息
fun sendMessage(view: View) {
if (!bound) return
val msg = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
selfMessenger = Messenger(ServerMsgHandler(this) )
//比较难设置,所以就在发消息的时候给service传过去这个messenger
msg.replyTo = selfMessenger
try {
mService?.send(msg)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
// 8.加个按钮给他绑定service
fun bindToService(view: View) {
Intent(this, ServiceUseMessenger::class.java).also { intent ->
bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
}
}
// 9.onstop的时候给他解绑service
private fun terminalService() {
if (bound) {
unbindService(mConnection)
bound = false
}
}
//在服务端(service)要发给客户端(activity)处理就需要在activity里也要有个消息处理器messageHandler
private class ServerMsgHandler(
context: Context,
private val applicationContext: Context = context.applicationContext
) : Handler() {
override fun handleMessage(msg: Message) {
when (msg.what) {
MSG_SEND_TO_ACTIVITY -> {
showToast("service sent to activity");
}
else -> {
super.handleMessage(msg)
}
}
}
fun showToast(s: String) {
Toast.makeText(applicationContext, s, Toast.LENGTH_SHORT).show()
}
}
//初始化一个用来处理service发来消息的处理器
private lateinit var selfMessenger: Messenger
}
AIDL
进程间通信方式之一,涉及不同APP中server端里处理多线程问题的需求用AIDL Android interface description language官网介绍
-
准备数据类main/java/com.app.aidl/Data implements Parcelable
-
定义数据类同名main/aidl/com.app.aidl/Data.aidl
-
创建接口aidl文件main/aidl/com.app.aidl/DataManager.aidl,里面定义要提供的方法
-
make project
-
定义Service组件, onBind()方法返回IBinder实现类: ManagerBinder
ManagerBinder extends DataManager.Stub{} //里面具体实现定义的方法
-
bindService(service,serviceConn,CREATE),在回调里获取对象,然后调用具体方法
serviceConn: ServiceConnection--> onServiceConnect(IBinder service) {dataManager = DataManager.Stub.asInterface(service)}
// Book.kt
@Parcelize
class Book(var name: String, var author: String,var publishYear: Int) : Parcelable
// Book.aidl
package com.wx.kotlinapp.aidl;
// Declare any non-default types here with import statements
import com.wx.kotlinapp.aidl.Book;
parcelable Book;
//BookDealer.aidl
import com.wx.kotlinapp.aidl.Book;
import com.wx.kotlinapp.aidl.DelayCallback;
interface BookDealer {
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
void showBookName(in Book book);
void purchasingBooks(in List<Book> book);
void delayPurchasing();
List<Book> showBooks(DelayCallback callback);
}
// BookDealerService
class BookDealerService : Service() {
override fun onBind(intent: Intent?): IBinder? {
return BookDealerBinder()
}
class BookDealerBinder : BookDealer.Stub(){
override fun basicTypes(anInt: Int, aLong: Long, aBoolean: Boolean, aFloat: Float, aDouble: Double, aString: String?
) {}
override fun showBookName(book: Book?) {}
override fun purchasingBooks(book: MutableList<Book>?) {}
override fun delayPurchasing() {}
override fun showBooks(callback: DelayCallback?): MutableList<Book> {return ArrayList<Book>()}
}
}
//activity里使用
class AIDLSampleActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_aidlsample)
}
private fun bindAIDLService() {
Intent(this,BookDealerService::class.java).also {
bindService(it,ConnectionCallback(), BIND_AUTO_CREATE)
}
}
private fun useAIDLFunction() {
bookDealer.showBookName(Book("little prince","some guy",2000))
bookDealer.purchasingBooks(
listOf(
Book("A","wx",2022),
Book("B","wx",2022),
Book("C","wx",2022),
)
)
}
private lateinit var bookDealer: BookDealer
inner class ConnectionCallback : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
bookDealer = BookDealer.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
TODO("Not yet implemented")
}
}
}
Binder的设计
这个设计我没有看过源码,就是看了最开头列举的博客,这里只记录下我记住了哪些东西。
Binder是Android提供的IPC方式,基于Client-Server通信模式,作用是在Client和Server中间建立一个通信管道,通过内存映射的方式共享数据,只需copy一次(从内核空间复制到client进程的用户控件)数据就可以达到传递数据的效果
内存映射就是把进程的虚拟内存地址和磁盘上的对象产生对应关系,多个进程的虚拟内存都可以指向同一个对象,其中一个进程操作了对象,其他进程也是可见的。
进程内存空间划分一个进程有用户空间(进程私有不可共享)和内核空间(进程共享),从线程的user space访问kernel space是要通过系统才能访问的
进程内存空间划分角色:
Server进程: 提供服务,支持多个Client进程向Server请求服务。
Client进程: 提供参数,通过Binder的代理对象调用Server服务。
Binder:就是用来创建虚拟内存区域,实现地址映射关系,Binder属于进程的内核空间,是虚拟的设备驱动,连接Server,Client和ServiceManager三个进程数据传输的桥梁。
ServiceManager: 管理Server的binder对象注册,用Binder类名的形式转化成Client中对该Binder的引用
Binder的使用流程
- 注册服务
Server通过Binder把自己注册到ServiceManager中,让ServiceManager拥有了server的进程信息。Server提供Binder实体 - 客户端获取服务
Client传递参数通过Binder给ServiceManager查找对应的server,再通过Binder把信息分发给Client, Server和Client建立连接。Client使用的Binder是serverBinder的一个代理对象。 - 使用服务
3.1 Binder实现内存映射:把内核空间缓存区域和server用户空间,通过Binder创建的接收缓存区建立映射关系
3.2 client发送参数给server: client把用户控件的参数复制到内核空间,又因为存在映射关系,写到内核空间相当于发送到server的用户空间,当前client请求服务的线程被挂起,Binder通知Server调用
3.3 server调用client请求的方法:收到binder通知后,server进程从线程池中取出线程,处理传入的参数包后调用方法,把结果卸载自己的用户空间
3.4 server进程返回结果给client:Biinder通知client取结果,client被挂起的线程被唤醒,client通过系统调用copy_to_user,把结果从内核空间拿到用户空间使用。
网友评论