前言
在Android跨进程通信的方式有很多比如广播,ContentProvider,AIDL等等,它们各自有各自的使用范围。而且AIDL更像是Java的ServerSocket通信机制, 需要一个常驻的服务端与调用它的客户端。AIDL现在的缺点可能也是需要一个服务配合使用。因为目前Android端Server的使用要求越来越多(前台化),使用场景也越来越少(WorkManager替代)。 AIDL更多的是在开发设备应用与系统开发中使用。切记不可随便选择AIDL技术。这样只会给你的项目带来混乱与大量强入侵性的代码。
这里提一下在设备开发中使用AIDL重要性。 在设备开发中,如果是自定义launcher就会大量涉及到系统功能开发,比如亮度调节、音量调节等等。当然一部分功能也可以反射代码(在MtkSettings工程里的大量功能都可以反射实现)与架framework包解决,但是有些系统功能已经是跨进程了(比如监听更底层的按键事件,触控点击事件无法在应用进程中反射与调用),无法依靠反射与架framework包解决。当然你也可以使用广播机制解决跨进程的调用,但是广播机制的使用过于零散。在系统中内置一个调用framework系统进程代码的AIDL是解决问题的正确思路。
简单实现Demo
服务端
创建AIDL
完成后,在这个默认文件里会有一些默认方法,它告诉你了AIDL可以使用那些类型数据
编辑AIDL文件
编辑完成后,ReBuild 重新编译下整个项目,Android stuido会自动在debug里生成一份对应的aidl的java接口类
package com.zh.aidl; interface IDemoService { void setNum(int num); int getNum(); }
自动生成的java接口
创建服务,将服务绑定AIDL
注册清单,服务一定需要安装下面的方式注册,否则客户端会出现无法绑定服务的问题
<!-- android:enabled="true" 与 android:exported="true" 是必要属性 --> <!-- android:enabled 定义服务能否被系统实例化的标签,true表示可以实例化,false不能实例化,默认为true。标签也有enabled标签,这个标签适用于application下所有组件。 只有当和下enabled标签的属性都为true的时候,才可以将广播接受者启动(enabled),否则广播接受者不能开启(disabled),不能被实例化。 --> <!-- android:exported 定义服务能否被外部应用的组件调用或者交互,true表示可以,false表示不能。 如果设置为false,服务只能接收本应用的组件或者是具有相同用户ID的应用所发出的所开启或绑定。 --> <service android:name=".DemoService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="DemoService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service> </application>
创建服务
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.app.Service; import android.content.Intent; import android.graphics.BitmapFactory; import android.graphics.Color; import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.util.Log; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; public class DemoService extends Service { private static final String CHANNEL_ID = "DemoService"; private int mNum = 0; @Nullable @Override public IBinder onBind(Intent intent) { //绑定AIDL return iBinder; } /** * 实例化AIDL */ private IBinder iBinder = new IDemoService.Stub() { @Override public void setNum(int num) throws RemoteException { mNum = num; Log.e("zh", "服务器端接收数据: Num = " + num); } @Override public int getNum() throws RemoteException { return mNum; } }; @Override public void onCreate() { super.onCreate(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { frontDeskService(); } } /** * 前台服务 */ @RequiresApi(api = Build.VERSION_CODES.O) private void frontDeskService() { NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "主服务", NotificationManager.IMPORTANCE_HIGH); channel.enableLights(true);//设置提示灯 channel.setLightColor(Color.RED);//设置提示灯颜色 channel.setShowBadge(true);//显示logo channel.setDescription("zh");//设置描述 channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); //设置锁屏可见 VISIBILITY_PUBLIC=可见 manager.createNotificationChannel(channel); Notification notification = new Notification.Builder(this) .setChannelId(CHANNEL_ID) .setContentTitle("主服务")//标题 .setContentText("运行中...")//内容 .setWhen(System.currentTimeMillis()) .setSmallIcon(R.mipmap.ic_launcher)//小图标一定需要设置,否则会报错(如果不设置它启动服务前台化不会报错,但是你会发现这个通知不会启动),如果是普通通知,不设置必然报错 .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)) .build(); startForeground(1, notification); } }
启动服务
private fun startService() { val intent = Intent(this, DemoService::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(intent) } else{ startService(intent) } }
客户端
将服务端的AIDL文件复制到客户端里
注意!包名路径要一致
绑定服务,接收数据,发送数据
请注意,这里的setAction需要与上面清单中注册的一致
private var mIDemoService : IDemoService? = null private var mIsBindService = false private val mServiceConnection = object :ServiceConnection{ override fun onServiceConnected(name: ComponentName?, service: IBinder?) { mIsBindService = true mIDemoService = IDemoService.Stub.asInterface(service) } override fun onServiceDisconnected(name: ComponentName?) { mIsBindService = false mIDemoService = null } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(mBinding.root) mBinding.connectService.setOnClickListener { //绑定服务 val intent = Intent().apply { setPackage("com.zh.aidl") setAction("DemoService") } bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE) } mBinding.sendValue.setOnClickListener { //发送数据 mIDemoService?.num = mBinding.editTextNumber.text.toString().toInt() } mBinding.getValue.setOnClickListener { //接收数据 Log.e("zh", "客户端接收: num = " + mIDemoService?.num) } }
自定义数据
服务端
目录结构
创建自定义Bean
UserBean数据需要添加序列化
在服务接口里引入UserBean
请注意!在参数前面需要添加 in 关键字。不添加会报错
其他绑定服务跟上面的demo是一样的
客户端
与上面的Demo一样需要把服务端的AIDL文件复制到客户端项目里,这里的UserBean一样有路径保持一致的要求,并且也是需要序列化的。
其他步骤与上面的Demo一致
AIDL实现接口回调
服务端
创建AIDL文件
IMyAidlInterface.aidl
// IMyAidlInterface.aidl
package com.zh.aidldemo;
//请注意,这里是容易疏漏的地方,需要用import将IMyListener引入
import com.zh.aidldemo.IMyListener;
interface IMyAidlInterface {
void setListener(IMyListener listener);
}
IMyListener.aidl
这个是接口回调
// IMyListener.aidl
package com.zh.aidldemo;
interface IMyListener {
void onEvent(boolean type);
}
创建服务
请注意!在清单中添加服务,也与上面的例子一致,这里不在重复
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
class MyService : Service() {
var mIMyListener: IMyListener? = null
companion object {
/**
* 启动服务
*/
fun startService(context: Context) {
val intent = Intent(context, MyService::class.java)
context.startService(intent)
}
}
private val mIBinder = object : IMyAidlInterface.Stub() {
override fun setListener(listener: IMyListener?) {
//这里处理接口
mIMyListener = listener
}
}
override fun onBind(intent: Intent?): IBinder? {
return mIBinder
}
}
客户端
导入aidl文件与上面的例子一样(需要保持路径与文件的一致),不在重复举例
绑定AIDL服务,添加接口回调
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import android.util.Log
class ClientAIDL {
private var mIMyAidlInterface: IMyAidlInterface? = null
private var mIsBindService = false
//请注意,这里一定要让IMyListener.Stub成为全局变量被持有,如果是方法内部的局部变量可能会出现 java.lang.RuntimeException: java.lang.reflect.InvocationTargetException 的错误
private var mIMyListener: IMyListener.Stub? = null
companion object {
val instance by lazy { ClientAIDL() }
}
private val mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.e("zh", "触发绑定服务 onServiceConnected: ")
mIsBindService = true
mIMyAidlInterface = IMyAidlInterface.Stub.asInterface(service)
}
override fun onServiceDisconnected(name: ComponentName?) {
mIsBindService = false
mIMyAidlInterface = null
}
}
/**
* 绑定服务
*/
fun bindService(context: Context) {
val intent = Intent().apply {
setPackage("com.zh.aidldemo")
setAction("aidldemo")
}
context.bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
}
/**
* 设置监听
*/
fun setListener() {
mIMyListener = object : IMyListener.Stub() {
override fun onEvent(type: Boolean) {
//处理回调
}
}
mIMyAidlInterface?.setListener(mIMyListener)
}
}
End
本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/16310607.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库