美文网首页
Binder实例

Binder实例

作者: 你身边的那个TA | 来源:发表于2018-06-28 13:30 被阅读76次

一、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在本地的代理,通过代理调用其中的接口。

  1. bindService
Intent i = new Intent();
i.setClassName("com.qp.myapplication", com.qp.myapplication.MyService.class.getName());
bindService(i, connection, Context.BIND_AUTO_CREATE);
  1. 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; 
    }
}
  1. 调用接口
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。

  1. 自定义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;
    }
}
  1. 新建MyData.aidl文件
// MyData.aidl
package com.qp.myapplication;
parcelable MyData;
  1. 在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;
        }
    }
}
  1. 在client使用MyData
if (iMyAidlInterface != null) {
    String str = iMyAidlInterface.getValue() + "," + iMyAidlInterface.getMyData().toString();
    Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
}
  1. 在其他app中使用MyData
    将MyData.aidl和MyData.java拷贝至其他app中,用法同

相关文章

网友评论

      本文标题:Binder实例

      本文链接:https://www.haomeiwen.com/subject/ziogeftx.html