提起进程间的通信我们就会想到Aidl,今天就个人理解的Aidl进行阐述,如有不当之处还望大家多多指教。好了,不说废话了,下面我们就进入今天的正题——Aidl,进程间的通信大使。
AIDL是一个缩写,全称是Android Interface Definition Language,也就是Android接口定义语言。是的,首先我们知道的第一点就是:AIDL是一种语言。既然是一种语言,那么相应的就很自然的衍生出了一些问题:
为什么要设计出这么一门语言?
它有哪些语法?
我们应该如何使用它?
下面我们就这些问题进行分开描述:
为什么要设计出这么一门语言?
设计这门语言的目的是为了实现进程间通信。
每一个进程都有自己的Dalvik VM实例,都有自己的一块独立的内存,都在自己的内存上存储自己的数据,执行着自己的操作,都在自己的那片狭小的空间里过完自己的一生。每个进程之间都你不知我,我不知你,就像是隔江相望的两座小岛一样,都在同一个世界里,但又各自有着自己的世界。而AIDL,就是两座小岛之间沟通的桥梁。相对于它们而言,我们就好像造物主一样,我们可以通过AIDL来制定一些规则,规定它们能进行哪些交流——比如,它们可以在我们制定的规则下传输一些特定规格的数据。
总之,通过这门语言,我们可以愉快的在一个进程访问另一个进程的数据,甚至调用它的一些方法
它有哪些语法?
其实AIDL这门语言非常的简单,基本上它的语法和 Java 是一样的,只是在一些细微处有些许差别——毕竟它只是被创造出来简化Android程序员工作的,太复杂不好——所以在这里我就着重的说一下它和 Java 不一样的地方。主要有下面这些点:
文件类型:用AIDL书写的文件的后缀是 .aidl,而不是 .java。
数据类型: Java基本类型,String,List,Map,CharSequence
我们如何使用它?
IBinder是远程对象的基本接口,是为了高性能而设计的轻量级远程调用机制的核心部分。但他 不仅用于远程调用,也用于进程内调用。该接口定义了与远程对象间交互的协议。但不要直接实现 这个接口,而是继承(extends)Binder。
IBinder是Android给我们提供的一个进程间通信的一个接口,而我们一般是不直接实现这个接口的, 而是通过继承Binder类来实现进程间通信!是Android中实现IPC(进程间通信)的一种方式!
Binder机制给我们带来的最直接的好处就是: 我们无需关心底层如何实现,只需按照AIDL的规则,自定义一个接口文件, 然后调用调用接口中的方法,就可以完成两个进程间的通信了!
到这里相信大家已经对Aidl有了一定的了解,那么此时,问题又来了。什么是IPC啊?它又是个什么鬼?别急别急,容我歇息一番后做出解答。
IPC这个名词,他的全名叫做:跨进程通信(interprocess communication), 因为在Android系统中,个个应用程序都运行在自己的进程中,进程之间一般是无法直接进行数据交换的, 而为了实现跨进程,Android给我们提供了上面说的Binder机制,而这个机制使用的接口语言就是: AIDL(Android Interface Definition Language)
这种接口语言并非真正的编程 语言,只是定义两个进程间的通信接口而已!而生成符合通信协议的Java代码则是由Android SDK的 platform-tools目录下的aidl.exe工具生成,生成对应的接口文件在:gen目录下,一般是:Xxx.java的接口! 而在该接口中包含一个Stub的内部类,该类中实现了在该类中实现了IBinder接口与自定义的通信接口, 这个类将会作为远程Service的回调类——实现了IBinder接口,所以可作为Service的onBind( )方法的返回值!
在开始编写AIDL接口文件前,我们需要了解下编写AIDL的一些注意事项:
AIDL注意事项:
接口名称需要与aidl文件名相同
接口和方法前面不要加访问权限修饰符:public ,private,protected等,也不能用static final!
AIDL默认支持的类型包括Java基本类型,String,List,Map,CharSequence,除此之外的其他类型都 需要import声明,对于使用自定义类型作为参数或者返回值,自定义类型需要实现Parcelable接口
说了这么多,接下来当然就是贴代码了,但是,再贴代码之前,还是先给出步骤:
服务端步骤如下:
Step 1:创建AIDL文件:
Step 2:自定义我们的Service类,完成下述操作:
1)继承Service类,同时也自定义了一个类用来继承MyServiceAIDL.Stub类 就是实现了MyServiceAIDL接口和IBinder接口
2)实例化自定义的Stub类,并重写Service的onBind方法,返回一个binder对象!
Step 3:在AndroidManifest.xml文件中注册Service
客户端步骤如下:
直接把服务端的那个aidl文件复制过来(包名要一致)
1)创建 ServiceConnection接口 实例
2) 调用bindService绑定远程Service bindService(service,conn,BIND_AUTO_CREATE);
3)和本地Service不同,绑定远程Service的ServiceConnection并不能直接获取Service的onBind( )方法 返回的IBinder对象,只能返回onBind( )方法所返回的代理对象,需要做如下处理: binder= MyServiceAIDL.Stub.asInterface(service); 再接着完成初始化,以及按钮事件等就可以了
下面给出代码:
首先是aidl文件:它的后缀名一定要是.aidl
package us.mifeng;
interface Authority{
String getName(int num);
}
服务端service:
public class MyService extends Service{
String name[]={"11","22","33","44","55"};
@Override
public void onCreate() {
super.onCreate();
}
@Override
public IBinder onBind(Intent intent) {
return new MyBinder();
}
class MyBinder extends Stub{
@Override
public String getName(int num) throws RemoteException {
int i = num%5;
return name[i];
}
}
}
清单文件中的代码如下:
<service android:name="cn.lzl.aidldemoa.MyService">
<intent-filter >
<action android:name="cn.bgs.aidldemo"/>
</intent-filter>
</service>
然后就是客户端的代码了。在这之前一定要先把服务端的aidl文件copy过来。一定一定。。。
public class MainActivity extends Activity {
private Button mBtn;
private EditText mEd;
private TextView mTv;
private Authority binder;
private ServiceConnection conn=new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e("", "===================");
binder = Authority.Stub.asInterface(service);
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mBtn=(Button) findViewById(R.id.mBtn);
mEd=(EditText) findViewById(R.id.mEd);
mTv=(TextView) findViewById(R.id.mTv);
Intent intent =new Intent();
intent.setAction("cn.bgs.aidldemo");
bindService(intent, conn, Service.BIND_AUTO_CREATE);
mBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
int result=Integer.valueOf(mEd.getText().toString());
try {
String data=binder.getName(result);
Log.e("", "================="+data);
mTv.setText(data);
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
}
}
忘记说一件事情,前几天在给学生讲这个代码的时候,遇到了一个小小的bug,那就是空指针异常,在我查阅资料后发现是因为
在android 5.0之后不能采用隐式跳转,需要在intent.setAction();方法之后,加一行代码。
即intent.setPackage(“服务端MyService的包名”);
然后,方便以后有遇到这个问题的新手查阅,特写此博客。(允许我小小的装一下逼(●’◡’●))。