Android Service AIDL
Android 中的各个应用程序都运行在自己的进程中,进程之间一般是无法直接进行数据交换的
而为了实现跨进程,Android 提供了上一章节提到的 Binder
机制,而这个机制使用的接口语言就是: AIDL (Android Interface Definition Language)
AIDL 语法很简单,只是定义两个进程间的通信接口而已
而生成符合通信协议的 Java
代码则是由 Android SDK
的 platform-tools/aidl.exe
工具生成
生成对应的接口文件在: gen
目录下,一般是 Xxx.java
的接口
在该接口中包含一个 Stub
的内部类,该类中实现了在该类中实现了 IBinder
接口与自定义的通信接口
Sub
类会作为远程 Service
的回调类——实现了 IBinder
接口,所以可作为 Service
的 onBind()
方法的返回值
AIDL 接口
开始编写 AIDL
接口文件前,我们先了解一些注意事项
-
接口名需要与
aidl
文件名相同 -
接口和方法前面 不要加访问权限修饰符 public ,private,protected 等,也不能用
static final
-
AIDL
默认支持的类型包括 Java基本类型 , String , List , Map , CharSequence ,除此之外的其它类型都 需要import
声明,对于使用自定义类型作为参数或者返回值, 自定义类型需要实现Parcelable
接口 -
自定义类型和
AIDL
生成的其它接口类型在aidl
描述文件中,应该显式import
,即便在该类和定义的包在同一个包中
Android Studio 创建
-
Android Studio 创建
AIDL
需要在main
目录下新建一个aidl
文件夹在
app
目录上点右键,然后选择New->Folder->AIDL FOLDER
-
接着定义一个和
aidl
包名相同的包 -
然后创建一个
aidl
文件在包名上点右键,然后选择
New->AIDL 文件
-
最后点击
Build->Make Project
编译
AIDL 实现两个进程间的简单通信
先来看看最后的效果图
创建服务端
-
创建一个 空的 Android 项目
cn.twle.android.AIDLServer
-
在
app
目录下创建aidl
目录 -
在
aidl
目录下新建包cn.twle.android.aidl
-
在
cn.twle.android.aidl
包下新建文件ILanguage.aidl
package cn.twle.android.aidl; interface ILanguage { String queryLanguage(int num); }
-
点击
Build->Make Project
编译代码打开
ILanguage.java
可以看到里面的代码如下/* * This file is auto-generated. DO NOT MODIFY. * Original file: /Android/AIDLServer/app/src/main/aidl/cn/twle/android/aidl/ILanguage.aidl */ package cn.twle.android.aidl; public interface ILanguage extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements cn.twle.android.aidl.ILanguage { private static final java.lang.String DESCRIPTOR = "cn.twle.android.aidl.ILanguage"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an cn.twle.android.aidl.ILanguage interface, * generating a proxy if needed. */ public static cn.twle.android.aidl.ILanguage asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof cn.twle.android.aidl.ILanguage))) { return ((cn.twle.android.aidl.ILanguage)iin); } return new cn.twle.android.aidl.ILanguage.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 { switch (code) { case INTERFACE_TRANSACTION: { reply.writeString(DESCRIPTOR); return true; } case TRANSACTION_queryLanguage: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); java.lang.String _result = this.queryLanguage(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements cn.twle.android.aidl.ILanguage { 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 queryLanguage(int num) 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); _data.writeInt(num); mRemote.transact(Stub.TRANSACTION_queryLanguage, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_queryLanguage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public java.lang.String queryLanguage(int num) throws android.os.RemoteException; }
我们只要关注
asInterface(IBinder)
和我们定义的接口中的queryLanguage()
方法该方法会把
IBinder
类型的对象转换成ILanguage
类型的,必要时生成一个代理对象返回结果 -
在
MainActivity.java
同一目录下新建文件MsLanguageService.java
继承 Service 类,同时也自定义了一个
LanguageQueryBinder
类用来继承ILanguage.Stub
类也就是实现了
IPerson
接口和IBinder
接口然后实例化自定义的
Stub
类,并重写Service
的onBind()
方法,返回一个 binder 对象package cn.twle.android.aidlserver; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import cn.twle.android.aidl.ILanguage.Stub; public class MsLanguageService extends Service { private IBinder binder = new LanguageQueryBinder(); private String[] names = {"python","php","java","kotin","c","swift"}; private String query(int num) { if(num > 0 && num < 6){ return names[num - 1]; } return null; } @Override public IBinder onBind(Intent intent) { return binder; } private final class LanguageQueryBinder extends Stub{ @Override public String queryLanguage(int num) throws RemoteException { return query(num); } } }
-
然后修改
AndroidManifest.xml
文件中注册Service
<service android:name=".MsLanguageService"> <intent-filter> <action android:name="cn.twle.android.aidlserver.MsLanguageService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
这里并没有提供 Activity
界面,但是改应用提供的 Service
可以供其它 app 来调用
客户端
-
创建一个 空的 Android 项目
cn.twle.android.AIDLClient
-
重复上面的 [2-5] 步骤创建
ILanguage.java
-
先编译一下
-
修改
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="请输入要查询的语言" /> <EditText android:id="@+id/ipt_search" android:inputType="number" android:layout_width="match_parent" android:layout_height="wrap_content"/> <Button android:id="@+id/btn_query" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="查询"/> <TextView android:id="@+id/txt_show" android:layout_width="wrap_content" android:layout_height="wrap_content"/> </LinearLayout>
-
修改
MainActivity.java
-
自定义
LanguageConnection
类实现ServiceConnection
接口 -
以
LanguageConnection
对象作为参数,调用bindService()
绑定远程 ServicebindService(service,conn,BIND_AUTO_CREATE); 第三个参数是设置如果服务没有启动的话,自动创建
-
和本地Service不同,绑定远程 Service 的
ServiceConnection
并不能直接获取 Service 的onBind()
方法返回IBinder
对象,只能返回onBind()
方法所返回的 代理对象需要做如下处理
iLanguage = ILanguage.Stub.asInterface(service)
再接着完成初始化
MainActivity.java
package cn.twle.android.aidlclient; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import cn.twle.android.aidl.ILanguage; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private EditText ipt_search; private Button btn_query; private TextView txt_show; private ILanguage iLanguage; private LanguageConnection conn = new LanguageConnection(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindViews(); //绑定远程Service Intent service = new Intent("cn.twle.android.aidlserver.MsLanguageService"); service.setPackage("cn.twle.android.aidlserver"); bindService(service, conn, BIND_AUTO_CREATE); btn_query.setOnClickListener(this); } private void bindViews() { ipt_search = (EditText) findViewById(R.id.ipt_search); btn_query = (Button) findViewById(R.id.btn_query); txt_show = (TextView) findViewById(R.id.txt_show); } @Override public void onClick(View v) { String number = ipt_search.getText().toString(); int num = Integer.valueOf(number); try { txt_show.setText(iLanguage.queryLanguage(num)); } catch (RemoteException e) { e.printStackTrace(); } ipt_search.setText(""); } private final class LanguageConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { iLanguage = ILanguage.Stub.asInterface(service); } public void onServiceDisconnected(ComponentName name) { iLanguage = null; } } }
-
接下来先启动 AIDLServivce
,然后再启动 AIDLClient
当然也可以直接启动 AIDLClient
,也会获得同样效果