一、Binder的路由原理
1.客户端通过ServiceManager的getService()获取service在本地的代理
2.通过service的代理找到service的binder实体
3.通过service的binder实体获取service所在的目标进程
4.然后把binder事务插入目标进程或目标进程的线程的todo队列中
Binder
二、Java层binder实例
2.1 定义service接口
package com.qp.javabinder;
public interface IMyService extends IInterface {
//DESCRIPTOR是唯一标识
static final java.lang.String DESCRIPTOR = " com.qp.javabinder.MyServer";
//TRANSACTION_say是binder通信的cmd
static final int TRANSACTION_say = android.os.IBinder.FIRST_CALL_TRANSACTION;
//service中的接口,提供给客户端用实现某些功能
public void sayHello(String str) throws RemoteException ;
}
2.2 实现service
public class MyService extends Binder implements IMyService{//继承自Binder实现IMyService接口
public MyService() {
this.attachInterface(this, DESCRIPTOR);
}
@Override
public IBinder asBinder() {
return this;
}
public static com.qp.javabinder.IMyService asInterface(android.os.IBinder obj) {
if ((obj == null)) return null;
android.os.IInterface iInterface = obj.queryLocalInterface(DESCRIPTOR);
if (( iInterface != null && iInterface instanceof com.qp.javabinder.IMyService)){
return ((com.qp.javabinder.IMyService) iInterface);
}
return null;
}
@Override
protected boolean onTransact(
int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_say: {//响应命令
data.enforceInterface(DESCRIPTOR);
String str = data.readString();
sayHello(str);
reply.writeNoException();
return true;
}}
return super.onTransact(code, data, reply, flags);
}
@Override
public void sayHello(String str) { //实现接口
System.out.println("MyService:: Hello, " + str);
}
}
2.3 注册service
public class ServerDemo {
public static void main(String[] args) {
Looper.prepareMainLooper();
android.os.Process.setThreadPriority(
android.os.Process.THREAD_PRIORITY_FOREGROUND);
ServiceManager.addService("MyService", new MyService()); //注册service
Looper.loop();
}
}
2.4 实现service代理
拷贝一份IMyService到客户端项目中。然后实现MyServiceProxy.java
public class MyServiceProxy implements IMyService {
private android.os.IBinder mRemote;
public MyServiceProxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public void sayHello(String str) throws RemoteException {
android.os.Parcel data = android.os.Parcel.obtain();
android.os.Parcel reply = android.os.Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
data.writeString(str);
mRemote.transact(TRANSACTION_say, _data, _reply, 0);//通过获取的mRemote(即service在本地的代理)发送命令
reply.readException();
} finally {
reply.recycle();
data.recycle();
}
}
}
2.5 使用service
public class ClientDemo {
public static void main(String[] args) throws RemoteException {
IBinder binder = ServiceManager.getService("MyService");
IMyService myService = new MyServiceProxy(binder);
myService.sayHello("binder");
}
}
三、使用AIDL
3.1 AIDL简介
AIDL(Android Interface Definition Language),即Android接口定义语言,Android提供的IPC的一种独特实现。使用AIDL可以让我们很方便的实现跨进程的binder通信。
AIDL会自动帮你生成一个对应的java文件,该java文件中是一个和aidl文件重名的接口类,其包含一个名为Stub的内部类(其继承自Binder,实现binder操作必须的一些接口:asInterface、asBinder、onTransact、class proxy等)、还包含开发人员在aidl文件中自定义的接口。开发人员仅关注自己定义的接口的实现即可。
那么什么时候用AIDL呢?
既然是IPC的一种实现,那么就是需要跨进程通信的时候
且需要server端同时处理多线程的时候
使用AIDL涉及如下几点:
创建 .aidl 文件
在Service中实现 .Stub 的接口
启动service并使用service中的接口
3.2 新建aidl文件
在android studio中自定义工程的/app/src/main包名目录下新建aidl目录,在改目录下新建一个IMyAidlInterface.aidl文件
// IMyAidlInterface.aidl
package com.qp.myapplication;
// Declare any non-default types here with import statements
interface IMyAidlInterface {
String getValue(); //自定义一个接口
}
保存后会自动在 /app/build/generated/source/aidl/debug/目录下生成IMyAidlInterface.java文件,这个文件我们不需要改动,其帮我们实现好了onTransact收命令处理(如2.2),proxy发命令处理(如2.4)。
3.3 新建service并实现接口
在android studio中自定义工程的/app/src/main/java目录下新建MyService.java文件,注意在androidmanifest.xml中声明(可以指定android:process将service运行在另一个进程中,如android:process=":remote",运行在com.qp.myapplication:remote进程中)。
package com.qp.myapplication;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent)
{
return new MyBinder(); //返回IMyAidlInterface.Stub实例
}
class MyBinder extends IMyAidlInterface.Stub
{
@Override
public String getValue() throws RemoteException { //实现自定义接口
return "test";
}
}
}
3.4 启动service并使用接口
主要是bindService获取service在本地的代理,通过代理调用其中的接口。
- bindService
Intent i = new Intent();
i.setClassName("com.qp.myapplication", com.qp.myapplication.MyService.class.getName());
bindService(i, connection, Context.BIND_AUTO_CREATE);
- connection
private class MyConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iMyAidlInterface = IMyAidlInterface.Stub.asInterface(service); //servie连接成功
Toast.makeText(MainActivity.this, "onServiceConnected", Toast.LENGTH_LONG).show();
}
@Override
public void onServiceDisconnected(ComponentName name) {
iMyAidlInterface = null;
}
}
- 调用接口
findViewById(R.id.aidl).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
if (iMyAidlInterface != null) {
Toast.makeText(MainActivity.this,
iMyAidlInterface.getValue(), //调用接口
Toast.LENGTH_LONG).show();
}
} catch (RemoteException e) {
e.printStackTrace();
}
}
});
3.5 在另一app中和该service通信
上面我们是在一个app中,定义两个进程(其中service跑在:remote进程中),来模拟跨进程通信。现在我们来看在另一app中如何使用本app的这个service。
其实很简单,只要将本app的aidl文件拷贝到另一个app目录下即可(拷贝而不是新建,是为了包名、接口等一致),然后bingservice和使用接口方式同上,即只是不需要新建service和实现其接口的过程。
3.6 aidl可使用的数据类型
studio帮忙默认生成的aidl文件中有如下内容,告诉我们aidl中仅能用这些基本数据类型:
/** * 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);
有时这些基本类型并不能满足我们的需要,那么我们如何来自定义数据类型呢?这时候可以用parcelable。
- 自定义MyData.java类继承自Parcelable,如下:
public class MyData implements Parcelable {
private int data1;
private int data2;
protected MyData() {
}
protected MyData(Parcel in) {
readFromParcel(in);
}
public static final Creator<MyData> CREATOR = new Creator<MyData>() {
@Override
public MyData createFromParcel(Parcel in) {
return new MyData(in);
}
@Override
public MyData[] newArray(int size) {
return new MyData[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(data1);
parcel.writeInt(data2);
}
public void readFromParcel(Parcel in){
data1 = in.readInt();
data2 = in.readInt();
}
public int getData2() {
return data2;
}
public void setData2(int data2) {
this.data2 = data2;
}
public int getData1() {
return data1;
}
public void setData1(int data1) {
this.data1 = data1;
}
@Override
public String toString() {
return "data1 = "+ data1 + ", data2="+ data2;
}
}
- 新建MyData.aidl文件
// MyData.aidl
package com.qp.myapplication;
parcelable MyData;
- 在MyService中使用该数据类型MyData
public class MyService extends Service {
private MyData mMyData;
public MyService() {
}
@Override
public void onCreate() {
super.onCreate();
mMyData = new MyData();
mMyData.setData1(10);
mMyData.setData2(20);
}
@Override
public IBinder onBind(Intent intent)
{
return new MyBinder();
}
class MyBinder extends IMyAidlInterface.Stub
{
@Override
public String getValue() throws RemoteException {
return "test";
}
@Override
public MyData getMyData() throws RemoteException {
return mMyData;
}
}
}
- 在client使用MyData
if (iMyAidlInterface != null) {
String str = iMyAidlInterface.getValue() + "," + iMyAidlInterface.getMyData().toString();
Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
}
- 在其他app中使用MyData
将MyData.aidl和MyData.java拷贝至其他app中,用法同
网友评论