2.3 IPC基础概念介绍(三)

1. 定义IBookManager接口

public interface IBookManager extends IInterface {
    static final java.lang.String DESCRIPTOR = "qingfengmy.developmentofart._2activity.Manual.IBookManager";

    static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    
    public java.util.List<qingfengmy.developmentofart._2activity.aidl.Book> getBookList() throws android.os.RemoteException;

    public void addBook(qingfengmy.developmentofart._2activity.aidl.Book book) throws android.os.RemoteException;
}

声明一个aidl性质的接口,只需要继承IInterface接口(接口继承接口)。定义两个业务方法和对应的ID,以及binder特有的唯一标识符DESCRIPTOR,一般为完整类名。

2. 实现Stub类

public class BookManagerImpl extends Binder implements IBookManager {
    /**
     * Construct the stub at attach it to the interface.
     * 构建连接到接口的stub
     */
    public BookManagerImpl() {
        this.attachInterface(this, DESCRIPTOR);
    }

    @Override
    public List<Book> getBookList() throws RemoteException {
        // 待实现
        return null;
    }

    @Override
    public void addBook(Book book) throws RemoteException {
        // 待实现
    }

    @Override
    public IBinder asBinder() {
        return this;
    }
}

3. 添加Stub的子类Proxy

/**
 * Cast an IBinder object into an IBookManager interface,
 * generating a proxy if needed.
 * <p/>
 * 客户端连接服务端会通过该方法获得Binder,根据是否跨应用会返回IBookManager或者代理Proxy.
 * 跨应用时返回代理,客户端拿到代理,调用方法时,实际是transact方法去调用onTransact的。
 */
public static IBookManager asInterface(IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof IBookManager))) {
        return ((IBookManager) iin);
    }
    return new BookManagerImpl.Proxy(obj);
}

private static class Proxy implements IBookManager {
    private IBinder mRemote;

    Proxy(IBinder remote) {
        mRemote = remote;
    }

    @Override
    public android.os.IBinder asBinder() {
        return mRemote;
    }

    public java.lang.String getInterfaceDescriptor() {
        return DESCRIPTOR;
    }

    @Override
    public java.util.List<Book> getBookList() throws RemoteException {
        Parcel _data = Parcel.obtain();
        Parcel _reply = Parcel.obtain();
        List<Book> _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(TRANSACTION_getBookList, _data, _reply, 0);
            _reply.readException();
            _result = _reply.createTypedArrayList(Book.CREATOR);
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }

    @Override
    public void addBook(Book book) throws RemoteException {
        Parcel _data = Parcel.obtain();
        Parcel _reply = Parcel.obtain();
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            if ((book != null)) {
                _data.writeInt(1);
                book.writeToParcel(_data, 0);
            } else {
                _data.writeInt(0);
            }
            mRemote.transact(TRANSACTION_addBook, _data, _reply, 0);
            _reply.readException();
        } finally {
            _reply.recycle();
            _data.recycle();
        }
    }
}

4. onTransact方法

Binder中的方法定义

/**
 * Default implementation is a stub that returns false.  You will want
 * to override this to do the appropriate unmarshalling of transactions.
 *
 * <p>If you want to call this, call transact().
 */
protected boolean onTransact(int code, Parcel data, Parcel reply,
        int flags) throws RemoteException {}

如果要调用binder中的onTransact方法,调用transact即可。transact是IBinder中定义的方法,Binder实现了IBinder,所以也有transact方法。如下

/**
 * Default implementation rewinds the parcels and calls onTransact.  On
 * the remote side, transact calls into the binder to do the IPC.
 */
public final boolean transact(int code, Parcel data, Parcel reply,
        int flags) throws RemoteException {
    if (false) Log.v("Binder", "Transact: " + code + " to " + this);
    if (data != null) {
        data.setDataPosition(0);
    }
    boolean r = onTransact(code, data, reply, flags);
    if (reply != null) {
        reply.setDataPosition(0);
    }
    return r;
}

可见其中调用了onTransact方法,在方法之前和之后做了处理。之前把输入型对象data的dataPosition设为0;之后把输出型对象reply的dataPosition设为0.
transact翻译为办理,binder在里面处理业务。根据code判断是哪个方法,data是输入型,reply是输出型。

@Override
public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(DESCRIPTOR);
            return true;
        }
        case TRANSACTION_getBookList: {
            data.enforceInterface(DESCRIPTOR);
            List<Book> _result = this.getBookList();
            reply.writeNoException();
            reply.writeTypedList(_result);
            return true;
        }
        case TRANSACTION_addBook: {
            data.enforceInterface(DESCRIPTOR);
            Book _arg0;
            if ((0 != data.readInt())) {
                _arg0 = Book.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            this.addBook(_arg0);
            reply.writeNoException();
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

开发中我们可以使用aidl,aidl的意义在于为我们提供了一种快速实现Binder的工具。它不是实现Binder的必须品,我们可以手写Binder。

5. 本应用使用自定义的Binder

public class ManualService extends Service {
    // 服务中的binder是private
    private BookManagerImpl bookManager;

    public ManualService() {
        bookManager = new BookManagerImpl();
    }

    @Override
    public IBinder onBind(Intent intent) {
        return bookManager;
    }

}
public class ManualActivity extends AppCompatActivity {

    ServiceConnection serviceConnection;
    // 接口是暴露出来的
    IBookManager bookManager;
    Intent intent;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_manual);

        serviceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                bookManager = BookManagerImpl.asInterface(service);
                Log.e("aaa","-----onServiceConnected-----");
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        };

        intent = new Intent(this, ManualService.class);
        bindService(intent, serviceConnection, BIND_AUTO_CREATE);

    }
    
    // click-调用getBookList方法
    bookManager.getBookList();
    // onDestroy
}

6. 跨应用调用服务

和aidl一样,首先创建包名类名一样的几个文件

qingfengmy.developmentofart._2activity.aidl.Book
qingfengmy.developmentofart._2activity.Manual.BookManagerImpl
qingfengmy.developmentofart._2activity.Manual.IBookManager

客户端调用

IBookManager iBookManager;
ServiceConnection serviceConnection;
Intent intent;

intent = new Intent();
intent.setAction("qingfengmy.developmentofart.ManualService");
intent.setPackage("qingfengmy.developmentofart");
serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        if (service != null) {
            iBookManager = BookManagerImpl.asInterface(service);
        }
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {

    }
};
bindService(intent, serviceConnection, Service.BIND_AUTO_CREATE);

// click点击事件调用服务端方法
iBookManager.getBookList();

7. likeToDeath和unlikeToDeath

Binder运行在服务端,如果服务端进程由于某种原因异常终止,这个时候我们连接服务端的Binder连接断裂(称之为Binder死亡),会导致我们的远程调用失败。
如果我们不知道Binder连接已经断裂,那么客户端的功能就会受影响。解决方法是给Binder设置死亡代理,当Binder死亡时,我们就会收到通知,这个时候我们就可以重新发起连接请求从而恢复连接。

// 定义死亡代理 recipient翻译为收件人
 final IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        // binder死亡时,会回调这个方法
        if (bookManager == null){
            return;
        }
        bookManager.asBinder().unlinkToDeath(this,0);
        bookManager = null;
        // 重连
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);
    }
};
serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        bookManager = BookManagerImpl.asInterface(service);
        // 设置死亡代理
        service.linkToDeath(mDeathRecipient,0);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {}
};

8. isBinderAlive

该方法用于判断Binder的状态

bookManager.asBinder().isBinderAlive();
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,236评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,867评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,715评论 0 340
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,899评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,895评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,733评论 1 283
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,085评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,722评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,025评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,696评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,816评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,447评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,057评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,254评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,204评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,561评论 2 343

推荐阅读更多精彩内容