一、前言
- 什么是AIDL?
- AIDL用来做什么的?
- 怎么使用AIDL?
AIDL是Android的一种接口定义语言,语法跟java接口定义类似,文件格式为 .aidl 非 .java 。AIDL主要是用来实现跨进程通信,AIDL的本质也是通过Binder机制实现的,最终也会通过绑定同一个远程服务来实现通信。下面我写了两个Demo,让DemoB发送消息,DemoA能显示在界面上,示例:
二、DemoA(服务端)
1、创建AIDL文件
- 在main下先创建个aidl文件夹,在此目录右键new AIDL文件IChatAidlInterface.aidl,As会自动在aidl文件夹下添加一个包目录
- IChatAidlInterface.aidl内容如下
// IChatAidlInterface.aidl package com.yufs.demoa; // 定义AIDL接口文件 interface IChatAidlInterface { //添加好友 void addFriends(String remark); //发送消息 void sendMsg(String content); /** * 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); }
-
build一下
-
-
在如下目录会自动生成一个java对应的接口类,内部有一个抽象的Sub类继承自Binder,还有一个代理类Proxy实现了当前接口
-
2、创建本地的业务实现类ChatAidlImpl,继承自IChatAidlInterface.Stub(),
class ChatAidlImpl:IChatAidlInterface.Stub() {
var messenger: Messenger?=null
companion object{
const val TAG = "yufs"
}
override fun addFriends(remark: String) {
Log.e(TAG,"收到添加好友请求:$remark")
val msg =Message.obtain()
msg.what = WHAT_ADD_FRIEND_KEY
val bundle = Bundle()
bundle.putString(CONTENT_KEY,remark)
msg.data = bundle
messenger?.send(msg)
}
override fun sendMsg(content: String) {
Log.e(TAG,"收到一条消息:$content")
val msg =Message.obtain()
msg.what = WHAT_MSG_KEY
val bundle = Bundle()
bundle.putString(CONTENT_KEY,content)
msg.data = bundle
messenger?.send(msg)
}
override fun basicTypes(
anInt: Int,
aLong: Long,
aBoolean: Boolean,
aFloat: Float,
aDouble: Double,
aString: String?
) {
}
}
在上面代码中,我们接收到DemoB发送的消息后并通过Messenger转发消息通知本地更新UI,这里也涉及到一个跨进程通信,是发生在DemoB中的一个远程服务通知本地UI更新
3、创建远程服务ChatService,并重写onBind,返回上面实现类ChatAidlImpl的实例对象
-
class ChatService:Service() { companion object{ const val MESSENGER = "messenger" } private val chatAidlImpl = ChatAidlImpl() private var mMessenger: Messenger?=null override fun onBind(intent: Intent?): IBinder? { mMessenger = intent?.getParcelableExtra(MESSENGER) //避免被其他绑定的服务空值覆盖 mMessenger?.let { chatAidlImpl.messenger = mMessenger } return chatAidlImpl } }
- 并在manifest文件中注册
-
<!--注册服务--> <service android:name=".service.ChatService" android:exported="true" android:process=":remote" > <!--允许通过隐式意图激活绑定服务--> <intent-filter> <action android:name="com.yufs.demoa.service.ChatService"/> </intent-filter> </service>
4、 MainActivity中通过绑定服务来接收显示
class MainActivity : AppCompatActivity() {
private lateinit var tvContent:TextView
private lateinit var tvHint:TextView
private val msgs = mutableListOf<String>()
private val mHandler = MyHandler(this)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvContent = findViewById(R.id.tv_content)
tvHint = findViewById(R.id.tv_hint)
val messenger = Messenger(mHandler)
//绑定服务用于服务中通知当前界面更新UI
val intent = Intent(this,ChatService::class.java)
intent.putExtra(ChatService.MESSENGER,messenger)
bindService(intent,conn, BIND_AUTO_CREATE)
}
class MyHandler(activity: MainActivity) :Handler(Looper.myLooper()!!){
private val weakReference = WeakReference(activity)
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){
WHAT_MSG_KEY ->{
val content = msg.data.getString(CONTENT_KEY)?:""
weakReference.get()?.apply {
msgs.add(content)
Log.e("yufs","size:${msgs},")
tvContent.text = msgs.joinToString("\n")
}
}
WHAT_ADD_FRIEND_KEY ->{
val content = msg.data.getString(CONTENT_KEY)?:""
weakReference.get()?.apply {
tvHint.visibility = View.VISIBLE
tvHint.text = content
}
}
}
}
}
private val conn = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.e("yufs","服务连接成功")
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
}
三、DemoB(客户端)
DemoB作为客户端,代码就简单得很多了,只需要绑定下DemoA中定义的远程服务,调用AIDL文件下定义的接口方法即可
1、把DemoA中的aidl文件夹完整的复制过来,aidl文件路径两端必须一样,然后build一下,同样会自定生成java对应的接口类
2、创建服务连接对象,初始化接口IChatAidlInterface代理对象
//声明AIDL接口对象
private lateinit var iChatAidlInterface: IChatAidlInterface
private val conn = object :ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
//根据实际情况返回 IBinder 的本地对象或其代理对象
iChatAidlInterface = IChatAidlInterface.Stub.asInterface(service)
Log.e(TAG,"服务连接成功")
}
override fun onServiceDisconnected(name: ComponentName?) {
//服务断开回调
}
}
3、通过隐式意图来绑定服务
其中com.yufs.demoa.service.ChatService为DemoA中注册服务时的action。包名也为DemoA的包名
//因为跨进程拿不到具体引用,需要指定包名和全路径来绑定远程服务
val intent = Intent("com.yufs.demoa.service.ChatService")
//远程应用APP包名,5.0以上需要
intent.setPackage("com.yufs.demoa")
//绑定远程服务
bindService(intent,conn, Context.BIND_AUTO_CREATE)
4、调用接口方法,结果会在DemoA中回调显示
iChatAidlInterface.sendMsg(content)
iChatAidlInterface.addFriends("您好,我是张三")
四、总结
AIDL的文件编写相对来说不是很难,都是定义的一些接口方法,供外部调用。其中,我们通过自定义的远程服务ChatService作为中间组件,在两个应用间都绑定过一次,并且在DemoA中我们的远程服务与Activity的通信是通过Messenger来实现的,Messenger内部也是通过Binder机制将Message从一个进程传递到另一个进程。