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<String>()
//添加数据
override fun getData(): MutableList<String> {
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()
}
}