Android IPC系列(二):AIDL源码分析

Android 中如何开启多进程

这里说一下一个应用开启多个进程的情况,不讨论多个应用多进程,Android开启多进程只有一种方法,在AndroidManifest文件中给四大组件指定 android:process属性,其实还有一种非常规的方法通过JNI在native层fork一个进程,这种属于特殊情况

  <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            android:process=":remote" />
        <activity
            android:name=".ThreeActivity"
            android:process="om.baidu.bpit.aibaidu.ipc.remote.aa" />

我们分别给SecondActivity和ThreeActivity指定了process属性,并且他们的属性值不同,所以现在又三个进程,MainActivity默认进程,默认进程进程名是包名,还有俩个我们自己指定的进程
我们通过shell命令,看一下是否有这些进程

$ adb shell ps | grep om.baidu.bpit.aibaidu.ipc
u0_a59    2343  204   1606528 98700          0 0000000000 S com.baidu.bpit.aibaidu.ipc
u0_a59    2360  204   1609616 97952          0 0000000000 S com.baidu.bpit.aibaidu.ipc:remote
u0_a59    2388  204   1766960 116352          0 0000000000 S om.baidu.bpit.aibaidu.ipc.remote.aa

我们发现SecondActivity和ThreeActivity指定进程名的方式不同一个是 android:process=":remote" ,一个是**android:process=“om.baidu.bpit.aibaidu.ipc.remote.aa”**这俩种有什么区别吗?

区别有两方面

  • “:”的意思是在当前进程名上加上本包名,另一种是一个完整的命名方式
  • “:”开头的进程,属于当前应用的私有进程,其他应用组件不可以和他跑在同一进程,完整包名的属于全局进程,应用可以通过ShareUid的方式可以和他跑在同一进程

Android 系统会为每一个进程分配一个唯一的Uid,具有相同Uid的应用才可以共享数据,比如data目录,组件信息等,不管他们是否跑在同一进程,如果他们跑在同一进程,那么除了共享data目录,组件信息,还可以共享内存

多进程会遇到的问题

每一个进程都会有一个独立的虚拟机,不同的虚拟机在内存分配不同的空间,所以只要需要通过共享内存,共享数据的操作都会失败

  • 静态成员和单例完全失效
  • 线程同步机制完全失效
  • SharePreference可靠性下降,因为sp底层是通过读写xml来实现的,那么并发读写会发生问题
  • Application多次创建

什么是Binder

  • 直观来说Binder是Android中的一个类,实现了IBinder
  • IPC角度来说,Binder是Android中一种跨进程通信的方式
  • Binder还可以理解为一种虚拟的物理设备,他的驱动是dev/binder,这种通信方式linux中没有
  • 从Android Framework角度来说,Binder是ServerManager连接各种Manager(如ActivityManager)和ManagerService的桥梁
  • 从Android应用层来说,他是服务端和客户端通信的媒介

Android 中的IPC方式

  • 使用Bundle,四大组件都是支持Bundle的,由于Bundle实现了Parcelable接口,可以在进程中传递数据,我们在一个进程中启动另一个进程的Bundle,可以进程间通信
  • 使用文件共享
  • 使用Messager
  • 使用Aidl
  • 使用ContentProvider
  • 使用socket

分析一下AIDL源码

上篇博客我们分析了如何使用AIDL,这篇我们分析一下之前定义的BookName.aidl文件到底生成了什么,编译项目后我们可以在gen目录下找到BookName.java文件,这个就是aidl生成文件,我们看一下文件内容

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: /Users/v_renxiaohui01/Downloads/aidl/app/src/main/aidl/com/baidu/bpit/aibaidu/aidl/BookName.aidl
 */
package com.baidu.bpit.aibaidu.aidl;

public interface BookName extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.baidu.bpit.aibaidu.aidl.BookName {
        private static final java.lang.String DESCRIPTOR = "com.baidu.bpit.aibaidu.aidl.BookName";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.baidu.bpit.aibaidu.aidl.BookName interface,
         * generating a proxy if needed.
         */
        public static com.baidu.bpit.aibaidu.aidl.BookName asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.baidu.bpit.aibaidu.aidl.BookName))) {
                return ((com.baidu.bpit.aibaidu.aidl.BookName) iin);
            }
            return new com.baidu.bpit.aibaidu.aidl.BookName.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_getName: {
                    data.enforceInterface(DESCRIPTOR);
                    java.lang.String _result = this.getName();
                    reply.writeNoException();
                    reply.writeString(_result);
                    return true;
                }
                case TRANSACTION_getList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.baidu.bpit.aibaidu.aidl.User> _result = this.getList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addInout: {
                    data.enforceInterface(DESCRIPTOR);
                    com.baidu.bpit.aibaidu.aidl.User _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addInout(_arg0);
                    reply.writeNoException();
                    if ((_arg0 != null)) {
                        reply.writeInt(1);
                        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
                case TRANSACTION_addIn: {
                    data.enforceInterface(DESCRIPTOR);
                    com.baidu.bpit.aibaidu.aidl.User _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addIn(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_addout: {
                    data.enforceInterface(DESCRIPTOR);
                    com.baidu.bpit.aibaidu.aidl.User _arg0;
                    _arg0 = new com.baidu.bpit.aibaidu.aidl.User();
                    this.addout(_arg0);
                    reply.writeNoException();
                    if ((_arg0 != null)) {
                        reply.writeInt(1);
                        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.baidu.bpit.aibaidu.aidl.BookName {
            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 getName() 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);
                    mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readString();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public java.util.List<com.baidu.bpit.aibaidu.aidl.User> getList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.baidu.bpit.aibaidu.aidl.User> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.baidu.bpit.aibaidu.aidl.User.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((user != null)) {
                        _data.writeInt(1);
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addInout, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        user.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((user != null)) {
                        _data.writeInt(1);
                        user.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addIn, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_addout, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) {
                        user.readFromParcel(_reply);
                    }
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_getList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_addInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_addIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
        static final int TRANSACTION_addout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
    }

    public java.lang.String getName() throws android.os.RemoteException;

    public java.util.List<com.baidu.bpit.aibaidu.aidl.User> getList() throws android.os.RemoteException;

    public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException;

    public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException;

    public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException;
}

我们分析一下这个类,首先BookName.java继承自android.os.IInterface接口,同时他自己也是一个接口,所有在Binder传输的都需要继承IInterface接口,这个类首先声明了5个方法getName,getList,addInout,addin,addout,这些是我们在aidl文件定义的,同时他还为每个方法声明一个整形,为了在transct区分方法,当应用处于不同进程时需要走transct方法,这个逻辑由sub内部类proxy代理

IBinder
IBinder是一个接口,它代表了一种跨进程传输的能力;只要实现了这个接口,就能将这个对象进行跨进程传递;这是驱动底层支持的;在跨进程数据流经驱动的时候,驱动会识别IBinder类型的数据,从而自动完成不同进程Binder本地对象以及Binder代理对象的转换。

IInterface
这里的IInterface代表的就是远程server对象具有什么能力。具体来说,就是aidl里面的接口。

DESCRIPTOR
Binder的唯一标识,一般用Binder的类名表示,

  private static final java.lang.String DESCRIPTOR = "com.baidu.bpit.aibaidu.aidl.BookName";

asInterface
用于将服务端的Binder转换为客户端所需的AIDL接口类型对象,这种转换是区分进程的,如果客户端和服务端位于同一进程,那么返回的就是服务端本身Stub的对象,如果是不同进程,就返回Stub.Proxy对象

asBinder
此方法用于返回当前的Binder对象

onTransact
该方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,请求会通过底层封装后,交由此方法处理,服务端通过code来判断客户端请求的方法是什么,接着从data取出目标方法需要的参数(如果目标方法有参数的话),然后执行目标方法,执行完之后就向reply中写入返回值(如果目标方法有返回值的话),这就是这个方法的执行过程,如果此方法返回false,那么客户端请求失败,可以以此做验证

Proxy/addList
此方法运行在客户端,当客户端调用从方法时,他的内部实现是这样的,首先准备输入型Parcel对象_data,输出型Parcel对象_reply,和返回值对象List,然后把该方法的参数写入_data(如果有参数的话),接着调用transact方法发起RPC(远程过程调用),同时当前线程挂起,然后服务端的onTransact方法会被调用,知道RPC过程返回后,当前线程继续执行,并从_reply取出返回结果

其他几个Proxy中的方法和此过程一样

这就是Binder工作机制,我们需要注意俩点

  • 当客户端发起远程请求是时,当前线程会被挂起,知道服务端返回,如果远程方法是一个耗时方法,那不能在UI线程做
  • 服务端的Binder方法运行在Binder线程池中,所以Binder方法不管是否耗时都可以同步调用
    在这里插入图片描述

自定义一个Binder

从上方流程看出,其实我们可以不通过aidl就可以实现Binder,之所以有了Aidl是为了方便生成代码,那我们手动生成一个Bidner

首先生成一个aidl性质的接口,继承自IInterface即可,如下:


public interface CustomBookName extends IInterface {

    static final java.lang.String DESCRIPTOR = "com.baidu.bpit.aibaidu.aidl.BookName";

    static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    static final int TRANSACTION_getList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    static final int TRANSACTION_addInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
    static final int TRANSACTION_addIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
    static final int TRANSACTION_addout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);

    public java.lang.String getName() throws android.os.RemoteException;

    public java.util.List<com.baidu.bpit.aibaidu.aidl.User> getList() throws android.os.RemoteException;

    public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException;

    public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException;

    public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException;
}

之后我们需要实现一个Binder继承自Binder,和上方的接口

public abstract class CustomBinder extends Binder implements CustomBookName {

    /**
     * Construct the stub at attach it to the interface.
     */
    public CustomBinder() {
        this.attachInterface(this, DESCRIPTOR);
    }

    /**
     * Cast an IBinder object into an com.baidu.bpit.aibaidu.aidl.BookName interface,
     * generating a proxy if needed.
     */
    public static CustomBookName asInterface(android.os.IBinder obj) {
        if ((obj == null)) {
            return null;
        }
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
        if (((iin != null) && (iin instanceof CustomBookName))) {
            return (CustomBookName) iin;
        }
        return new CustomBinder.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_getName: {
                data.enforceInterface(DESCRIPTOR);
                java.lang.String _result = this.getName();
                reply.writeNoException();
                reply.writeString(_result);
                return true;
            }
            case TRANSACTION_getList: {
                data.enforceInterface(DESCRIPTOR);
                java.util.List<com.baidu.bpit.aibaidu.aidl.User> _result = this.getList();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            case TRANSACTION_addInout: {
                data.enforceInterface(DESCRIPTOR);
                com.baidu.bpit.aibaidu.aidl.User _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                this.addInout(_arg0);
                reply.writeNoException();
                if ((_arg0 != null)) {
                    reply.writeInt(1);
                    _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } else {
                    reply.writeInt(0);
                }
                return true;
            }
            case TRANSACTION_addIn: {
                data.enforceInterface(DESCRIPTOR);
                com.baidu.bpit.aibaidu.aidl.User _arg0;
                if ((0 != data.readInt())) {
                    _arg0 = com.baidu.bpit.aibaidu.aidl.User.CREATOR.createFromParcel(data);
                } else {
                    _arg0 = null;
                }
                this.addIn(_arg0);
                reply.writeNoException();
                return true;
            }
            case TRANSACTION_addout: {
                data.enforceInterface(DESCRIPTOR);
                com.baidu.bpit.aibaidu.aidl.User _arg0;
                _arg0 = new com.baidu.bpit.aibaidu.aidl.User();
                this.addout(_arg0);
                reply.writeNoException();
                if ((_arg0 != null)) {
                    reply.writeInt(1);
                    _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                } else {
                    reply.writeInt(0);
                }
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }

    private static class Proxy implements CustomBookName {
        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 getName() 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);
                mRemote.transact(TRANSACTION_getName, _data, _reply, 0);
                _reply.readException();
                _result = _reply.readString();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }

        @Override
        public java.util.List<com.baidu.bpit.aibaidu.aidl.User> getList() throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            java.util.List<com.baidu.bpit.aibaidu.aidl.User> _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(TRANSACTION_getList, _data, _reply, 0);
                _reply.readException();
                _result = _reply.createTypedArrayList(com.baidu.bpit.aibaidu.aidl.User.CREATOR);
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        }

        @Override
        public void addInout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                if ((user != null)) {
                    _data.writeInt(1);
                    user.writeToParcel(_data, 0);
                } else {
                    _data.writeInt(0);
                }
                mRemote.transact(TRANSACTION_addInout, _data, _reply, 0);
                _reply.readException();
                if ((0 != _reply.readInt())) {
                    user.readFromParcel(_reply);
                }
            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }

        @Override
        public void addIn(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                if ((user != null)) {
                    _data.writeInt(1);
                    user.writeToParcel(_data, 0);
                } else {
                    _data.writeInt(0);
                }
                mRemote.transact(TRANSACTION_addIn, _data, _reply, 0);
                _reply.readException();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }

        @Override
        public void addout(com.baidu.bpit.aibaidu.aidl.User user) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(TRANSACTION_addout, _data, _reply, 0);
                _reply.readException();
                if ((0 != _reply.readInt())) {
                    user.readFromParcel(_reply);
                }
            } finally {
                _reply.recycle();
                _data.recycle();
            }
        }
    }
}

这里面的主要方法,极其作用我们上方都讲过

下面我们使用一下自定义的Bidner类

  • 首先实现自己的binder

    class MyCustomBinder extends CustomBinder {

        @Override
        public String getName() throws RemoteException {
            return "我是自定义的binder";
        }

        @Override
        public List<User> getList() throws RemoteException {
            return null;
        }

        @Override
        public void addInout(User user) throws RemoteException {

        }

        @Override
        public void addIn(User user) throws RemoteException {

        }

        @Override
        public void addout(User user) throws RemoteException {

        }
    }
  • 然后在service中返回自定义的binder
  @Override
    public IBinder onBind(Intent intent) {
        return new MyCustomBinder();
    }
  • 把自定义的binder类拷进客户端
  • 客户端绑定service获取binder
 private void bindServer() {
        Intent mIntent = new Intent();
        //你定义的service的action
        mIntent.setAction("com.aaa.aaa");
        //这里你需要设置你应用的包名
        mIntent.setPackage("com.baidu.bpit.aibaidu.aidl");
        bindService(mIntent, new ServiceConnection() {

            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                customBookName = CustomBinder.asInterface(service);
            }

            @Override
            public void onServiceDisconnected(ComponentName name) {

            }
        }, BIND_AUTO_CREATE);
    }
  • 点击按钮调用自定义binder的getName方法

    private void initView() {
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                try {
                    String name = customBookName.getName();
                    Log.d("mmm",name);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        });
    }
  • 看下log
01-18 19:14:45.688 3554-3554/com.baidu.bpit.aibaidu.client D/mmm: 我是自定义的binder

成功了

发布了100 篇原创文章 · 获赞 5 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_34760508/article/details/97260147
今日推荐