前言
因为业务需要,需要实现两个app相互通信传递数据的功能,并可以回调
实现
准备:客户端的首先创建两个Android项目,一个项目为服务端,另外一个为客户端。服务端的applicationId(即包名)为com.project.service,客户端的applicationId(即包名)为com.project.client。
服务端
1.在app(应用)模块的build.gradle文件中进行aidl的配置
......
android {
......
sourceSets {
main {
aidl.srcDirs = ['src/main/aidl']
}
}
buildFeatures {
aidl true
}
......
}
......
2.在app/src/main创建aidl目录,然后创建com/project/service/test目录。后面的aidl文件都在该目录路径里面
3.创建IStudentManagerService.aidl文件,该文件编译后自动会在app/build/generated/aidl_source_output_dir/debug/out/com/project/service/test中生成继承Binder类的Sub抽象类。
// IStudentManagerService.aidl
package com.project.service.test;
import java.util.List;
import com.project.service.test.Student;
import com.project.service.test.StudentCallBack;
// Declare any non-default types here with import statements
interface IStudentManagerService {
//aidl中定义的方法传入的数据和返回数据的基本数据类型, 这个方法可删可不删,没有啥重要的
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
List<Student> getStudents();
int addStudent(in Student student);
int getStudentNumbers(int type);
void addStudentCallback(StudentCallBack studentCallback);
void removeStudentCallback(StudentCallBack studentCallback);
}
4.创建Student.aidl文件
// Student.aidl
package com.project.service.test;
parcelable Student;
5.创建StudentCallBack.aidl,用于后面服务端回调客户端
// StudentCallBack.aidl
package com.project.service.test;
import com.project.service.test.Student;
// Declare any non-default types here with import statements
interface StudentCallBack {
void selectPerfectStudentNow(in Student student,int stuNo);
}
6.在app/src/main/java/com/project/service/test目录下创建Student.kt,注意,Student.kt的目录路径(com/project/service/test)要和Student.aidl一样
package com.project.service.test
import android.os.Parcel
import android.os.Parcelable
/**
*
* @ProjectName: Test
* @Package: com.project.service.service
* @ClassName: Student
* @Description:
* @Author: gzl
* @CreateDate: 2023/10/17 11:45
*/
class Student() : Parcelable {
var stuNo: String? = null
var name: String? = null
var age: Int? = null
constructor(parcel: Parcel) : this() {
age = parcel.readInt()
stuNo = parcel.readString()
name = parcel.readString()
}
override fun writeToParcel(parcel: Parcel, flags: Int) {
parcel.writeInt(age ?: 0)
parcel.writeString(stuNo ?: "")
parcel.writeString(name ?: "")
}
override fun describeContents(): Int {
return 0
}
companion object CREATOR : Parcelable.Creator<Student> {
override fun createFromParcel(parcel: Parcel): Student {
return Student(parcel)
}
override fun newArray(size: Int): Array<Student?> {
return arrayOfNulls(size)
}
}
override fun toString(): String {
return "age $age \n name $name \n stuNo $stuNo \n"
}
}
7.在app/src/main/java/com/project/service目录或其他子目录下创建StudentManagerService.kt
package com.project.service
import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.os.RemoteCallbackList
import android.os.RemoteException
import android.util.Log
import com.project.service.test.IStudentManagerService
import com.project.service.test.Student
import com.project.service.test.StudentCallBack
class StudentManagerService : Service() {
val TAG = "StudentManagerService"
val remoteCallbackList = RemoteCallbackList<StudentCallBack>()
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate ")
}
private val binder = object : IStudentManagerService.Stub() {
override fun basicTypes(
anInt: Int,
aLong: Long,
aBoolean: Boolean,
aFloat: Float,
aDouble: Double,
aString: String?
) {
}
override fun getStudents(): List<Student> {
return listOf(Student().apply {
stuNo = "8"
name = "zss"
age = 9
})
}
override fun addStudent(student: Student?): Int {
Log.d(TAG, "addStudent ${student?.toString()} ")
student?.let { onAddStudentCallback(it) }
return 1
}
override fun getStudentNumbers(type: Int): Int {
Log.d(TAG, "getStudentNumbers $type ")
return 2
}
override fun addStudentCallback(studentCallback: StudentCallBack?) {
Log.d(TAG, "addStudentCallback ")
studentCallback?.let { remoteCallbackList.register(studentCallback) }
}
override fun removeStudentCallback(studentCallback: StudentCallBack?) {
Log.d(TAG, "removeStudentCallback ")
studentCallback?.let { remoteCallbackList.unregister(it) }
}
}
private fun onAddStudentCallback(student: Student) {
val callBackNum = remoteCallbackList.beginBroadcast()
for (i in 0 until callBackNum) {
try {
remoteCallbackList.getBroadcastItem(i).selectPerfectStudentNow(student, 3)
} catch (e: RemoteException) {
e.printStackTrace()
}
}
remoteCallbackList.finishBroadcast()
}
override fun onBind(intent: Intent?): IBinder {
return binder
}
}
8.在AndroidManifest.xml中注册服务
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<application
android:icon="@mipmap/ic_launcher"
android:label="service">
......
<service
android:name="com.project.service.StudentManagerService"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="com.project.service.StudentManagerService" />
</intent-filter>
</service>
......
</application>
</manifest>
客户端
1.在app(应用)模块的build.gradle文件中进行aidl的配置
......
android {
......
sourceSets {
main {
aidl.srcDirs = ['src/main/aidl']
}
}
buildFeatures {
aidl true
}
......
}
......
2.将服务端app/src/main/aidl整个目录包含(IStudentManagerService.aidl,Student.aidl,StudentCallBack.aidl)文件复制到客户端app/src/main目录中,注意目录结构与文件名必须和服务端保持一致,每个aidl文件里面的方法顺序也不能改变。
3.将服务端app/src/main/java/com/project/service/test/Student.kt文件复制到客户端app/src/main/java/com/project/service/test/目录中,注意目录结构与文件名,方法等必须和服务端保持一致
4.在activity中先绑定服务,然后调用服务端提供的方法addStudentCallback,把回调注入到服务端中,当调用addStudent之后,会回调StudentCallBack的方法。
package com.project.client.test
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.clickable
import androidx.compose.material3.Text
import androidx.compose.ui.Modifier
import com.project.service.test.IStudentManagerService
import com.project.service.test.Student
import com.project.service.test.StudentCallBack
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Text("Hello world!", modifier = Modifier.clickable {
startCallServiceMethod()
})
}
bindService()
}
var iStudentService: IStudentManagerService? = null
private fun bindService() {
val intent = Intent()
intent.action = "com.project.service.StudentManagerService"
intent.setPackage("com.project.service")
intent.setClassName(
"com.project.service",
"com.project.service.StudentManagerService"
)
try {
bindService(intent, object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, iBinder: IBinder) {
Log.e("bindService", "onServiceConnected")
iStudentService = IStudentManagerService.Stub.asInterface(iBinder)
}
override fun onServiceDisconnected(componentName: ComponentName) {
iStudentService = null
Log.e("bindService", "onServiceDisconnected")
}
}, Context.BIND_AUTO_CREATE)
} catch (e: Exception) {
Log.e("bindService", e.message ?: "")
}
}
private fun startCallServiceMethod() {
iStudentService?.apply {
addStudentCallback(object : StudentCallBack.Stub() {
override fun selectPerfectStudentNow(student: Student?, stuNo: Int) {
Log.d("bindService", "selectPerfectStudentNow ${student?.toString()}")
}
})
}
iStudentService?.addStudent(Student().apply {
age = 78
name = "dpdpdp"
stuNo = "bbbbbbbb"
})
}
}
5.在AndroidManifest.xml中申请服务端app的包的访问,注意这边的action一定要和服务端注册服务时的intent-filter的action保持一致,代码中的action也是一致的
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<queries>
<package android:name="com.project.service" />
<intent>
<action android:name="com.project.service.StudentManagerService" />
</intent>
</queries>
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="client"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.Test">
......
</application>
</manifest>
注意:xml的package,setPackage等这些包名是app的applicationId.