这里只是简单地教大家如何初步认识,如何利用AIDL来实现进程间通讯的代码编写,并非深入了解源码。
实现服务端
1、新建实体类,实现Parcelable接口
package com.tonydash.aidl.bean
import android.os.Parcel
import android.os.Parcelable
class Person() : Parcelable {
var name: String = ""
var age: Int = 0
constructor(name: String, age: Int) : this() {
this.name = name
this.age = age
}
constructor(parcel: Parcel) : this() {
name = parcel.readString().toString()
age = parcel.readInt()
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeString(name)
parcel.writeInt(age)
}
fun readFromParcel(dest: Parcel) {
name = dest.readString().toString()
age = dest.readInt()
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Person> {
override fun createFromParcel(parcel: Parcel): Person {
return Person(parcel)
}
override fun newArray(size: Int): Array<Person?> {
return arrayOfNulls(size)
}
}
}
大部分都是自动生成的代码,如果有自己需要的构造函数或者方法需要增加,自行增加就好。
这里为什么一定要实现Parcelable接口?
因为binder默认支持的就是8大基础类型,如果要传自己的对象类型的数据,就要实现Parcelable,使得这个对象是可以被打包的数据,从而进行数据传递。
2、新建aidl文件
image.png 先在main文件夹下面新建一个aidl文件夹,然后在aidl文件夹中新建aidl文件,创建成功后就会自动帮你加上包路径,不需要自己建立。这里需要注意的是,IPerson.aidl的包路径,是com.tonydash.aidl,所以对应下方java的路径,也只是com.tonydash.aidl,但是下方的Person类,却放在了bean文件夹中,这样默认的IPerson.aidl是找不到Person类的,所以我们需要改一下
//默认的包名是com.tonydash.aidl
//但是由于我的实体类Person,存放的位置我放在了com.tonydash.aidl.bean中
//所以这里也需要修改,不然无法关联到
package com.tonydash.aidl.bean;
//声明Person
parcelable Person;
interface IPersonAidlInterface {
void addPersonIn(in Person person);
}
3、新建一个Service
package com.tonydash.aidl
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import com.tonydash.aidl.bean.IPersonAidlInterface
import com.tonydash.aidl.bean.Person
class PersonService:Service() {
private val personList = ArrayList<Person>();
private val personBinder:IPersonAidlInterface.Stub =object : IPersonAidlInterface.Stub(){
override fun addPersonIn(person: Person?) {
Log.d("TAG","我是服务器,我收到一个客户端发过来的人:name = ${person?.name}")
}
}
override fun onCreate() {
super.onCreate()
Log.d("PersonService","PersonService onCreate")
// for (i in 1 .. 5){
// personList.add(Person("name$i",i))
// }
}
override fun onBind(intent: Intent?): IBinder {
return personBinder
}
}
4、MainActivity启动Service
package com.tonydash.aidl
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
startService(Intent(this,PersonService::class.java))
}
}
5、注册service
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tonydash.aidl">
<!--自定义权限,name 随便写,对应服务的权限就好-->
<permission
android:name="com.permission.aidl"
android:protectionLevel="normal" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AIDL">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--
exported = "true":可让其他 app 使用服务
permission: 若 exported = "true",则需要给这个服务一个自定义的权限
使用方若调用该服务就要声明这个权限,否则报错
-->
<service android:name=".PersonService"
android:exported="true"
android:permission="com.permission.aidl">
<intent-filter>
<!--定义action给客户端隐式调用,可随便写-->
<action android:name="com.tiga.action.aidl" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</service>
</application>
</manifest>
注册里会定义一个权限,是给其他APP调用时用的。到这里,服务端就完成了。
实现客户端
1、新建一个model或者project都可以
2、增加服务端定义的权限
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tonydash.client">
<permission android:name="com.permission.aidl"/>
<uses-permission android:name="com.permission.aidl"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AIDL">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
3、把服务端中的IPerson.aidl和Person类,连包路径一起都复制到client中。 image.png
一定要完全一致,不然会有问题
4、编写发送消息的逻辑
package com.tonydash.client
import android.content.ComponentName
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import com.tonydash.aidl.bean.IPersonAidlInterface
import com.tonydash.aidl.bean.Person
class MainActivity : AppCompatActivity() {
var tvName:TextView? = null
var iPerson:IPersonAidlInterface? = null
val connection:ServiceConnection = object : ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
iPerson = IPersonAidlInterface.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tvName = findViewById(R.id.tvName)
tvName?.setOnClickListener {
val person = Person()
person.name = "奔波儿灞"
person.age = 500
iPerson?.addPersonIn(person)
}
connectService()
}
private fun connectService() {
val intent = Intent()
//服务端包名
intent.setPackage("com.tonydash.aidl")
//服务端设置的 action
intent.action = "com.tiga.action.aidl"
bindService(intent, connection, BIND_AUTO_CREATE)
}
}
测试步骤
先把服务端APP启动起来,然后退到后台;
再把客户端APP启动起来,然后点击中间的TextView;
网友评论