使用方法工作原理初涉AndroidAIDLIPC(工作原理使用方法初涉客户端服务)

今天来讲讲AIDL,这个神秘的AIDL,也是最近在学习的,看了某课大神的讲解写下的blog,希望结合自己的看法给各位同价通俗易懂的讲解官方文档:http://developer.android.com/guide/components/aidl.html一.What is AIDL?(什么是AIDL)AIDL:Android Interface Definition Language (Android接口定义语言)首先,我们要知道,进程1和进程2,我们要如何让他通讯?在Android系统的虚拟机中,每一个app运行都是在一个独立的沙箱里面的,这样的话,一个应用崩溃也不会影响到其他应用,这样我们就提出了一个问题,跨进程如如何进行通讯?如何传递数据?其实两个进程是不能直接通讯的,他需要Android系统底层的帮助来进行通讯。

那就是我们每个人都知道的四大组件我们首先还是先进Google的API看看他大概的意思是他允许你去定义一个自己的标准,使用的是IPC(进程间通讯)进制,跨进程通讯,有兴趣的可以去翻译一下,不过值得注意的是他的第二段标记这里提到了两个东西- Binder- Messenger我们继续往下看话就知道- Binder - Messenger 翻译:如果您不需要执行并发IPC在不同的应用程序中 你就用Binder ,或者如果你想执行IPC,但不需要处理多线程,实现你的接口Messenger,无论如何,确保你了解实现AIDL之前绑定服务。
所以我们就能理清楚AIDL的概念了 AIDL //IPC 多应用 多线程二.Defining an AIDL Interface(语法)文档中强调。
你必须定义一个.aidl的文件,那我们怎样去创建尼?1. Create the .aidl file(创建一个.aidl文件)官方提供的语法// IRemoteService.aidlpackage 包名(com.lgl.android);// Declare any non-default types here with import statements/ Example service interface 接口/interface IRemoteService { / Request the process ID of this service, to do evil things with it. 方法/ int getPid(); / Demonstrates some basic types that you can use as parameters and return values in AIDL. / void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);}我们来做一个简单的演示我们要使用的编译工具在我们的SDK/buidl-tools/android版本/aidl.bat,不过实际开发中也不需要手动编译,我们新建一个项目ForAIDL,这里我们使用的开发工具是Android Studio,其实Eclipse可能更加让人熟悉,不过用AS也是大势所趋了,而且AS的目录结构我也很喜欢我们直接java-new - folder - AIDL folder然后你就会发现多了一个aidl的文件夹我们继续新建一个包名,新建一个文件文件new - AIDL - file大致的内容// IMyAidlInterface.aidlpackage com.lgl.foraidl;// Declare any non-default types here with import statementsinterface IMyAidlInterface { / Demonstrates some basic types that you can use as parameters and return values in AIDL. / void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);}AS默认是不会去重新构建Gradle,我们点击一下构建按钮然后我们就可以在MainActivity中调用了三.AIDL Client And Service(客户端和服务端)现在就好玩了,我们先来理理思路,一般是这样的,我们一个软件有某个功能,也就是服务端,然后客户端通过AIDL去访问这里可以看到服务端只是处理一些操作,我们客户端去请求,这样的话,那我创建一个AIDLService,同样的,我们需要去创建AIDL文件,命名-ServiceAidlInterfaceServiceAidlInterface// ServiceAidlInterface.aidlpackage com.lgl.aidlservice;// Declare any non-default types here with import statementsinterface ServiceAidlInterface { / 计算两数之和 / int add(int num1,int num2);}现在我们处理的就不是默认的东西了,AIDL的原理就是你自定义语言接口,对的,我们也来2. Implement the interface(实现一个AIDL)根据我们的Google文档,第一步Create the .aidl file已经完成了,现在就来进行第二步了,我们这里需要使用到Service,看文档这样的话我们新建一个IRemoteService继承service,我们实现计算的逻辑处理package com.lgl.aidlservice;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import android.util.Log;/ 监听服务 Created by lgl on 16/3/13. /public class IRemoteService extends Service{ / 当客户端绑定到该服务的时候启动onBind @param intent @return / @Override public IBinder onBind(Intent intent) { //绑定之后就计算 return iBinder; } / 开始处理结果 / private IBinder iBinder = new ServiceAidlInterface.Stub(){ @Override public int add(int num1, int num2) throws RemoteException { Log.i(\"AIDL\",\"收到了请求,參數1\"+num1+\"參數2\"+num2); return num1+num2; } };}3. Expose the interface to clients(客户端的实现)OK,写完了服务端就可以写客户端了,我们直接new一个Module-AIDLClients我們先按剛才的逻辑,把界面写了<?xml version=\"1.0\" encoding=\"utf-8\"?><LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\" android:layout_width=\"match_parent\" android:layout_height=\"match_parent\" android:orientation=\"vertical\" android:gravity=\"center\" android:padding=\"15dp\"> <TextView android:text=\"AIDL\" android:textSize=\"50sp\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" /> <EditText android:hint=\"请输入数字1\" android:id=\"@+id/et_num1\" android:layout_width=\"match_parent\" android:layout_height=\"wrap_content\" /> <TextView android:layout_marginBottom=\"15dp\" android:layout_marginTop=\"15dp\" android:textSize=\"20sp\" android:text=\"+\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" /> <EditText android:hint=\"请输入数字2\" android:id=\"@+id/et_num2\" android:layout_width=\"match_parent\" android:layout_height=\"wrap_content\" /> <TextView android:layout_marginBottom=\"15dp\" android:layout_marginTop=\"15dp\" android:textSize=\"20sp\" android:text=\"=\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" /> <TextView android:id=\"@+id/tv_result\" android:text=\"结果:\" android:layout_width=\"wrap_content\" android:layout_height=\"wrap_content\" /> <Button android:textColor=\"#fff\" android:background=\"@android:color/holo_blue_light\" android:layout_marginTop=\"30dp\" android:id=\"@+id/btn_ok\" android:text=\"计算\" android:layout_width=\"match_parent\" android:layout_height=\"wrap_content\" /></LinearLayout>我们要想客户端调用服务端的内容,那么就一定要定义标准的语言,所以客户端的aidl和服务端必须一致记住,每次不通过的时候先编译一遍MainActivity(客户端)package com.lgl.aidlclients;import android.content.ComponentName;import android.content.Context;import android.content.Intent;import android.content.ServiceConnection;import android.os.IBinder;import android.os.RemoteException;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import com.lgl.aidlservice.ServiceAidlInterface;/ AIDL 客户端 @author lgl /public class MainActivity extends AppCompatActivity implements View.OnClickListener { //输入 private EditText et_num1,et_num2; //结果 private TextView tv_result; //AIDL远程访问 private Button btn_ok; private ServiceAidlInterface aidl; private ServiceConnection conn = new ServiceConnection() { //连接上 @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { //拿到远程服务 aidl = ServiceAidlInterface.Stub.asInterface(iBinder); } //断开时 @Override public void onServiceDisconnected(ComponentName componentName) { //回收 aidl = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); bindServices(); } private void initView() { et_num1 = (EditText) findViewById(R.id.et_num1); et_num2 = (EditText) findViewById(R.id.et_num2); tv_result = (TextView) findViewById(R.id.tv_result); btn_ok = (Button) findViewById(R.id.btn_ok); btn_ok.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.btn_ok: int num1 = Integer.parseInt(et_num1.getText().toString()); int num2 = Integer.parseInt(et_num2.getText().toString()); try { //结果 int res = aidl.add(num1,num2); tv_result.setText(\"结果:\"+res); } catch (RemoteException e) { e.printStackTrace(); } break; } } / 开启服务 / private void bindServices() { //获取服务端 Intent intent = new Intent(); //5.0之后的改变 intent.setComponent(new ComponentName(\"com.lgl.aidlservice\",\"com.lgl.aidlservice.IRemoteService\")); //绑定服务 bindService(intent,conn, Context.BIND_AUTO_CREATE); } / 销毁服务 / @Override protected void onDestroy() { super.onDestroy(); unbindService(conn); }}这里逻辑也是十分的清晰,你创建了之后启动服务,我调用方法来计算,我们来演示一下,我们先启动服务端,再启动客户端记得注册 <service android:name=\".IRemoteService\" android:process=\":remote\" android:exported=\"true\" > <intent-filter> <category android:name=\"android.intent.category.DEFAULT\" /> <action android:name=\"com.lgl.aidlservice.IRemoteService\" /> </intent-filter> </service>写到这里才看到玉刚也写了相关的信息,这边android:exported=\"true\"为权限问题,具体可以看下:[android跨进程通信(IPC):使用AIDL](http://blog.csdn.net/singwhatiwanna/article/details/17041691)不过这终究也只是一些小菜,我们项目中也不可能用到这么low的方法,毕竟AIDL还是很强大的,既然这样,那我们来玩玩高级一点的四.AIDL数据传递我们不能传递很大的数据这是总所周知的,那AIDL默认支持什么类型的尼?我们可不可以自定义尼?当然可以1.默认数据类型我们可以看一下官方文档- 1.基本数据类型- 2.String - 3.CharSequence- 4.List- 5.Map- 6.Parcelable(序列化)我们新建一个aidl文件,他都会自己生成一个方法void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);意指的就是基础类型然而在我们的实际测试中,你会发现他是不支持short的,我们做这样的小测试 void basicTypes(byte b,short s, int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString);然后编译/Users/lgl/Documents/Android/ASCode/AIDLService/app/src/main/aidl/com/lgl/aidlservice/IMyAidlInterface.aidl:11 parameter s (2) unknown type short他报的错误就是不支持short再比如说,你想写一个List< String>list的数据,你必须指定他是in还是out类型的in List< String>list2.自定义类型这里我们先在服务端新建一个aidl文件-IMyAidlInterface,然后写入我们的方法 //我们返回的数据是一个List集合 List<Person>add(in Person person);这里报错是毋庸置疑的,因为他不识别我们的Person,但是就算我们新建一个Person类也是无用的,因为aidl不支持这个类型,上面也说了,他只支持基本类型和其他有限的几种数据类型,这样的话,我们就需要自定义了,怎么来呢?让他implements Parcelable就可以package com.lgl.aidlservice;import android.os.Parcel;import android.os.Parcelable;/ 序列化实体类 Created by lgl on 16/3/15. /public class Person implements Parcelable{ private String name; private int age; //构造方法 public Person(String name,int age){ this.name =name; this.age = age; } protected Person(Parcel in) { name = in.readString(); age = in.readInt(); } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel parcel, int i) { parcel.writeString(name); parcel.writeInt(age); } public static final Creator<Person> CREATOR = new Creator<Person>() { @Override public Person createFromParcel(Parcel in) { return new Person(in); } @Override public Person[] newArray(int size) { return new Person[size]; } };}这里值得注意的就是读写部分了,你是怎么写的就得怎么读,顺序错了就没用了,那我们编译一下,你会发现他还是提示错误,依然是位置的List数据类型/Users/lgl/Documents/Android/ASCode/AIDLService/app/src/main/aidl/com/lgl/aidlservice/IMyAidlInterface.aidl:9 unknown return type List<Person>这里就需要我们手动导入这个Person了不然人家也不认识啊。

那要怎么做?还是需要创建一个Person的AIDL文件,内容十分的简单,说明文件// IMyAidlInterface.aidlpackage com.lgl.aidlservice;parcelable Person;可以看到,里面就一句话,好的,现在编译通过了,那我们重新来修改一下IRemoteService了,这次就不是返回计算数据的和了package com.lgl.aidlservice;import android.app.Service;import android.content.Intent;import android.os.IBinder;import android.os.RemoteException;import java.util.ArrayList;import java.util.List;/ 监听服务 Created by lgl on 16/3/13. /public class IRemoteService extends Service{ private ArrayList<Person>persons; / 当客户端绑定到该服务的时候启动onBind @param intent @return / @Override public IBinder onBind(Intent intent) { persons = new ArrayList<Person>(); //绑定之后就计算 return iBinder; } / 开始处理结果 /// private IBinder iBinder = new ServiceAidlInterface.Stub(){//// @Override// public int add(int num1, int num2) throws RemoteException {// Log.i(\"AIDL\",\"收到了请求,參數1\"+num1+\"參數2\"+num2);// return num1+num2;// }// }; / 序列化 / private IBinder iBinder = new IMyAidlInterface.Stub(){ @Override public List<Person> add(Person person) throws RemoteException { //每增加一个人都能得到返回 persons.add(person); return persons; } };}好的,服务端部分写完了,刚开始可能是有点混乱的,不过当你确实已经理解之后你会发现,也就这么点东西的逻辑,那我们继续来写客户端,看他是怎么进行序列化的处理的。
还是一样,我们把Person.aidl和IMyAidlInterface.aidl复制过去,顺便把Person类拷贝过来,这里要注意的事就是包名要一致然后我们就开启服务了package com.lgl.aidlclients;import android.content.ComponentName;import android.content.Context;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.util.Log;import android.view.View;import android.widget.Button;import android.widget.EditText;import android.widget.TextView;import com.lgl.aidlservice.IMyAidlInterface;import com.lgl.aidlservice.Person;import com.lgl.aidlservice.ServiceAidlInterface;import java.util.ArrayList;/ AIDL 客户端 @author lgl /public class MainActivity extends AppCompatActivity implements View.OnClickListener { //输入 private EditText et_num1,et_num2; //结果 private TextView tv_result; //AIDL远程访问 private Button btn_ok; private ServiceAidlInterface aidl; private IMyAidlInterface imy; private ServiceConnection conn = new ServiceConnection() { //连接上 @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { //拿到远程服务 aidl = ServiceAidlInterface.Stub.asInterface(iBinder); imy = IMyAidlInterface.Stub.asInterface(iBinder); } //断开时 @Override public void onServiceDisconnected(ComponentName componentName) { //回收 aidl = null; imy = null; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindServices(); initView(); } private void initView() { et_num1 = (EditText) findViewById(R.id.et_num1); et_num2 = (EditText) findViewById(R.id.et_num2); tv_result = (TextView) findViewById(R.id.tv_result); btn_ok = (Button) findViewById(R.id.btn_ok); btn_ok.setOnClickListener(this); } @Override public void onClick(View view) { switch (view.getId()){ case R.id.btn_ok:// int n1 = Integer.parseInt(et_num1.getText().toString());// int n2 = Integer.parseInt(et_num2.getText().toString());//// try {// //结果// int res = aidl.add(n1,n2);// tv_result.setText(\"结果:\"+res);// } catch (RemoteException e) {// e.printStackTrace();// tv_result.setText(\"Error\");// } try{ ArrayList<Person>persons = (ArrayList<Person>) imy.add(new Person(\"name\",21)); Log.i(\"Person\",persons.toString()); }catch (RemoteException e){ e.printStackTrace(); } break; } } / 开启服务 / private void bindServices() { //获取服务端 Intent intent = new Intent(); //5.0之后的改变 intent.setComponent(new ComponentName(\"com.lgl.aidlservice\",\"com.lgl.aidlservice.IRemoteService\")); //绑定服务 bindService(intent,conn, Context.BIND_AUTO_CREATE); } / 销毁服务 / @Override protected void onDestroy() { super.onDestroy(); unbindService(conn); }}这样的话,只要我们点击了计算按钮,Log打印一条消息,那事实是如此吗?我们运行一下不会发现,我们每一次添加都是多增加了一行,相当于我们远程添加了一行五.AIDL的工作原理我们每次编译AIDL的文件的时候,都会编译成一个JAVA文件,具体是什么,我们一起研究研究,借用大神的一张图,你现在可能看不懂,等讲完原理你就懂了编译好的JAVA文件在/ This file is auto-generated. DO NOT MODIFY. Original file: /Users/lgl/Documents/Android/ASCode/AIDLService/aidlclients/src/main/aidl/com/lgl/aidlservice/IMyAidlInterface.aidl /package com.lgl.aidlservice;//他是继承系统的接口,也就是说他本身就是一个接口public interface IMyAidlInterface extends android.os.IInterface{/ 存根,并且实现了自己的接口/public static abstract class Stub extends android.os.Binder implements com.lgl.aidlservice.IMyAidlInterface{private static final java.lang.String DESCRIPTOR = \"com.lgl.aidlservice.IMyAidlInterface\";/自己的构造方法/public Stub(){this.attachInterface(this, DESCRIPTOR);}/ Cast an IBinder object into an com.lgl.aidlservice.IMyAidlInterface interface, generating a proxy if needed. /public static com.lgl.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.lgl.aidlservice.IMyAidlInterface))) {return ((com.lgl.aidlservice.IMyAidlInterface)iin);}return new com.lgl.aidlservice.IMyAidlInterface.Stub.Proxy(obj);}@Override public android.os.IBinder asBinder(){ //返回的this是Stubreturn 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_add:{data.enforceInterface(DESCRIPTOR);com.lgl.aidlservice.Person _arg0;if ((0!=data.readInt())) { //如果拿到传过来数值封装成一个data_arg0 = com.lgl.aidlservice.Person.CREATOR.createFromParcel(data);}else {_arg0 = null;}java.util.List<com.lgl.aidlservice.Person> _result = this.add(_arg0);reply.writeNoException(); //最后通过这个又写回去了,这样就完成了整个的通讯reply.writeTypedList(_result);return true;}}return super.onTransact(code, data, reply, flags);} //代理,他是Stud的内部类private static class Proxy implements com.lgl.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;}//我们返回的数据是一个List集合@Override public java.util.List<com.lgl.aidlservice.Person> add(com.lgl.aidlservice.Person person) throws android.os.RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();java.util.List<com.lgl.aidlservice.Person> _result;try { //写入了类名_data.writeInterfaceToken(DESCRIPTOR);if ((person!=null)) { //写了一个1,同时add数据,当你拿到了远程服务的时候,其实你拿到的只是远程服务的代理_data.writeInt(1);person.writeToParcel(_data, 0);}else {_data.writeInt(0);} //传入到onTransactmRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);_reply.readException();_result = _reply.createTypedArrayList(com.lgl.aidlservice.Person.CREATOR);}finally {_reply.recycle();_data.recycle();}return _result;}}static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);}//我们返回的数据是一个List集合//我们创建AIDL命名的这个方法public java.util.List<com.lgl.aidlservice.Person> add(com.lgl.aidlservice.Person person) throws android.os.RemoteException;}Ok,AIDL这边算是讲完了,如果有兴趣的话,可以再去深入研究一下,我这边也只是学习了大神的视频,然后做的一个小总结和小笔记Demo下载:http://download.csdn.net/detail/qq_26787115/9462515
使用方法工作原理初涉AndroidAIDLIPC(工作原理使用方法初涉客户端服务)
(图片来源网络,侵删)

联系我们

在线咨询:点击这里给我发消息