平常我们开发的过程中,涉及到aidl的应用特别少,但是他却是Android开发进阶中必须要掌握的一项知识点,下面我就来简单的介绍一下aidl
1 为什么要引入AIDL?
我们知道,每一个app都拥有一个独立的进程,那么如果在一个app里需要访问另外一个app的数据该怎么办呢?那么就要涉及到跨进程通信了,而Android是通过AIDL来实现跨进程通信的。
2 那么什么是AIDL?
AIDL:Android Interface Definition Language,即Android接口定义语言;用于让某个Service与多个应用程序组件之间进行跨进程通信,从而可以实现多个应用程序共享同一个Service的功能。由此可见,AIDL是为了解决跨进程之间的通讯问题。
3 AIDL的运用场景:在多进程通信中,存在两个进程角色(以最简单的为例):服务器端和客户端,那么既然说到服务端,我们可以反思一下Service的基本知识点,如本地服务、远程服务等,其二者最大的区别是:远程Service与调用者不在同一个进程里(即远程Service是运行在另外一个进程);而本地服务则是与调用者运行在同一个进程里
那么具体的步骤是什么样的呢?就以我写的demo为例子(客户端如何访问远程服务端的例子),先以服务端为例,代码结构如下:
其截图如下:
代码片段如下:
// IMyAidlInterface.aidl
package com.example.lbs06.aidlserver;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
void callfromClient();
}
//AIDL中支持以下的数据类型
//1. 基本数据类型
//2. String 和CharSequence
//3. List 和 Map ,List和Map 对象的元素必须是AIDL支持的数据类型;
//4. AIDL自动生成的接口(需要导入-import)
//5. 实现android.os.Parcelable 接口的类(需要导入-import)
Service里的代码片段如下:
/**
* Created by lbs06 on 2017/10/24.
*/
public class MyService extends Service{
public String TAG="MyService";
// 实例化AIDL的Stub类(Binder的子类)
IMyAidlInterface.Stub mbinder=new IMyAidlInterface.Stub() {
//重写接口里定义的方法
@Override
public void callfromClient() throws RemoteException {
Log.d(TAG,"客户端调用了服务端的callfromClient方法");
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind方法被调用");
return mbinder;
}
}
清单文件里的内容如下所示:
<service
android:name=".MyService"
android:process=":remote"
android:exported="true"
>
//android:process=":remote" 设置服务为远程服务,拥有一个独立的进程
//android:exported="true" 设置服务可以被其他进程访问
//MyService可以响应带有com.lbs.aidl.server.IMyAidlInterface这个action的Intent。
<intent-filter>
<action android:name="com.example.lbs06.aidlserver.IMyAidlInterface"/>
</intent-filter>
</service>
客户端的项目的结构如下:
xml文件很简单:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<TextView
android:id="@+id/tv_bind_service"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="#e3e3e3"
android:gravity="center"
android:text="绑定服务"
/>
</LinearLayout>
那么在activity里面的代码块是这样的,如下图:
public class MainActivity extends AppCompatActivity {
public String TAG="客户端的进程";
//定义aidl接口变量
private IMyAidlInterface myinterface;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.tv_bind_service).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//通过Intent指定服务端的服务名称和所在包,与远程Service进行绑定
//参数与服务器端的action要一致,即"服务器包名.aidl接口文件名"
Intent intent=new Intent("com.example.lbs06.aidlserver.IMyAidlInterface");
//Android5.0后无法只通过隐式Intent绑定远程Service
//需要通过setPackage()方法指定包名
intent.setPackage("com.example.lbs06.aidlserver");
//绑定服务,传入intent和ServiceConnection对象
bindService(intent,connection, Context.BIND_AUTO_CREATE);
}
});
}
//创建ServiceConnection的匿名类
ServiceConnection connection=new ServiceConnection() {
//重写onServiceConnected()方法和onServiceDisconnected()方法
//在Activity与Service建立关联和解除关联的时候调用
//在Activity与Service建立关联时调用
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//IMyAidlInterface.Stub.asInterface()方法将传入的IBinder对象传换成了mAIDL_Service对象
myinterface=IMyAidlInterface.Stub.asInterface(iBinder);
try {
//通过该对象调用在IMyAidlInterface.aidl文件中定义的接口方法,从而实现跨进程通信
myinterface.callfromClient();
} catch (RemoteException e) {
e.printStackTrace();
}
}
//在Activity与Service解除关联时调用
@Override
public void onServiceDisconnected(ComponentName componentName) {
}
};
分别运行客户端和服务端的代码,点击客户端的按钮,会发现log日志如下:
10-25 08:55:06.472 19892-19892/com.example.lbs06.aidlserver:remote D/MyService: onBind方法被调用
10-25 08:55:06.475 19892-19905/com.example.lbs06.aidlserver:remote D/MyService: 客户端调用了服务端的callfromClient方法
在此,客户端向服务端通信成功。
不过在此,曾遇到过两个坑,第一,在代码中,当新建aidl文件夹后,创建相应包下的aidl后,无法引用IMyAidlInterface的问题,如下截图所示:
就因为这个问题后困扰了好久,后来通过在网上查找资料,发现clean一下项目即可。
还有一个问题,看下截图:
最开始我把1和2处的包名定义成不一样的,也不能通信成功,后来改成一样的,通信成功,也不知道是什么问题所导致的,还请有知道的告知下,谢谢。最后,放上源码的地址,有需要的可以去下载:http://download.csdn.net/download/u010667468/10038464