IPC中的AIDL机制

Android IPC简介

IPC是Inter-Process Communication的缩写,含义是进程间通信或者跨进程通信,是指两个进程间进行数据交互的一个过程。
IPC不是Andrord中所独有的,任何一个操作系统都需要有相应的IPC机制,比如Windows上可以通过剪贴板、管道和邮槽等来进行进程间通信,、Linux上可以通过命名管道,共享内容、信号量等来进行进程间通信。可以看到不同的操作系统系统有着不同的进程通信方式。
对于Android来说,它是一种基于Linux内核的操作系统,他的进程间通信方式并不能完全继承自Linux,相反,它有自己的进程间通信方式。在Android中最有特色的进程间通信方式就是Binder了,通过Binder可以轻松地实现进程间通信。

多进程的创建

Android多进程创建很简单,只需要在AndroidManifest.xml的声明四大组件的标签中增加”android:process”属性即可。命名之后,就成了一个单独的进程。

process分私有进程和全局进程

  • 私有进程的名称前面有冒号,例如:
<service android:name=".MusicService"   
           android:process=":musicservice"/>
  • 全局进程的名称前面没有冒号,例如:
<service android:name=".MusicService"   
           android:process="com.trampcr.musicdemo.service"/>
私有进程与全局进程的区别

私有进程:其它应用的组件不可以和它跑在同一个进程中
全局进程:其它应用可以通过shareUID方式和它跑在同一个进程中(相同的shareUID和签名)
ShareUID:在Android里,每个app都有一个唯一的linux user ID,因此权限就被设置成该应用程序的文件只对该用户和应用程序自身可见。假如让两个app使用相同的userID,不论是否在同一个进程,它们都能看到对方的文件,比如data目录,组件信息等。如果在同一个进程,还可以共享内存数据。

多进程的问题

  • 静态成员和单例模式完全失效

Android为每个应用分配一个独立的虚拟机,或者说为每一个进程分配一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致在不同虚拟机中访问同一个类的对象会产生多份副本。

  • 线程同步机制完全失效。

因为都不是同一块内存空间,所以不管是锁对象还是锁全局类都无法保证线程同步,因为不同进程锁的不是同一个对象。

  • SharePreferences的可靠性降低。

SharePreferences不支持两个进程同时去执行写操作,否则会导致一定几率的数据丢失。这是因为SharedPreferences底层是通过读/写XML文件来实现的,并发写显然是可能出问题的,甚至并发读/写都有可能出问题。之前可以使用MODE_MULTI_PROCESS字段,但也不是很可靠,现在已经不使用了。

  • application多次初始化

当一个组件在另一个进程运行时,相当于另一个应用程序,所以在另一个进程中也将新建一个Application的实例。因此,每新建一个进程,Application的onCreate都会被调用一次。

如果在Application的onCreate中有许多初始化工作并且需要根据进程来区分,可以根据判断进程名来区分。获取进程名代码如下:

 public static String getProcessName() {
        try {
            File file = new File("/proc/" + android.os.Process.myPid() + "/" + "cmdline");
            BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));
            String processName = mBufferedReader.readLine().trim();
            mBufferedReader.close();
            return processName;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
 
 ## Application.java
String processName = getProcessName(this, android.os.Process.myPid());

if (!TextUtils.isEmpty(processName) && processName.equals(this.getPackageName())) {//判断进程名,保证只有主进程运行
	//主进程初始化逻辑
	....
}

Binder机制

从Android Framework角度来说,Binder是ServiceManager连接各种Manager(ActivityManager、WindowManager,等等)和相应Managerservice的桥梁;

从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据,这里的服务包括普通服务和基于AIDL的服务。

Android开发中,Binder主要用在Service中,包括AIDL和Messenger,其中普通Service中的Binder不涉及进程间通信,所以较为简单,无法触及Binder的核心,而Messenger的的底层其实是AIDL,所以这里选择用AIDL来分析Binder的工作机制。

AIDL是什么?

AIDL是一个缩写,全程是Android Interface Definition Language,也是android接口定义语言。准确的来说,它是用于定义客户端/服务器通信接口的一种描述语言。它其实一种IDL语言,可以拿来生成用于IPC的代码。从某种意义上说它其实是一个模板。为什么这么说?因为在我们使用中,实际起作用的并不是我们写的AIDL代码,而是系统根据它生成的一个IInterface的实例的代码。而如果大家都生成这样的几个实例,然后它们拿来比较,你会发现它们都是有套路的——都是一样的流程,一样的结构,只是根据具体的AIDL文件的不同由细微变动。所以其实AIDL就是为了避免我们一遍遍的写一些前篇一律的代码而出现的一个模板。

AIDL的使用

1.什么时候使用AIDL

使用AIDL只有在你先允许来自不同应用的客户端跨进程通信访问你的Service,并且想要在你的Service处理多线程的时候才是必要的。简单的来说,就是多个客户端,多个线程并发的情况下要使用AIDL。官方文档还指出,如果你的IPC不需要适用多个客户端,就用Binder。如果你想要IPC,但是不需要多线程,那就选择Messager。

2.建立AIDL

1、创建AIDL文件
在这里插入图片描述

新建三个文件Book.java、Book.aidl和IBookManageraidl,代码如下所示。

Book.java

public class Book implements Parcelable {

    public int bookId;
    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }


    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }

    @Override
    public int describeContents() {
        return 0;
    }

    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in) {
            return new Book(in);
        }

        @Override
        public Book[] newArray(int size) {
            return new Book[size];
        }
    };
}

Book.aidl

// Book.aidl.aidl
package com.liuguilin.multiprocesssample;

// Declare any non-default types here with import statements

parcelable Book;

IBookManager.aidl

// IBookManager.aidl
package com.liuguilin.multiprocesssample;

// Declare any non-default types here with import statements

interface IBookManager {

    List<Book>getBookList();
    void addBook(in Book book);
}

上面的三個文件,Book.java是一个表示图书信息的类,他实现了Parcelable接口,而Book.aidl是Book类在AIDL中的声明,IBookManager是我们定义的一个接口,里面有两个方法,getBookListaddBook,其中getBookList是从服务器中获取图书列表,而addBook用于往图书列表中添加一本书。

我们可以看到,尽管Book类和IBookManager位于相同的包中,但是在为IBookManager仍然要导入Book类,这就是AIDL的特殊之处。

这时候可以点击"同步"按钮,或者rebuild以下项目。

在app/build/generated/aidl_source_output_dir/debug/out/的aidl包中有一个IBookManager类,这就是我们要找的类,我们通过这个系统生成的Binder类来分析Binder的工作原理。

AIDL原理

代码如下:

/*
 * This file is auto-generated.  DO NOT MODIFY.
 */
package com.chinatsp.aidldemo;
// Declare any non-default types here with import statements

public interface IBookManager extends android.os.IInterface {
    /**
     * Default implementation for IBookManager.
     */
    public static class Default implements com.chinatsp.aidldemo.IBookManager {
        @Override
        public java.util.List<com.chinatsp.aidldemo.Book> getBookList() throws android.os.RemoteException {
            return null;
        }

        @Override
        public void addBook(com.chinatsp.aidldemo.Book book) throws android.os.RemoteException {
        }

        @Override
        public void registerCallback(com.chinatsp.aidldemo.IBookCallback cb) throws android.os.RemoteException {
        }

        @Override
        public void unregisterCallback(com.chinatsp.aidldemo.IBookCallback cb) throws android.os.RemoteException {
        }

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

    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.chinatsp.aidldemo.IBookManager {
        private static final java.lang.String DESCRIPTOR = "com.chinatsp.aidldemo.IBookManager";

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

        /**
         * Cast an IBinder object into an com.chinatsp.aidldemo.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.chinatsp.aidldemo.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.chinatsp.aidldemo.IBookManager))) {
                return ((com.chinatsp.aidldemo.IBookManager) iin);
            }
            return new com.chinatsp.aidldemo.IBookManager.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 {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_getBookList: {
                    data.enforceInterface(descriptor);
                    java.util.List<com.chinatsp.aidldemo.Book> _result = this.getBookList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(descriptor);
                    com.chinatsp.aidldemo.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.chinatsp.aidldemo.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_registerCallback: {
                    data.enforceInterface(descriptor);
                    com.chinatsp.aidldemo.IBookCallback _arg0;
                    _arg0 = com.chinatsp.aidldemo.IBookCallback.Stub.asInterface(data.readStrongBinder());
                    this.registerCallback(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_unregisterCallback: {
                    data.enforceInterface(descriptor);
                    com.chinatsp.aidldemo.IBookCallback _arg0;
                    _arg0 = com.chinatsp.aidldemo.IBookCallback.Stub.asInterface(data.readStrongBinder());
                    this.unregisterCallback(_arg0);
                    reply.writeNoException();
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements com.chinatsp.aidldemo.IBookManager {
            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.util.List<com.chinatsp.aidldemo.Book> getBookList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.chinatsp.aidldemo.Book> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        return getDefaultImpl().getBookList();
                    }
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.chinatsp.aidldemo.Book.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addBook(com.chinatsp.aidldemo.Book book) 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 ((book != null)) {
                        _data.writeInt(1);
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    boolean _status = mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().addBook(book);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void registerCallback(com.chinatsp.aidldemo.IBookCallback cb) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((cb != null)) ? (cb.asBinder()) : (null)));
                    boolean _status = mRemote.transact(Stub.TRANSACTION_registerCallback, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().registerCallback(cb);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void unregisterCallback(com.chinatsp.aidldemo.IBookCallback cb) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeStrongBinder((((cb != null)) ? (cb.asBinder()) : (null)));
                    boolean _status = mRemote.transact(Stub.TRANSACTION_unregisterCallback, _data, _reply, 0);
                    if (!_status && getDefaultImpl() != null) {
                        getDefaultImpl().unregisterCallback(cb);
                        return;
                    }
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            public static com.chinatsp.aidldemo.IBookManager sDefaultImpl;
        }

        static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
        static final int TRANSACTION_registerCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
        static final int TRANSACTION_unregisterCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);

        public static boolean setDefaultImpl(com.chinatsp.aidldemo.IBookManager impl) {
            // Only one user of this interface can use this function
            // at a time. This is a heuristic to detect if two different
            // users in the same process use this function.
            if (Stub.Proxy.sDefaultImpl != null) {
                throw new IllegalStateException("setDefaultImpl() called twice");
            }
            if (impl != null) {
                Stub.Proxy.sDefaultImpl = impl;
                return true;
            }
            return false;
        }

        public static com.chinatsp.aidldemo.IBookManager getDefaultImpl() {
            return Stub.Proxy.sDefaultImpl;
        }
    }

    public java.util.List<com.chinatsp.aidldemo.Book> getBookList() throws android.os.RemoteException;

    public void addBook(com.chinatsp.aidldemo.Book book) throws android.os.RemoteException;

    public void registerCallback(com.chinatsp.aidldemo.IBookCallback cb) throws android.os.RemoteException;

    public void unregisterCallback(com.chinatsp.aidldemo.IBookCallback cb) throws android.os.RemoteException;
}

查看代码的结构:

在这里插入图片描述
我们可以发现IBookManager本身也是一个接口,其内部代码主要分成两部分,抽象类Stub 和 原来aidl声明的getBookList()和addBook()方法,其余的两个方法先不用管。

重点在于Stub类,它是一个静态抽象类,下面我们来分析一下。从Stub类中我们可以看到是继承自Binder,并且实现了IBookManager接口。如下图:

在这里插入图片描述
Stub类基本结构如下:

  • 静态方法 asInterface(android.os.IBinder obj)
  • 静态内部类 Proxy
  • 方法 onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
  • 方法 asBinder()
  • private的String类型常量DESCRIPTOR
  • private的int类型常量TRANSACTION_getBookList
  • private的int类型常量TRANSACTION_addBook
  • DESCRIPTOR

Binder的唯一标识,一般用当前Binder的类名表示,比如本例子中的“com.liuguil.multiProcess.IBookManager”

  • asInterface(android.os.IBinder obj)

用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这种转换过程是区分进程的,如果客户端和服务端位于同一进程,那么此方法返回的就是服务端的Stub对象本身,否则返回的是系统封装后的Stub.proxy

  • asBinder

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

  • onTransact

这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过系统底层封装后交由此方法来处理。该方法的原型为public Boolean onTransact (int code,android.os.Pareel data,android.os.Pareel reply,int fiags),服务端通过code可以确定客户端所请求的目标方法是什么,接着从data中取出目标方法所需要的参数(如果目标方法中有参数的话),然后执行目标方法,当目标方法执行完毕后,就向reply中写入返回值(如果目标方法有返回值的话),onTransact方法的执行过程就是这样的。需要注意的是,如果此方法返回false,那么客户端的请求会失败,因此我们可以利用这个特性来做权限验证,毕竟我们也不希望随便一个进程都能远程调用我们的服务。

静态类Stub.Proxy的结构如下:
在这里插入图片描述

1、Proxy 实现了 com.gebilaolitou.android.aidl.IBookManager接口,所以他内部有IBookManagaer接口的两个抽象方法
2、Proxy的asBinder()方法返回的mRemote,而这个mRemote是什么时候被赋值的?是在构造函数里面被赋值的。

  • Proxy#getBookList

这个方法运行在客户端,当客户端远程调用此方法时,它的内部实现是这样的:首先创建该方法所需要的输入型Parcel对象_data、输出型Parcel对象_reply和返回值对象List然后把该方法的参数信息写入_data中(如果有参数的话):接着调用transact方法来发起RPC(远程过程调用)请求,同时当前线程挂起;然后服务端的onTransact方法会被调用。直到RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果;最后返回_reply中的数据。

1.通过阅读静态类Stub.Proxy的getBookList()方法,我们容易分析出来里面的两个android.os.Parcel_data和_reply是用来进行跨进程传输的"载体"。而且通过字面的意思,很容易猜到,_data用来存储 客户端流向服务端 的数据,_reply用来存储 服务端流向客户端 的数据。
2、通过mRemote. transact()方法,将_data和_reply传过去
3、通过_reply.readException()来读取服务端执行方法的结果。
4、最后通过finally回收l_data和_reply

  • Proxy#addBook

这个方法路行在客户端,它的执行过程和getBookList是一样的,addBook没有返回值,所以它不需要从.reply中取出返回值。

关于 transact()方法:这是客户端和和服务端通信的核心方法,也是IMyAidlInterface.Stub继承android.os.Binder而重写的一个方法。调起这个方法之后,客户端将会挂起当前线程,等候服务端执行完相关任务后,通知并接收返回的_reply数据流。关于这个方法的传参,有注意两点:

1 方法ID:transact()方法第一个参数是一个方法ID,这个是客户端和服务端约定好的给方法的编码,彼此一一对应。在AIDL文件转话为.java时候,系统会自动给AIDL里面的每一个方法自动分配一个方法ID。而这个ID就是咱们说的常量TRANSACTION_getBookList和TRANSACTION_addBook这些常量生成了递增的ID,是根据你在aidl文件的方法顺序而来,然后在IBookManager.Stub中的onTransact()方法里面switch根据第一个参数code即我们说的ID而来。

2最后的一个参数:transact()方法最后一个参数是一个int值,代表是单向的还是双向的。具体大家请参考我们前面的文章Android跨进程通信IPC之5——Binder的三大接口中关于IBinder部分。我这里直接说结论:0表示双向流通,即_reply可以正常的携带数据回来。如果为1的话,那么数据只能单向流程,从服务端回来的数据_reply不携带任何数据。注意:AIDL生成的.java文件,这个参数均为0。

AIDL流程分析(IBookManager的流程分析)

1.客户端流程

BookManagerActivity.java


public class BookManagerActivity extends AppCompatActivity {

    private static final String TAG = "BookManagerActivity";

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "list Type :" + list.getClass());
                getCanonicalName();
                Log.i(TAG, "list string : " + list.toString());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private void getCanonicalName() {
    }

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

        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();

        unbindService(mConnection);
    }
}


1.1、获取IBookManager对象

客户端中通过Intent去绑定一个服务端的Service。在 onServiceConnected(ComponentName name, IBinder service)方法中通过返回service可以得到AIDL接口的实例。这是调用了asInterface(android.os.IBinder) 方法完成的。
1.2、asInterface(android.os.IBinder)方法

 public static com.cmnit.ipc.IBookManager asInterface(android.os.IBinder obj)
    {
      if ((obj==null)) {
        return null;
      }
      android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
      if (((iin!=null)&&(iin instanceof com.cmnit.ipc.IBookManager))) {
        return ((com.cmnit.ipc.IBookManager)iin);
      }
      return new com.cmnit.ipc.IBookManager.Stub.Proxy(obj);
    }

asInterface(android.os.IBinder)我们知道是调用的new com.gebilaolitou.android.aidl.IBookManager.Stub.Proxy(obj)构造的一个Proxy对象。

所以可以这么说在Activity中获取的IBookManager其实是一个IBookManager.Stub.Proxy对象。

1.3 调用getBookList方法

上文我们说过了Activity类中的IBookManager其实IBookManager.Stub.Proxy对象,所以调用getBookList()方法其实是IBookManager.Stub.ProxygetBookList()方法。代码如下:

@Override public java.util.List<com.cmnit.ipc.aidl_ipc.bean.Book> getBookList() throws android.os.RemoteException
      {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        java.util.List<com.cmnit.ipc.aidl_ipc.bean.Book> _result;
        try {
          _data.writeInterfaceToken(DESCRIPTOR);
          boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
          if (!_status && getDefaultImpl() != null) {
            return getDefaultImpl().getBookList();
          }
          _reply.readException();
          _result = _reply.createTypedArrayList(com.cmnit.ipc.aidl_ipc.bean.Book.CREATOR);
        }
        finally {
          _reply.recycle();
          _data.recycle();
        }
        return _result;
      }
  • 1、这里面主要是生成了_data和_reply数据流,并向_data中存入客户端的数据。
  • 2、通过 transact()方法将他们传递给服务端,并请求服务指定的方法
  • 3、接收_reply数据,并且从中读取服务端传回的数据。

通过上面客户端的所有行为,我们会发现,其实通过ServiceConnection类中onServiceConnected(ComponentName name, IBinder service)中第二个参数service很重要,因为我们最后是调用它的transact() 方法,将客户端的数据和请求发送给服务端去。从这个角度来看,这个service就像是服务端在客户端的代理一样,而IBookManager.Stub.Proxy对象更像一个二级代理,我们在外部通过调用这个二级代理来间接调用service这个一级代理。

2.服务端流程

1.远程服务端Service的实现
BookManagerService.java

public class BookManagerService extends Service {
    private static final String TAG = BookManagerService.class.getSimpleName();
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();
    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBookList.add(book);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        mBookList.add(new Book(1,"Android"));
        mBookList.add(new Book(2,"Ios"));
    }

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

上面是一个服务端Service的典型实现,首先在onCreate中初始化添加了两本图书的信息,然后创建了一个Binder对象并在onBind中返回它,这个对象继承自IBookManager.Stub并且实现了它内部的AIDL方法。

注意这里采用了CopyOnWriteArrayList,这个CopyOnWriteArrayList支持并发读/写。在前面我们提到,AIDL方法是在服务端的Binder线程池中执行的,因此当多个客户端同时连接的时候,会存在多个线程同时访问的情形,所以我们要在AIDL方法中处理线程同步,而我们直接使用CopyonWriteArayList来进行自动的线程同步。

前面我们提到,AIDL中能够使用的List只有ArrayList,但是我们这里却使用了CopyOnWriteArrayList,注意它不是继承ArrayList,为什么能够正常工作呢?这是因为AIDL中所支持的是抽象的List,而List只是一个接口,因此虽然服务端返回的是CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端。所以,我们在服务端采用CopyOnWriteArrayList是完全可以的。和此类似的还有ConcurrentHashMap,你可以体会一下这种转换情形,然后我们需要在清单文件中注册一下。

<service
     android:name=".BookManagerService"
     android:process=":remote" />

此时BookManagerService组件将运行在单独的进程中。

  • 2.1 获取IBookManager.Stub的transact()方法
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
      java.lang.String descriptor = DESCRIPTOR;
      switch (code)
      {
        case INTERFACE_TRANSACTION:
        {
          reply.writeString(descriptor);
          return true;
        }
        case TRANSACTION_getBookList:
        {
          data.enforceInterface(descriptor);
          java.util.List<com.cmnit.ipc.aidl_ipc.bean.Book> _result = this.getBookList();
          reply.writeNoException();
          reply.writeTypedList(_result);
          return true;
        }
        case TRANSACTION_addBook:
        {
          data.enforceInterface(descriptor);
          com.cmnit.ipc.aidl_ipc.bean.Book _arg0;
          if ((0!=data.readInt())) {
            _arg0 = com.cmnit.ipc.aidl_ipc.bean.Book.CREATOR.createFromParcel(data);
          }
          else {
            _arg0 = null;
          }
          this.addBook(_arg0);
          reply.writeNoException();
          return true;
        }
        default:
        {
          return super.onTransact(code, data, reply, flags);
        }
      }
    }

可以看到,它在收到客户端的 transact()方法后,直接调用了switch选择,根据ID执行不同操作,因为我们知道是调用的getBookList()方法,所以对应的code是TRANSACTION_getBookList,所以我们下case TRANSACTION_getBookList:的内容,如下:

 case TRANSACTION_getBookList:
        {
          data.enforceInterface(descriptor);
          java.util.List<com.cmnit.ipc.aidl_ipc.bean.Book> _result = this.getBookList();
          reply.writeNoException();
          reply.writeTypedList(_result);
          return true;
        }

这里面十分简单了,就是直接调用服务端的getBookList()方法。

通过上面分析,读者应该已经理解到Binder工作机制,但是有两点还是需要额外说明一下,首先,当客户端发起远程请求时,由于当前线程会被挂起直至服务器进程返回数据,所以如果一个远程方法是很耗时的,那么不能再UI线程中发起此远程请求,其次,由于服务器的Binder方法运行在Binder的线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程中了。为了更好的说明Binder,下面给出一个Binder的工作机制图:
在这里插入图片描述
设计的类图如下:
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值