kotlin优雅实现AIDL
AIDL(Android 接口定义语言)与您可能使用过的其他 IDL 类似。 您可以利用它定义客户端与服务使用进程间通信 (IPC) 进行相互通信时都认可的编程接口。 在 Android 上,一个进程通常无法访问另一个进程的内存。 尽管如此,进程需要将其对象分解成操作系统能够识别的原语,并将对象编组成跨越边界的对象。 编写执行这一编组操作的代码是一项繁琐的工作,因此 Android 会使用 AIDL 来处理。
实现AIDL有下面几个步骤
1、定义.aidl文件,您开发每个包含 .aidl 文件的应用时,Android SDK 工具都会生成一个基于该 .aidl 文件的 IBinder 接口,并将其保存在项目的 gen/ 目录中。服务必须视情况实现 IBinder 接口。然后客户端应用便可绑定到该服务,并调用 IBinder 中的方法来执行 IPC。aidl支持的类型有string、charSequence、list、map等。
定义服务接口时,请注意:方法可带零个或多个参数,返回值或空值。所有非原语参数都需要指示数据走向的方向标记。可以是 in、out 或 inout(见以下示例)。原语默认为 in,不能是其他方向
示例:
interface Data { //查询数据 List<String> getData(); //添加数据 void setData(String s);}
2、定义一个服务,用户绑定返回一个IBinder
class MyService : Service() { //返回IBinder、DataAiDl override fun onBind(intent: Intent): IBinder? { return DataAiDl() }}
DataAiDl.kt的代码实现,注意看注释
/** * Created by aiiage on 2018/8/9. * DataAiDl继承Data.Stub()实现AIDL */class DataAiDl : Data.Stub(){ //设置数据 override fun setData(s: String) { Log.e("MainActivity",s) list.add(s) } var list= ArrayList() //添加数据 override fun getData(): MutableList { return list }}
3、在activity中连接并绑定服务,代码如下
class MainActivity : BaseActivity(),View.OnClickListener { private var TAG="MainActivity" private var i=0 //由AIDL文件生成的Java类 private var data: Data? = null //标志当前与服务端连接状况的布尔值,false为未连接,true为连接中 private var mBound=false //定义数据链表 var list= ArrayList<String>() //控件点击事件 override fun onClick(p0: View) { //如果与服务端的连接处于未连接状态,则尝试连接 if (!mBound) { attemptToBindService() Toast.makeText(this, "当前与服务端处于未连接状态,正在尝试重连,请稍后再试", Toast.LENGTH_SHORT).show() return } if(data==null) return when(p0.id){ R.id.addDataBty->{ data!!.setData("data"+i) i++ } R.id.seeDataBty->{ list= data!!.data as ArrayList<String> var sb=StringBuffer() for (d in list) { sb.append(d).append("\n") } textView.text=sb } } } //获取布局文件id override fun getContentViewLayoutID(): Int { return R.layout.activity_main } //初始化控件 override fun initView(savedInstanceState: Bundle?) { addDataBty.setOnClickListener(this) seeDataBty.setOnClickListener(this) } //连接服务器 private fun attemptToBindService() { val intent = Intent() Log.e(TAG, " connected now") intent.action = "com.andream.aiiage.Data" //在AndroidManifest.xml进行配置隐形启动action intent.`package` = "com.andream.aiiage.aidlkotlin" //你的包名 bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE) } private val mServiceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName, service: IBinder) { Log.e(TAG, "service connected") data = Data.Stub.asInterface(service) mBound = true if (data != null) { try { list = data!!.data as ArrayList<String> } catch (e: RemoteException) { e.printStackTrace() } } } override fun onServiceDisconnected(name: ComponentName) { Log.e(TAG, "service disconnected") mBound = false } } override fun onStart() { super.onStart() attemptToBindService() } override fun onStop() { super.onStop() unbindService(mServiceConnection) }}
4、记得在在AndroidManifest.xml进行配置隐形启动action来启动service
<service android:name=".MyService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name ="com.andream.aiiage.Data"/> intent-filter> service>
5、总结:AIDL可以进行跨进程通讯,配置和使用起来简单。如果你想把某个类跨进程传递的话,该类必须支持 Parcelable 接口。支持 Parcelable 接口很重要,因为 Android 系统可通过它将对象分解成可编组到各进程的原语。
6、注意:AIDL 接口的调用是直接函数调用。 您不应该假设发生调用的线程。 视调用来自本地进程还是远程进程中的线程,实际情况会有所差异。 具体而言:
来自本地进程的调用在发起调用的同一线程内执行。如果该线程是您的主 UI 线程,则该线程继续在 AIDL 接口中执行。 如果该线程是其他线程,则其便是在服务中执行您的代码的线程。 因此,只有在本地线程访问服务时,您才能完全控制哪些线程在服务中执行(但如果真是这种情况,您根本不应该使用 AIDL,而是应该通过实现 Binder 类创建接口)。
来自远程进程的调用分派自平台在您的自有进程内部维护的线程池。 您必须为来自未知线程的多次并发传入调用做好准备。 换言之,AIDL 接口的实现必须是完全线程安全实现。
oneway 关键字用于修改远程调用的行为。使用该关键字时,远程调用不会阻塞;它只是发送事务数据并立即返回。接口的实现最终接收此调用时,是以正常远程调用形式将其作为来自 Binder 线程池的常规调用进行接收。 如果 oneway 用于本地调用,则不会有任何影响,调用仍是同步调用。
7、工程目录和效果图
8、附录
baseactivity.java代码
abstract class BaseActivity : AppCompatActivity() { /** * 获取布局id */ protected abstract fun getContentViewLayoutID(): Int /** * 初始化控件 */ protected abstract fun initView(savedInstanceState: Bundle?) override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) if (getContentViewLayoutID()!=0) { setContentView(getContentViewLayoutID()) initView(savedInstanceState) } } protected fun showToast(desc: String) { Toast.makeText(this,desc,Toast.LENGTH_SHORT).show() }}
更多相关文章
- Android怎样绕开Camera打开手电筒的LED
- 第三部分:Android(安卓)应用程序接口指南---第三节:应用程序资源--
- andorid ANR keyDispatchingTimedOut的原因和解决之道
- 备战面试旺季:2019年Android面试题整理(组件+View+线程+面经)
- Android(安卓)Nougat多窗口简析
- Android(安卓)任务、进程和线程
- Android(安卓)Service解析解析再解析
- Android开发之旅(二)服务生命周期和广播接收者生命周期
- Android(安卓)AsyncTask与handler