本节引言:
1.Binder机制初涉
1)IBinder和Binder是什么鬼?
我们来看看官方文档怎么说:
中文翻译:
好吧,估计你看完上这一串东西可能云里雾里的,这里简单的小结下:
IBinder是Android给我们提供的一个进程间通信的一个接口,而我们一般是不直接实现这个接口的, 而是通过继承Binder类来实现进程间通信!是Android中实现IPC(进程间通信)的一种方式!
2)Binder机制浅析
大概调用流程如下,另外Service Manager比较复杂,这里并不详细研究!
流程解析:
3)为何Android使用Binder机制来实现进程间的通信?
当然,作为一个初级的开发者我们并不关心上述这些,Binder机制给我们带来的最直接的好处就是: 我们无需关心底层如何实现,只需按照AIDL的规则,自定义一个接口文件, 然后调用调用接口中的方法,就可以完成两个进程间的通信了!
2.AIDL使用详解
1)AIDL是什么?
2)AIDL实现两个进程间的简单通信
在开始编写AIDL接口文件前,我们需要了解下编写AIDL的一些注意事项:
AIDL注意事项:
1.服务端:
Step 1:创建AIDL文件:
IPerson.aidl
package com.jay.aidl; interface IPerson { String queryPerson(int num); }
我们打开IPerson.java看看里面的代码:
IPerson.java
/* * This file is auto-generated. DO NOT MODIFY. * Original file: C:\\Code\\ASCode\\AIDLServer\\app\\src\\main\\aidl\\com\\jay\\aidl\\IPerson.aidl */ package com.jay.aidl; public interface IPerson extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.jay.aidl.IPerson { private static final java.lang.String DESCRIPTOR = "com.jay.aidl.IPerson"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.jay.aidl.IPerson interface, * generating a proxy if needed. */ public static com.jay.aidl.IPerson asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.jay.aidl.IPerson))) { return ((com.jay.aidl.IPerson)iin); } return new com.jay.aidl.IPerson.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_queryPerson: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); java.lang.String _result = this.queryPerson(_arg0); reply.writeNoException(); reply.writeString(_result); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.jay.aidl.IPerson { 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 queryPerson(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_queryPerson, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_queryPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); } public java.lang.String queryPerson(int num) throws android.os.RemoteException; }
这里我们关注的只是**asInterface(IBinder)**和我们定义的接口中的**queryPerson()**方法!
该方法会把IBinder类型的对象转换成IPerson类型的,必要时生成一个代理对象返回结果!
其他的我们可以不看,直接跳过,进行下一步。
Step 2:**自定义我们的Service类,完成下述操作:
1)继承Service类,同时也自定义了一个PersonQueryBinder类用来继承IPerson.Stub类 就是实现了IPerson接口和IBinder接口
2)实例化自定义的Stub类,并重写Service的onBind方法,返回一个binder对象!
AIDLService.java
package com.jay.aidlserver; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; import com.jay.aidl.IPerson.Stub; /** * Created by Jay on 2015/8/18 0018. */ public class AIDLService extends Service { private IBinder binder = new PersonQueryBinder(); private String[] names = {"B神","艹神","基神","J神","翔神"}; private String query(int num) { if(num > 0 && num < 6){ return names[num - 1]; } return null; } @Override public IBinder onBind(Intent intent) { return null; } private final class PersonQueryBinder extends Stub{ @Override public String queryPerson(int num) throws RemoteException { return query(num); } } }
Step 3:在AndroidManifest.xml文件中注册Service
<service android:name=".AIDLService"> <intent-filter> <action android:name="android.intent.action.AIDLService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
这里我们并没有提供Activity界面,但是改应用提供的Service可以供其他app来调用!
2.客户端
直接把服务端的那个aidl文件复制过来,然后我们直接在MainActivity中完成,和绑定本地Service的操作
有点类似,流程如下:
1)自定义PersonConnection类实现ServiceConnection接口
2)以PersonConnection对象作为参数,调用bindService绑定远程Service
bindService(service,conn,BIND_AUTO_CREATE);
ps:第三个参数是设置如果服务没有启动的话,自动创建
3)和本地Service不同,绑定远程Service的ServiceConnection并不能直接获取Service的onBind( )方法
返回的IBinder对象,只能返回onBind( )方法所返回的代理对象,需要做如下处理:
iPerson = IPerson.Stub.asInterface(service);
再接着完成初始化,以及按钮事件等就可以了
具体代码如下:
MainActivity.java
package com.jay.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 com.jay.aidl.IPerson; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private EditText edit_num; private Button btn_query; private TextView txt_name; private IPerson iPerson; private PersonConnection conn = new PersonConnection(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindViews(); //绑定远程Service Intent service = new Intent("android.intent.action.AIDLService"); service.setPackage("com.jay.aidlserver"); bindService(service, conn, BIND_AUTO_CREATE); btn_query.setOnClickListener(this); } private void bindViews() { edit_num = (EditText) findViewById(R.id.edit_num); btn_query = (Button) findViewById(R.id.btn_query); txt_name = (TextView) findViewById(R.id.txt_name); } @Override public void onClick(View v) { String number = edit_num.getText().toString(); int num = Integer.valueOf(number); try { txt_name.setText(iPerson.queryPerson(num)); } catch (RemoteException e) { e.printStackTrace(); } edit_num.setText(""); } private final class PersonConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { iPerson = IPerson.Stub.asInterface(service); } public void onServiceDisconnected(ComponentName name) { iPerson = null; } } }
接下来先启动AIDLServivce,然后再启动AIDLClient,输入查询序号,即可获得对应姓名! 当然也可以直接启动AIDLClient,也会获得同样效果:
效果图如下:
3)传递复杂数据的AIDL Service
——Parcelable接口简介:
相信用过序列化的基本上都知道这个接口了,除了他还有另外一个Serializable,同样是用于序列化的, 只是Parcelable更加轻量级,速度更快!但是写起来就有点麻烦了,当然如果你用的as的话可以用 的插件来完成序列化,比如:Android Parcelable Code Generator 当然,这里我们还是手把手教大家来实现这个接口~
首先需要实现:writeToParcel和readFromPacel方法 写入方法将对象写入到包裹(parcel)中,而读取方法则从包裹中读取对象, 请注意,写入属性顺序需与读取顺序相同
接着需要在:该类中添加一个名为CREATOR的static final属性
改属性需要实现:android.os.Parcelable.Creator
再接着需要从写接口中的两个方法: createFromParcel(Parcel source)方法:实现从source创建出JavaBean实例的功能 newArray(int size):创建一个类型为T,长度为size的数组,只有一个简单的return new T[size]; (这里的T是Person类)
最后,describeContents():这个我也不知道是拿来干嘛的,直接返回0即可!不用理他
——另外,非原始类型中,除了String和CharSequence以外,其余均需要一个方向指示符。 方向指示符包括 in、out、和inout。in表示由客户端设置,out表示由服务端设置,inout表示客户端和服务端都设置了该值。
好的,接着来写代码试试(AS这里自定义类型有点问题,暂时还没解决,就用回Eclipse~):
代码示例:
自定义两种对象类型:Person与Salary,Person作为调用远程的Service的参数,Salary作为返回值! 那么首先要做的就是创建Person与Salary类,同时需要实现Parcelable接口
1.——服务端
Step 1:创建Person.aidl和Salary.aidl的文件,因为他们需要实现Parcelable接口,所以就下面一条语句:
Person.aidl: parcelable Person; Salary.aidl: parcelable Salary;Step 2:分别建立Person类与Salary类,需实现Parcelable接口,重写对应的方法!
Person.java:
package com.jay.example.aidl; import android.os.Parcel; import android.os.Parcelable; /** * Created by Jay on 2015/8/18 0018. */ public class Person implements Parcelable{ private Integer id; private String name; public Person() {} public Person(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public String getName() { return name; } //实现Parcelable必须实现的方法,不知道拿来干嘛的,直接返回0就行了 @Override public int describeContents() { return 0; } //写入数据到Parcel中的方法 @Override public void writeToParcel(Parcel dest, int flags) { //把对象所包含的数据写入到parcel中 dest.writeInt(id); dest.writeString(name); } //必须提供一个名为CREATOR的static final属性 该属性需要实现 //android.os.Parcelable.Creator<T>接口 public static final Parcelable.Creator<Person> CREATOR = new Parcelable.Creator<Person>() { //从Parcel中读取数据,返回Person对象 @Override public Person createFromParcel(Parcel source) { return new Person(source.readInt(),source.readString()); } @Override public Person[] newArray(int size) { return new Person[size]; } }; //因为我们集合取出元素的时候是根据Person对象来取得,所以比较麻烦, //需要我们重写hashCode()和equals()方法 @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Person other = (Person) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } } <pre><p><strong>Salary.java</strong>~照葫芦画瓢</p> <pre> package com.jay.example.aidl; import android.os.Parcel; import android.os.Parcelable; /** * Created by Jay on 2015/8/18 0018. */ public class Salary implements Parcelable { private String type; private Integer salary; public Salary() { } public Salary(String type, Integer salary) { this.type = type; this.salary = salary; } public String getType() { return type; } public Integer getSalary() { return salary; } public void setType(String type) { this.type = type; } public void setSalary(Integer salary) { this.salary = salary; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(type); dest.writeInt(salary); } public static final Parcelable.Creator<Salary> CREATOR = new Parcelable.Creator<Salary>() { //从Parcel中读取数据,返回Person对象 @Override public Salary createFromParcel(Parcel source) { return new Salary(source.readString(), source.readInt()); } @Override public Salary[] newArray(int size) { return new Salary[size]; } }; public String toString() { return "工作:" + type + " 薪水: " + salary; } }
Step 3:创建一个ISalary.aidl的文件,在里面写一个简单的获取工资信息的方法:
package com.jay.example.aidl; import com.jay.example.aidl.Salary; import com.jay.example.aidl.Person; interface ISalary { //定义一个Person对象作为传入参数 //接口中定义方法时,需要制定新参的传递模式,这里是传入,所以前面有一个in Salary getMsg(in Person owner); }
ps:这里可以记得如果使用的是自定义的数据类型的话,需要import哦!!!切记!!!
Step 4:核心Service的编写: 定义一个SalaryBinder类继承Stub,从而实现ISalary和IBinder接口;定义一个存储信息的Map集合! 重新onBind方法,返回SalaryBinder类的对象实例!
AidlService.java
package com.jay.example.aidl_complexservice; import java.util.HashMap; import java.util.Map; import com.jay.example.aidl.ISalary.Stub; import com.jay.example.aidl.Person; import com.jay.example.aidl.Salary; import android.app.Service; import android.content.Intent; import android.os.IBinder; import android.os.RemoteException; public class AidlService extends Service { private SalaryBinder salaryBinder; private static Map<Person,Salary> ss = new HashMap<Person, Salary>(); //初始化Map集合,这里在静态代码块中进行初始化,当然你可也以在构造方法中完成初始化 static { ss.put(new Person(1, "Jay"), new Salary("码农", 2000)); ss.put(new Person(2, "GEM"), new Salary("歌手", 20000)); ss.put(new Person(3, "XM"), new Salary("学生", 20)); ss.put(new Person(4, "MrWang"), new Salary("老师", 2000)); } @Override public void onCreate() { super.onCreate(); salaryBinder = new SalaryBinder(); } @Override public IBinder onBind(Intent intent) { return salaryBinder; } //同样是继承Stub,即同时实现ISalary接口和IBinder接口 public class SalaryBinder extends Stub { @Override public Salary getMsg(Person owner) throws RemoteException { return ss.get(owner); } } @Override public void onDestroy() { System.out.println("服务结束!"); super.onDestroy(); } }
注册下Service:
<service android:name=".AidlService"> <intent-filter> <action android:name="android.intent.action.AIDLService" /> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
2——客户端编写
Step 1:把服务端的AIDL文件拷贝下,拷贝后目录如下:
Step 2:编写简单的布局,再接着就是核心MainActvitiy的实现了 定义一个ServciceConnection对象,重写对应方法,和前面的普通数据的类似 再接着在bindService,然后再Button的点击事件中获取Salary对象并显示出来!
MainActivity.java
package com.jay.example.aidl_complexclient; import com.jay.example.aidl.ISalary; import com.jay.example.aidl.Person; import com.jay.example.aidl.Salary; import android.app.Activity; import android.app.Service; 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.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; public class MainActivity extends Activity { private ISalary salaryService; private Button btnquery; private EditText editname; private TextView textshow; private ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { salaryService = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { //返回的是代理对象,要调用这个方法哦! salaryService = ISalary.Stub.asInterface(service); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btnquery = (Button) findViewById(R.id.btnquery); editname = (EditText) findViewById(R.id.editname); textshow = (TextView) findViewById(R.id.textshow); Intent it = new Intent(); it.setAction("com.jay.aidl.AIDL_SERVICE"); bindService(it, conn, Service.BIND_AUTO_CREATE); btnquery.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { try { String name = editname.getText().toString(); Salary salary = salaryService.getMsg(new Person(1,name)); textshow.setText(name + salary.toString()); }catch(RemoteException e){e.printStackTrace();} } }); } @Override protected void onDestroy() { super.onDestroy(); this.unbindService(conn); } }
运行截图:
PS: 这里的代码是之前用Eclipse写的代码,Android Studio下自定义类型有点问题,
暂时没找到解决方法,如果知道的朋友请告知下!!!万分感激!!!
出现的问题如下:
两个实例的代码下载(基于Eclipse的):
1)使用AIDL完成进程间的简单通信
2)传递复杂数据的AIDL Service的实现
3.直接通过Binder的onTransact完成跨进程通信
上面讲过Android可以通过Binder的onTrensact方法来完成通信,下面就来简单试下下,还是前面那个根据 序号查询名字的例子:
服务端实现:
/** * Created by Jay on 2015/8/18 0018. */ public class IPCService extends Service{ private static final String DESCRIPTOR = "IPCService"; private final String[] names = {"B神","艹神","基神","J神","翔神"}; private MyBinder mBinder = new MyBinder(); private class MyBinder extends Binder { @Override protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { switch (code){ case 0x001: { data.enforceInterface(DESCRIPTOR); int num = data.readInt(); reply.writeNoException(); reply.writeString(names[num]); return true; } } return super.onTransact(code, data, reply, flags); } } @Override public IBinder onBind(Intent intent) { return mBinder; } }
客户端实现:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private EditText edit_num; private Button btn_query; private TextView txt_result; private IBinder mIBinder; private ServiceConnection PersonConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { mIBinder = null; } @Override public void onServiceConnected(ComponentName name, IBinder service) { mIBinder = service; } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bindViews(); //绑定远程Service Intent service = new Intent("android.intent.action.IPCService"); service.setPackage("com.jay.ipcserver"); bindService(service, PersonConnection, BIND_AUTO_CREATE); btn_query.setOnClickListener(this); } private void bindViews() { edit_num = (EditText) findViewById(R.id.edit_num); btn_query = (Button) findViewById(R.id.btn_query); txt_result = (TextView) findViewById(R.id.txt_result); } @Override public void onClick(View v) { int num = Integer.parseInt(edit_num.getText().toString()); if (mIBinder == null) { Toast.makeText(this, "未连接服务端或服务端被异常杀死", Toast.LENGTH_SHORT).show(); } else { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); String _result = null; try{ _data.writeInterfaceToken("IPCService"); _data.writeInt(num); mIBinder.transact(0x001, _data, _reply, 0); _reply.readException(); _result = _reply.readString(); txt_result.setText(_result); edit_num.setText(""); }catch (RemoteException e) { e.printStackTrace(); } finally { _reply.recycle(); _data.recycle(); } } } }
运行截图:
代码比较简单,就不多解释了~用到自己改改即可! PS:代码参考于:Android aidl Binder框架浅析
4.Android 5.0后Service一些要注意的地方:
官方文档:http://developer.android.com/intl/zh-cn/guide/components/intents-filters.html#Types
文档说明处: