android之AIDL实例详解
前面我们将了本地service的基本用法,今天来介绍的就是远程服务,就是Service端和Client端分别在不同的进程。
这里就不得不提到AIDL了。
什么是AIDL:
大家可以看一下官方文档的定义,简单来说AIDL就是Android系统提供的一套帮助开发者快速实现binder的工具。而什么是binder呢?binder是Android系统实现进程间通讯的机制。这个再再再后面讲……
使用AIDL:
既然是C/S模型那么为了方便演示,这里我们就直接创建2个mode,一个aidlService(服务端),一个aidlClient(客户端)。
aidlService端:
创建aidl文件:直接在项目main目录右键-new-aidl,然后输入文件名称即可。
系统会默认新建一个和Java目录平级的aidl目录,下面是和我们报名相同的路径,然后是我们创建的aidl文件。
先来编写接口:
package com.example.aidlService;import com.example.aidlService.bean.BookBean;interface IMyAidlInterface { String getString(); BookBean getBook();}
可以看到创建的aidl文件是一个接口,我们定义了2个方法,一个getString返回一个String,getBook返回我们自定义的实体类。这里讲一下aidl支持的数据类型:
除了基本数据类型外还有String、CharSequence、List、map、实现Parcelable的类。其中Parcelable接口类需要自己写import导入,就像上面的import com.example.aidlService.bean.BookBean;类,就是为了导入BookBean。
Parcelable接口是Android提供的可序列化的接口,简单看一下BookBean的实现:
class BookBean() : Parcelable { var name = "" var price = 0.0f constructor(name: String, price: Float) : this() { this.name = name this.price = price } constructor(parcel: Parcel) : this() { name = parcel.readString().toString() price = parcel.readFloat() } override fun writeToParcel(dest: Parcel?, flags: Int) { dest?.let { it.writeString(name) it.writeFloat(price) } } override fun describeContents(): Int = 0 override fun toString(): String { return "BookBean(name='$name', price=$price)" } companion object CREATOR : Parcelable.Creator { override fun createFromParcel(parcel: Parcel): BookBean { return BookBean(parcel) } override fun newArray(size: Int): Array { return arrayOf() } }}
Parcelable接口主要需要我们实现一个带Parcel参数的构造方法和writeToParcel方法,以及Creator接口,Creator接口主要是用来创建我们的BookBean实例的,在writeToParcel方法中我们需要将需要保存的属性通过Parcel的writexxx方法写入,然后在带有Parcel参数的构造方法中农使用readxxx读取出来。需要注意的是两边写入和读取的顺序要相同。
编写完aidl接口后我们需要让android studio同步一下代码,让它自动生成aidl文件对应的java文件。可以点击Build菜单的make mode来重新编译整个项目。
编译成功之后会在build目录下生成一个aidl同名的Java接口文件
里面的内容暂时先不用管,我们后面再说。先看怎么使用这个接口。
接下来我们就需要创建Service了
创建 一个AIDLService文件,这就是我们的Service,还记得上一章四大组件之Service(1)讲到的Service的启动方式和对应的生命周期函数吗?因为是要给客户端提供接口的,所以客户端必须使用bindService方法来启动我们的service,所以我们这里的Service只需要实现onBind并且返回Ibinder的接口实现类。
看看代码:
class AIDLService : Service() { override fun onBind(intent: Intent): IBinder { return myAidlInterfaceBinder } private val myAidlInterfaceBinder = object : IMyAidlInterface.Stub() { override fun getBook(): BookBean { Log.d("ZLog AIDLService", "getBook: ") return BookBean("android 从入门到放弃", 0.0f) } override fun getString(): String { Log.d("ZLog AIDLService", "getString: ") return "这是服务端返回的String" } }}
重点是我们在onBind中返回的是什么?我们这里的myAidlInterfaceBinde是一个直接继承ImyAidlInterface.Stub的类。IMyAIDLInterface是我们自己创建的aidl接口,那Stub是哪来的呢?并且还包含了我们定义的2个接口方法getString和getBook。回到我们刚刚创建好ImyAidlInterface.aidl后重新编译了项目生成的ImyAidlInterface.java接口,这个时候再来看看它里面的内容:
public interface IMyAidlInterface extends android.os.IInterface { /** * Default implementation for IMyAidlInterface. */ public static class Default implements com.example.aidlService.IMyAidlInterface { @Override public java.lang.String getString() throws android.os.RemoteException { return null; } @Override public com.example.aidlService.bean.BookBean getBook() throws android.os.RemoteException { return null; } @Override public android.os.IBinder asBinder() { return null; } } /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.aidlService.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "com.example.aidlService.IMyAidlInterface"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.aidlService.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.example.aidlService.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.aidlService.IMyAidlInterface))) { return ((com.example.aidlService.IMyAidlInterface) iin); } return new com.example.aidlService.IMyAidlInterface.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { java.lang.String descriptor = DESCRIPTOR; switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(descriptor); return true; } case TRANSACTION_getString: { data.enforceInterface(descriptor); java.lang.String _result = this.getString(); reply.writeNoException(); reply.writeString(_result); return true; } case TRANSACTION_getBook: { data.enforceInterface(descriptor); com.example.aidlService.bean.BookBean _result = this.getBook(); reply.writeNoException(); if ((_result != null)) { reply.writeInt(1); _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; } default: { return super.onTransact(code, data, reply, flags); } } } private static class Proxy implements com.example.aidlService.IMyAidlInterface { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public java.lang.String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public java.lang.String getString() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { _data.writeInterfaceToken(DESCRIPTOR); boolean _status = mRemote.transact(Stub.TRANSACTION_getString, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { return getDefaultImpl().getString(); } _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public com.example.aidlService.bean.BookBean getBook() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); com.example.aidlService.bean.BookBean _result; try { _data.writeInterfaceToken(DESCRIPTOR); boolean _status = mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { return getDefaultImpl().getBook(); } _reply.readException(); if ((0 != _reply.readInt())) { _result = com.example.aidlService.bean.BookBean.CREATOR.createFromParcel(_reply); } else { _result = null; } } finally { _reply.recycle(); _data.recycle(); } return _result; } public static com.example.aidlService.IMyAidlInterface sDefaultImpl; } static final int TRANSACTION_getString = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); public static boolean setDefaultImpl(com.example.aidlService.IMyAidlInterface impl) { if (Stub.Proxy.sDefaultImpl == null && impl != null) { Stub.Proxy.sDefaultImpl = impl; return true; } return false; } public static com.example.aidlService.IMyAidlInterface getDefaultImpl() { return Stub.Proxy.sDefaultImpl; } } public java.lang.String getString() throws android.os.RemoteException; public com.example.aidlService.bean.BookBean getBook() throws android.os.RemoteException;}
内容很多,我们直接看里面的Stub类,它继承了我们上一章说的Binder类,并且实现了我们定义的ImyAidlInterface接口,所以我们可以在onBind中返回它。
其中有一个特殊的方法 asInterface()它接收一个Ibinder参数,返回的是我们定义的接口。看到这里大概有的朋友也知道了,这个方法就是客户端拿到我们定义的接口的方法。
好了,我们接着往下,接着我们需要配置一下service,在manifest.xml中我们给service增加一个action
我们添加了一个com.example.aidlService.Service的action,并且配置了service 的process属性,表示我们这个service是以一个独立的进程运行的。当然,这个配置不是必须的。
至此Service端的工作就做完了,接下来开始Client端。
首先要做的第一件事就是我们需要把service端所有的aidl都拷贝到client来,并且如果有传递了自定义的数据类也要拷贝过来,保证包名相同!保证包名相同!保证包名相同!
这样client端才能正常使用相关内容
然后就是在客户端启动service:
private lateinit var aidlServiceIntent: Intent private lateinit var aidlInterface: IMyAidlInterface override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) aidlServiceIntent = Intent() aidlServiceIntent.action = "com.example.aidlService.Service" aidlServiceIntent.component = ComponentName("com.example.aidlService", "com.example.aidlService.AIDLService") bindService(aidlServiceIntent, conn, Context.BIND_AUTO_CREATE) acMainBtGetData.setOnClickListener { if (this::aidlInterface.isInitialized) { acMainTvMsg.text = aidlInterface.book.toString()Log.d("ZLog MainActivity", "调用服务端getBook: ${aidlInterface.book}") Log.d("ZLog MainActivity", "调用服务端getString: ${aidlInterface.book}") } else { acMainTvMsg.text = "绑定失败" } } } private val conn = object : ServiceConnection { override fun onServiceDisconnected(name: ComponentName?) { } override fun onServiceConnected(name: ComponentName?, service: IBinder?) { Log.d("ZLog MainActivity", "onServiceConnected: $service") aidlInterface = IMyAidlInterface.Stub.asInterface(service) }}
这里bindService的intent跟上一章将的不太一样,因为这里我们是没有Service的,只能通过action和包名来告诉系统我们要注册的service在哪。Android 5.0之后不允许隐式启动service,所以我们这里首先设置了intent的action,就是我们在服务端的manifest中添加的action。下面设置了ComponentName,第一个参数是服务端的包名,第二参数是服务端service的完整类名。这里有一个问题,因为这里的C、S都是我们自己写的,所以是知道包名这些参数的,如果是第三方或者其他人写的,而且只提供了action怎么办呢?推荐一个下面的方法来获取:
private fun createExplicitFromImplicitIntent(implicitIntent: Intent): Intent? { val resolveInfo = packageManager.queryIntentServices(implicitIntent, 0) if (resolveInfo.size != 1) { return null } val serviceInfo = resolveInfo[0] val packageName = serviceInfo.serviceInfo.packageName val className = serviceInfo.serviceInfo.name val component = ComponentName(packageName, className) val explicitIntent = Intent(implicitIntent) explicitIntent.component = component return explicitIntent }
这个方法的参数Intent只需要设置action即可,然后通过packageManager获取一个ResolveInfo列表,正常情况这个列表里面符合条件的就一项,然后通过ResolveInfo就可以拿到包名和类名了。
然后我们看绑定的ServiceConnection方法,在onServiceConnected方法中我们使用之前讲的IMyAidlInterface.Stub.asInterface方法就能获取IMyAIDLInterface接口了。
在button的点击事件中我们来调用接口的相关方法。
这里有一点要注意的是我们需要先启动service端的apk,在启动client端的apk。
好了,到这里aidl的基本用法就讲完了。
更多相关文章
- Android网络编程之通过Get方法实现
- android控件的监听绑定方法
- 全面的Android文件目录解析和获取方法(包含对6.0系统的说明)
- Android官方的文档中提到了模拟器中设置代理服务器的方法,即在命
- 如何向Android的framework里添加新类 &&& android修改开放类方法
- Android swap分区作用及swapper软件设置方法
- android Matrix处理图片原理及方法整理