Android AIDL使用

Android kotlin语言AIDL使用

AIDL作为Android的一个IPC工具,可以用于进程间通信,进程间通信不仅是不同应用间也可以是同一个应用的不同进程间。Java语言使用AIDL的文档网上有太多,这里不以Java为例,使用Kotlin为例进行讲解。

前言

AIDL英文全写为(Android Interface Definition Language)直译为android接口定义语言。就是用来定义不同进程间通信所用的接口的语言。说白了就是一个帮助我们简化Binder开发的工具,我们按照AIDL的语法进行编写之后进行build操作时SDK中的AIDL.exe会自动为我们在build.generated.source.aidl文件夹中的debug或者release文件夹中生成对应名称的java文件,这个java文件实际使用了Binder机制。此篇文章浅显的讲述Kotlin语言使用AIDL进行进程间通信,至于这个java文件如何编写,我会在下一篇文章中进行讲述。在此感谢**[Android 开发艺术探索]**的作者任玉刚老师对知识的分享。

image

步骤

  1. 定义数据实体类
  2. 创建aidl文件
  3. make project(build),更改生成的java文件
  4. 创建服务端服务
  5. 创建客户端服务

在进行讲述之前我们先看一张最后完成的服务端的整体结构

image

Person.aidl文件是实体类对应的aidl文件,IMyAidl.aidl文件是生成Java文件的依据文件,Person.kt文件是定义的实体类,MyAidlService.kt文件是服务端服务文件

一、定义数据实体类

Person.kt

package com.example.com.testapplication.bean.kotlin

import android.annotation.SuppressLint
import android.os.Parcelable
import kotlinx.android.parcel.Parcelize

@Parcelize
@SuppressLint("ParcelCreator")
class Person(var name: String) : Parcelable {

	override fun toString(): String {
    	return " [Person name = " + name + " ]"
	}
}

随便定义了一个实体类使用注解实现了Parcelable

二、创建aidl文件

在main文件夹下创建aidl文件夹,在其中添加跟项目路径一致的文件夹并在其对应文件夹中创建aidl文件

Person.aidl

// Person.aidl
package com.example.com.testapplication.bean.kotlin;

// Declare any non-default types here with import statements

parcelable Person;

IMyAidl.aidl

// IMyAidl.aidl
package com.example.com.testapplication;
import com.example.com.testapplication.bean.kotlin.Person;

// Declare any non-default types here with import statements

interface IMyAidl {
	/**
 	* 除了基本数据类型,其他类型的参数都需要标上方向类型:in(输入), out(输出), inout(输入输出)
 	*/
	/**
 	* Demonstrates some basic types that you can use as parameters
 	* and return values in AIDL.
 	*/
	//    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
	//            double aDouble, String aString);

	void addPerson(in Person person);

	List<Person> getPersonList();
}

三、make project(build),更改生成的java文件

在编写完aidl文件之后执行make project或者build之后会提示如下错误

image

虽然会出现上面的问题,但是需要的Java文件其实已经生成了。

这个错误的产生是因为kotlin的parcelable的实现CREATOR.createFromParcel会将返回类型当成any类型而不是指定类型,主要是因为kotlin的注解序列化机制产生的问题,如果你不是使用的@Parcelize注解进行的序列化那么就不会遇到这个问题,但是会遇到错误: CREATOR可以在Person中访问private,是因为Java与kotlin语言在机制上的冲突引起的。

解决上面的冲突方法也很简单,在本例中就是在生成的Java文件中使用Person.CREATOR.createFromParcel的地方加个强转

(Person) Person.CREATOR.createFromParcel(data)

四、创建服务端服务

在我们改完生成的Java文件后我们需要删除使用的aidl文件并将生成的文件放入对应的文件夹中而不是继续放在build中,因为在我们打包或者运行的时候每次都会检测aidl文件夹并且生成对应的文件,那么步骤三出现的问题就会一直出现。

上面的步骤做完之后我们就可以编写服务端具体逻辑代码了

MyAidlService.kt

package com.example.com.testapplication.service

import android.app.Service
import android.content.Intent
import android.os.IBinder
import com.example.com.testapplication.IMyAidl
import com.example.com.testapplication.bean.kotlin.Person

class MyAidlService : Service() {
	val mPersons by lazy { mutableListOf<Person>() }
	override fun onBind(intent: Intent?): IBinder = mIBinder

	private val mIBinder = object : IMyAidl.Stub() {
    	override fun addPerson(person: Person?) {
        	mPersons.add(person!!)

    	}

    	override fun getPersonList(): MutableList<Person> {
        	return mPersons
    	}
	}

}

之后需要在AndroidManifest.xml文件中注册服务。

 	<service
        android:name="com.example.com.testapplication.service.MyAidlService"
        android:enabled="true"
        android:exported="true"
        android:process=":aidl">
        <intent-filter>
            <action android:name="testapplication.service.MyAidlService" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </service>

至此,服务端的我们就完成了。

五、创建客户端服务

我们假设使用aidl的是为了实现不同应用间的信息传递,那么我们就需要在新创建一个应用,并在这个应用中对应的放入需要的文件。

那么我们都需要什么呢?我们需要同路径下的数据实体类、已经改完的生成Java文件。在此不建议直接导入aidl文件夹,因为还需要重新更改生成的文件还项目增加结构复杂度。

将我们需要的文件都导入之后我们就可以使用bind绑定我们在服务端的服务进行跨进程开发了

另一个程序的MainActivity.kt

package com.aidl.zhang.aidltestapplication

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.support.v7.app.AppCompatActivity
import com.example.com.testapplication.IMyAidl
import com.example.com.testapplication.bean.kotlin.Person
import kotlinx.android.synthetic.main.activity_main.*
import java.util.*

class MainActivity : AppCompatActivity() {
	private var mAidl: IMyAidl? = null

	override fun onCreate(savedInstanceState: Bundle?) {
    	super.onCreate(savedInstanceState)
    	setContentView(R.layout.activity_main)
    	initAidlService()
    	btn_click.setOnClickListener {
        	mAidl?.addPerson(Person("测试" + Random().nextInt(5)))
        	val personList = mAidl?.personList
        	tv_aidl.setText(personList.toString())
    	}

	}

	private fun initAidlService() {
    	val intent = Intent()
    	intent.action = "testapplication.service.MyAidlService"
    	intent.`package` = "com.example.com.testapplication"
    	bindService(intent, MyServiceConnection(), Context.BIND_AUTO_CREATE)
	}

	inner private class MyServiceConnection : ServiceConnection {
    	override fun onServiceDisconnected(name: ComponentName?) {
        	mAidl = null
    	}

    	override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        	mAidl = IMyAidl.Stub.asInterface(service)
    	}
	}
}

至此我们的客户端也开发完了。整体流程走下李,我们知道kotlin语言的aidl开发比java语言的aidl开发多了一个更改生成文件的步骤,其他的都是一样的,而且就代码量来说个人觉得kotlin是真棒。

坑:

  1. aidl的编写上很容易遇到问题,要注意引入的类路径和类名
  2. aidl是基于binder内核进行的,也是CS结构的,如果服务端没有启动那么就像网络访问没有启动服务器一样是没有办法进行通信的。

致敬源码

Android AIDLAndroid Interface Definition Language)是一种用于定义客户端和服务之间通信接口的语言。AIDL 是一个 Android 特有的 RPC(远程过程调用)机制。 下面是使用 AIDL 的基本步骤: 1. 定义 AIDL 接口 在服务端创建一个 AIDL 文件,定义服务的接口方法。例如,创建一个名为 IMyService.aidl 的文件,其中包含以下内容: ``` interface IMyService { void sayHello(); } ``` 2. 实现 AIDL 接口 在服务端实现 AIDL 接口,例如: ``` public class MyService extends Service { private final IMyService.Stub binder = new IMyService.Stub() { @Override public void sayHello() throws RemoteException { Log.i("MyService", "Hello World"); } }; @Nullable @Override public IBinder onBind(Intent intent) { return binder; } } ``` 3. 绑定服务 在客户端绑定服务,例如: ``` private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { IMyService myService = IMyService.Stub.asInterface(service); try { myService.sayHello(); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; private void bindService() { Intent intent = new Intent(this, MyService.class); bindService(intent, connection, Context.BIND_AUTO_CREATE); } ``` 通过上述步骤,就可以实现客户端与服务端之间的通信。需要注意的是,AIDL 接口中定义的方法必须是可序列化的。如果方法参数或返回值类型不支持序列化,可以通过 Parcelable 接口实现序列化。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值