前言:Binder的介绍
在 Android 中,Binder 是一种跨进程通信(IPC)机制,它是基于进程间通信(IPC)机制中的共享内存和消息传递机制实现的。Binder 的主要作用是在 Android 操作系统中跨进程传递数据和服务,例如 Activity、Service、Broadcast 等都是通过 Binder 实现跨进程通信和交互的
Binder 是 Android 系统的一个重要组成部分,它主要由以下三部分组成:
Binder 驱动:位于底层的驱动程序,它负责提供进程间通信的基础设施和实现对 Binder 对象的创建和跟踪。
Binder API:位于 Java 层,是 Android 应用程序和 Binder 驱动之间的接口,它提供了创建 Binder 服务和跨进程访问 Binder 服务的方法和实现。
Binder 对象:也称为 Binder 服务,是 Android 应用程序中提供跨进程通信的关键对象。每个 Binder 对象都有一个唯一的标识符,被用来标识不同的 Binder 服务,其他进程可以通过这个标识符获取对这个服务的引用并使用它。
在 Android 应用程序中使用 Binder 通常可以通过两种方式实现,一种是使用 AIDL(Android 接口定义语言)定义接口,并通过 Binder 服务实现接口的跨进程调用,另一种是通过 Messenger 实现基于消息的跨进程通信。
本篇使用Binder实现一个模拟第三方QQ登录的一个效果,效果图如下
一、首先创建服务端程序BinderB,详细步骤如下
1、创建BLoginActivity类和activity_b_login.xml,布局不再给出,java代码如下:
private const val NAME = "android" private const val PWD = "123456" class BLoginActivity : AppCompatActivity() { private var isStartRemote = false private lateinit var iLogin: ILoginInterface private var conn = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { iLogin = ILoginInterface.Stub.asInterface(service) } override fun onServiceDisconnected(name: ComponentName?) { TODO("Not yet implemented") } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_b_login) btn_login.setOnClickListener { var account = et_account.text.toString() var pwd = et_password.text.toString() if (TextUtils.isEmpty(account) || TextUtils.isEmpty(pwd)) { ToastUtils.showShort("账号或者密码为空!") return@setOnClickListener } val progress = ProgressDialog(this) progress.setTitle("登录") progress.setMessage("登录中") progress.show() Thread { SystemClock.sleep(2000) runOnUiThread { var loginStatus = false if (NAME == account && PWD == pwd) { ToastUtils.showShort("登录成功!") loginStatus = true finish() } else { ToastUtils.showShort("登录失败!") } iLogin.loginCallback(loginStatus, account) progress.dismiss() } }.start() } initBindService() } private fun initBindService() { val intent = Intent() intent.action = "BinderA_Action" intent.`package` = "com.king.learn" //客户端程序的包名 bindService(intent, conn, Context.BIND_AUTO_CREATE) isStartRemote = true } override fun onDestroy() { super.onDestroy() if (isStartRemote) { unbindService(conn) } } }
2、创建服务
class LoginService : Service() { override fun onBind(intent: Intent?): IBinder? { return object : ILoginInterface.Stub() { override fun login() { LogUtils.d("BinderB_LoginService") //单向通信,真实项目中,跨进程都是双向通信,双向服务绑定的 //做qq登录,qq分享的时候,需要填入自己的包名,就是这个原因 serviceStartActivity() } override fun loginCallback(loginStatus: Boolean, loginUser: String?) { TODO("Not yet implemented") } } } /** * 启动页面 */ private fun serviceStartActivity() { val intent = Intent(this, BLoginActivity::class.java) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK startActivity(intent) } }
2.1、并在清单文件中注册服务
<!-- android:enabled="true" 是否可以被系统实例化 android:exported="true" 是否可以能被其他应用隐式调用 android:process=":remote_server" 表示:应用程序当中需要使用该服务的话, 会自动创建名为:remote_server的进程 --> <service android:name=".service.LoginService" android:enabled="true" android:exported="true" android:process=":remote_server"> <intent-filter> <action android:name="BinderB_Action" /> </intent-filter> </service>
3、创建AIDL名字为ILoginInterface代码如下,服务端程序和客户端程序在同一个包名下面,我这里统一在package com.aidl;
interface ILoginInterface { void login();//登录 void loginCallback(boolean loginStatus,String loginUser);//登录回调 }
二、客户端程序A
1、创建步骤类似,首先创建AActivity 和activity_a.xml
class AActivity : AppCompatActivity() { private var isStartRemote: Boolean = false private var iLogin: ILoginInterface?=null /** * 服务的连接 */ private var conn: ServiceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { //使用服务端的功能(方法) iLogin = ILoginInterface.Stub.asInterface(service) } override fun onServiceDisconnected(name: ComponentName?) { LogUtils.d("测试") } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_a) initBindService() btn_login.setOnClickListener { if (iLogin != null) { try { iLogin?.login() } catch (e: Exception) { e.printStackTrace() } } else { ToastUtils.showShort("请先安装QQ应用!") } } } /** * 绑定服务 */ private fun initBindService() { val intent = Intent() //设置Server应用Action(服务的唯一标识) intent.action = "BinderB_Action" //设置Server应用包名 intent.`package` = "com.king.kotlinproject" bindService(intent, conn, BIND_AUTO_CREATE) isStartRemote = true } override fun onDestroy() { super.onDestroy() if (isStartRemote) { //解绑服务,一定要记得,不然服务连接异常 unbindService(conn) } } }
2、创建三方应用登录后,返回的服务
class ResultService : Service() { override fun onBind(intent: Intent?): IBinder? { return object : ILoginInterface.Stub() { override fun login() { TODO("Not yet implemented") } override fun loginCallback(loginStatus: Boolean, loginUser: String?) { //第三方登录成功后,不用挂起,直接打印了日志 LogUtils.d("loginStatus:$loginStatus/ loginUser:$loginUser") } } } }
在AndroidManifest.xml清单文件中,进行注册
<service android:name=".service.ResultService" android:enabled="true" android:exported="true" android:process=":remote"> <intent-filter> <action android:name="BinderA_Action"/> </intent-filter> </service>
3、创建对应的AIDL ,ILoginInterface
interface ILoginInterface { void login();//登录 void loginCallback(boolean loginStatus,String loginUser);//登录回调 }
日志输入如下:
在服务端B已经启动的情况下,启动客户端程序A,点击进行三方登录
服务端B程序进行日志打印:
在服务端B程序进行登录后,跳转到客户端程序A,打印的
日志如下: