Android Binder机制AIDL实例介绍

相关文章推荐:

Android 多进程的基础使用及优缺点

 

前言

我们知道,Android的底层是使用Linux内核运行的,而Linux为了保证系统的稳定性,使用的是进程隔离的机制,也就是让不同的进程运行在不同的虚拟空间,使得不同进程之间无法共享数据,防止数据的篡改。关于多进程带下进行开发的介绍上面链接中已经讲的很清楚了。但是,有时候我们也会遇到不同进程间需要通信的情况,那么,这时候就需要使用Binder机制来建立通讯。所以,Binder是Android系统进程间通信(IPC)方式之一,用于跨进程间的方法调用。

但是,安卓中已经有非常多的通信模型了,比如管道、报文队列、插口等等。

那为什么还要选择Binder呢?

因为高效。Binder实现通信的时候,只需要拷贝1次数据,而其他的比如socket需要拷贝2次,这样也节省了手机内存。

一、什么是Binder机制?

刚刚说到了Binder机制是用来实现多进程间通讯的,解决了Linux使用进程隔离机制所带来的问题。那么,我们一定很好奇,这个Binder机制到底是到底是什么呢?从字面上理解binder是"粘结剂"的意思,那么google的工程师为什会以"粘结剂"来命名binder呢?这是因为binder是基于一种Client-Server通信方式,也就是我们所说的CS架构,在这个架构中存在着4个组件,分别是Client、Server、Service Manager和Binder驱动程序,binder这个"粘结剂"正是将这四个组件连接在一起。其中Client、Server运行在两个不同的进程,他们进行跨进程调用需要binder驱动作基础设施,Service Manager管理binder Server。

提到机制两字,大家不觉得熟悉吗? 对,没错,就是 “handler机制”,它用来实现多线程之间通讯的,这个我们并不陌生。由handle对象、message对象、messagequeen对象和looper对象,这四个对象组成,相互配合、不同分工,共同完成了一场多线程通讯的精彩把戏。   

说到这,应该会有人会明白我所表达的意思了。所以说,Binder机制其实和Handler机制一样,其实也是一场由四个角色对象合作共同完成的一场通讯把戏,不同是,它比handle更牛逼一点,它是在多进程情况下完成的通讯。好了,下面用热烈的掌声欢迎Binder剧组的成员们出场吧。

  • server:提供服务的对象 ;
  • client:需要获取服务的对象;
  • serviceManager:服务的管理者,本质上也是一个server;
  • Binder驱动:C/S 联系的中介

一,服务注册。每个情报员都通过对接暗号确认身份。即通过Binder驱动将自身注册到serviceManager中。

二,Binder驱动和Server通信。Binder驱动为了和server进行通信,它做了两件事: 

  1. 为每一个server在内核空间里创建一个binder节点,为每一个节点分配一个大于0的句柄。
  2. 把server的名字和句柄一起发送到serviceManager中。即服务注册。

三,Binder驱动和client通信。client要想和server通信,首先要知道server的名字。在知道了服务的名字以后,client会把这个名字和一个句柄为0的值封装成一个数据包,发送给Binder驱动;Binder驱动一看,这个句柄为0,它就会把这个数据包发送给serviceManager。serviceManager在接收到这个数据包以后,就会根据名字来寻找它里面有没有client想要的server,如果有,那么它就会把这个server的大于0的句柄发送给Binder驱动。Binder驱动再把这个句柄发送给client,这样client就拿到了目标server的句柄。最后,client就用这个目标server的句柄外加server的名字,再封装成一个数据包,发送给Binder驱动,驱动再发送给serviceManager,这样就可以找到对应的server实现通信了。

小结:

  • 所有的server注册到serviceManager中; 
  • Binder驱动为server创建节点,分配句柄; 
  • client想要什么服务就通过Binder驱动拿到对应服务的句柄,用句柄去serviceManager中去寻找对应的服务,当然获取句柄,获取服务都是要通过Binder驱动的,client不能直接对server进行访问,因为Linux内核有进程隔离机制啊,这样应该很清楚了吧。

二、AIDL实例初步了解binder机制

假设一个场景:在图书馆,有两个进程。A进程是图书管理员,B进程向A进程添加图书。当B进程收到A进程添加的图书时,通知A进程,图书已经添加成功。了解场景之后,我们通过aidl来实现这个需求。下文贴出binder服务端与客户端的代码。


1、binder服务端
使用一个远程Service模拟进程A,提供binder服务端。

package com.shine.binderdemo;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteException;

import java.util.concurrent.CopyOnWriteArrayList;

public class MyService extends Service {

    private CopyOnWriteArrayList<Book> booklist;
    private IOnBookAddListener mListener;

    private IBinder mBinder = new IBookManager.Stub() {

        @Override
        public void addBook(Book book) throws RemoteException {
            booklist.add(book);
            if (mListener != null) {
                mListener.onBookadd(book);
            }
        }

        @Override
        public void registerListener(IOnBookAddListener listener) throws RemoteException {
            mListener = listener;
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        booklist = new CopyOnWriteArrayList();
    }


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

在AndroidManifest.xml中通过process属性使MyService 运行在其他进程。
有一点我们需要格外注意:我们看到在用来存放添加书籍的booklist是一个CopyOnWriteArrayList。为什么在这要用CopyOnWriteArrayList呢?这是因为binder服务端在接收到客户端的请求访问时都会在一个单独的线程中处理这个请求,所以会出现线程安全问题,在这个地方通过CopyOnWriteArrayList来避免出现的线程安全问题,这个过程在子线程中完成也可以通过客户端的代码来验证。


2、binder客户端
在activity中绑定服务来模拟进程B,通过binder跨进程调用A的添加图书的方法,实现binder客户端。

package com.shine.binderdemo;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    private IBookManager mBookManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, MyService.class);
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    public void add(View view) {
        Book book = new Book();
        book.setBookId(1);
        book.setBookName("《第一行代码》");
        try {
            mBookManager.addBook(book);
        } catch (RemoteException e) {
            e.printStackTrace();
        }

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mServiceConnection);
    }

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBookManager = IBookManager.Stub.asInterface(service);
            try {
                mBookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private IOnBookAddListener mListener = new IOnBookAddListener.Stub() {
        @Override
        public void onBookadd(final Book book) throws RemoteException {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, book.toString(), Toast.LENGTH_SHORT).show();
                }
            });
        }

    };
}

首先来验证服务端中mBinder 的addBook(Book book)方法确实是在一个子线程中进行的。

private IOnBookAddListener mListener = new IOnBookAddListener.Stub() {
        @Override
        public void onBookadd(final Book book) throws RemoteException {
            //方法回调在子线程中
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, book.toString(), Toast.LENGTH_SHORT).show();
                }
            });
        }

    };

在onBookadd(final Book book) 回调中通过runOnUiThread(new Runnable())将线程切回主线程然后才能Toast提示。这也从侧面证明了binder服务端处理客户端的请求时会在一个子线程中处理。这里我们还有一个地方要注意,虽然binder服务会在一个子线程中处理客户端的请求,但是客户端请求时却不会新开一个线程,从上面的代码我们可能还看不出什么,如果将服务端的添加图书的代码设置为耗时操作,运行程序,点击添加图书可能就会出现ANR。(这里就不验证了)所以在确定服务端处理请求时是耗时的操作的时候。有必要新开一个线程去请求。
上面说了这么多,总结一句话:binder服务在处理客户端的请求时是在一个独立的线程中完成的,而客户端请求处理,不会新开一个线程,如果是耗时操作,则可能出现ANR。

三、分析Binder如何来进行跨进程间的调用?

首先来看,在activity中点击添加图书会调用add(View view),然后调用mBookManager.addBook(book);就能成功的往MyService的bookList中添加一本书,这是为什么呢?我们的activity和service明明处于两个进程,确好像在同一个进程中直接持有了服务端实现addBook(Book book)方法的mBinder 的引用,通过这个引用就调用了MyService 所在进程的方法。要解释这个问题,我们自然而然的要分析activity中的mBookManager是怎么被赋值的:

private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //mBookManager 在这里赋值
            mBookManager = IBookManager.Stub.asInterface(service);
            try {
                mBookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

看到这里mBookManager = IBookManager.Stub.asInterface(service);我们可能会糊涂,这个IBookManager.Stub到底是个什么东西,为什么调用它的asInterface(service)方法就能得到mBinder 的引用?接下来我们就来分析IBookManager.Stub是个啥,它的asInterface(service)到底有什么奥秘。

package com.shine.binderdemo;

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

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

        /**
         * Cast an IBinder object into an com.shine.binderdemo.IBookManager interface,
         * generating a proxy if needed.
         */
        public static com.shine.binderdemo.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.shine.binderdemo.IBookManager))) {
                return ((com.shine.binderdemo.IBookManager) iin);
            }
            return new com.shine.binderdemo.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 {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shine.binderdemo.Book _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.shine.binderdemo.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addBook(_arg0);
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_registerListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shine.binderdemo.IOnBookAddListener _arg0;
                    _arg0 = com.shine.binderdemo.IOnBookAddListener.Stub.asInterface(data.readStrongBinder());
                    this.registerListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.shine.binderdemo.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 void addBook(com.shine.binderdemo.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);
                    }
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

            @Override
            public void registerListener(com.shine.binderdemo.IOnBookAddListener listener) 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((((listener != null)) ? (listener.asBinder()) : (null)));
                    mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public void addBook(com.shine.binderdemo.Book book) throws android.os.RemoteException;

    public void registerListener(com.shine.binderdemo.IOnBookAddListener listener) throws android.os.RemoteException;
}

上面直接贴出了IBookManager.aidl编译之后生成的IBookManager.java的代码。这个代码初看之下很长,不容易读懂,下面我贴一张as里面关于这个类的结构图。

通过这个结构图大概可以看出Stub是IBookManager的一个内部类,有一个asInterface(android.os.IBinder obj)方法,这也是上面activity中给mBookManager 赋值中用到的一个方法,我们待会再来分析。Stub内部又有一个内部类Proxy ,从名字上来看它应该是一个代理类,那么它到底代理的那个类呢?

public static com.shine.binderdemo.IBookManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.shine.binderdemo.IBookManager))) {
                return ((com.shine.binderdemo.IBookManager) iin);
            }
            //将IBinder类型的obj通过构造方法传入Proxy
            return new com.shine.binderdemo.IBookManager.Stub.Proxy(obj);
 }

Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

看完上面两段代码应该可以看出Proxy代理的就是一个IBinder类型的obj,到这里我们明白了mBookManager原来就是一个Proxy代理类。熟悉代理模式的话,我们知道代理类只是持有一个真实类的引用,真正功能都是由这个真实类实现的。在这个IBookManager.Stub.Proxy里面,真实类是什么呢?

private static class Proxy implements com.shine.binderdemo.IBookManager {
            //mRemote是这个代理中的真实对象
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

           ......

            @Override
            public void addBook(com.shine.binderdemo.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);
                    }
                    //mBookManager的addBook(Book book)方法实际上是调用 mRemote.transact(...)方法
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
           ......
}

这段代码的注释已经很明确的IBinder类型的mRemote是这个类的真实引用。
mBookManager.addBook(Book book)方法最后会调用mRemote.transact(...)方法,那么这个mRemote是个啥呢?mRemote是如何传入Proxy呢?
我们在之前的binder机制的概念时说过,binder机制涉及到4个组件,binder server ,binder client,binder内核驱动,Service Manager。在上面的情景中我们只分析了binder server 和binder client,接下来binder内核驱动(对于binder驱动,在下面只会提到它的作用,不涉及具体的代码,具体的代码分析我也不懂)就要出场了,而对于Service Manager,这个例子中却不会直接涉及,在activity的启动过程中会出现,到时候再分析,敬请期待。。。
有了binder驱动介入,就可以解决mRemote到底是个啥了。先看下MyService的onBinder(...)方法

@Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }
private IBinder mBinder = new IBookManager.Stub() {

        @Override
        public void addBook(Book book) throws RemoteException {
            booklist.add(book);
            if (mListener != null) {
                mListener.onBookadd(book);
            }
        }

        @Override
        public void registerListener(IOnBookAddListener listener) throws RemoteException {
            mListener = listener;
        }
    };

这个mBinder是一个IBookManager.Stub()类型的变量,而IBookManager.Stub()继承Binder,所以mBinder是一个Binder类型的对象。这个binder类型的对象实际上就是binder服务端,在binder服务端开启的时候,同时会在binder内核建一个mRemote的binder对象,我们在上面提到的mRemote其实就是binder内核里面的mRemote binder对象。实际在binder进程间调用的时候必须要考虑的问题就是如何获取binder内核mRemote 对象。这个例子采用的是Service做binder服务端,而binderService中google的工程师已经为我们实现好了。在ServiceConnection里面有个回调方法可以获取binder内核的mRemote,如下:

private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //通过获取到的service(mRemote)生成IBookManager.Stub.Proxy对象
            mBookManager = IBookManager.Stub.asInterface(service);
            try {
                mBookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

这样我们就获取到了binder内核的mRemote对象,同时也传入了IBookManager.Stub.Proxy,接着就可以使用mRemote来进行跨线程的调用了。
接下来看下mBookManager到底是怎样实现addBook(Book,book)方法的,上面已经分析了会调用到IBookManager.Stub.Proxy.addBook(Book book)。

@Override
            public void addBook(com.shine.binderdemo.Book book) throws android.os.RemoteException {
                 //_data表示发送binder服务端的数据,这些数据需要通过Parcel (包裹)进行传递
                android.os.Parcel _data = android.os.Parcel.obtain();
               //_reply 表示binder服务端相应的数据,这些数据同样需要通过Parcel (包裹)进行传递
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                   //用来做安全检查,binder服务端会处理这个数据
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((book != null)) {
                        _data.writeInt(1);
                        //Parcel (包裹)不仅可以传递基本类型的数据还可以传递对象,但是对象必须实现Parcelable接口
                        book.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                     //调用binder内核的mRemote对象往binder服务端发送信息
                    mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }

代码中注释已经很详细了,就不解释了,接着看下面,我们说过mRemote实际上是一个binder类型的对象, mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);就会调用到Binder的transact(...)方法中

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);
        }
        //调用binder服务端的onTransact(...)中
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

其中transact(...)有四个参数,分别是 code,data,reply,flag。
code:整形的一个识别码,客户端传入,用于区分服务端执行哪个方法。
data:客户端传入的Parcel类型的参数。
reply:服务端返回的结果值,也是Parcel类型数据。
flag:整形的一个标记,用于标记是否是否有返回值,0表示有返回值,1表示没有。
接着再次涉及到binder内核驱动,具体的细节我也不太懂,直接的结论是流程会进入到binder服务端的IBookManager.Stub的onTransact(...)中:

@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;
                }
                //根据transact第一个参数来确定请求的是哪个方法
                case TRANSACTION_addBook: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shine.binderdemo.Book _arg0;
                    if ((0 != data.readInt())) {//如果客户端传递是有非基本类型的数据从data中取出
                        _arg0 = com.shine.binderdemo.Book.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    //取出来的数据传到MyService的mBinder的addBook(...)
                    this.addBook(_arg0);
                    //由于addBook(...)没有返回值,所以不需要通过reply返回结果
                    reply.writeNoException();
                    return true;
                }
                case TRANSACTION_registerListener: {
                    data.enforceInterface(DESCRIPTOR);
                    com.shine.binderdemo.IOnBookAddListener _arg0;
                    _arg0 = com.shine.binderdemo.IOnBookAddListener.Stub.asInterface(data.readStrongBinder());
                    this.registerListener(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

通过上面从activity的addBool(Book book)开始,我们一步一步的分析了binder客户端如何调用binder服务端的方法,这个过程在binder内核驱动的基础上实现,让我们感觉好像是调用本地(同一个进程)方法一样,实质上底层为我们做了大量的工作。这样基于aidl实现的Binder跨进程调用就大概谈完了,对binder跨进程调用也应该有了一定的了解。
#######binder小结
跨进程调用的关键点在于如何获得服务端的binder对象在内核里面的引用(如上面分析的mRemote)。
一般来说有两种途径,其一是通过Service Manager,这篇文章没有直接涉及Service Manager,但是在底层源码里面这种情况很常见,在Activity启动过程中用到的ActivityManagerService就是通过这种方式在客户端得到服务端的binder对象在内核里面的引用,我们以后再分析。其二是通过已经建立好的binder连接来获取这个引用。如上面的例子中用到的一样。

private IOnBookAddListener mListener = new IOnBookAddListener.Stub() {
        @Override
        public void onBookadd(final Book book) throws RemoteException {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(MainActivity.this, book.toString(), Toast.LENGTH_SHORT).show();
                }
            });
        }

    };

这是一个实现了接口方法的Binder服务,通过已经建立好的binde连接mBookManager传递给Myservice所在进程。

private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBookManager = IBookManager.Stub.asInterface(service);
            try {
                //通过已经建立好的连接传送服务端binder的引用
                mBookManager.registerListener(mListener);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

有一点需要注意,在此时activity所在的进程就成为了我们通常所说的binder服务端,而MyService则是binder客户端。这就是第二种获取binder服务引用的方式。再多谈一点,其实在这个例子中,两个建立的binder连接都是通过已将建立好的连接来传递的,除了mListener 这个binder引用的获得,mBinder也是这种情况。这里就不再详细讨论了,如果感兴趣可以学习一下bindService的源码,就肯定能发现这一点。最后贴上一张例子用到的uml类图:

四、总结

这篇文章是学习技术以来的第一篇文章,很久以前就在谋划了,但是由于一些事情和水平的关系拖了很久,但是总算是写完了。在写作的过程中还是遇到了很多困难的,有时候知道的东西你不一定能写出来,需要的知识也不在一个层面。有些知识可能以为懂了,但是真正到了写的时候,还是发现不会。我觉得写技术博客是提升技术的一个很好的途径,希望以后我能保持。对binder有了概念之后,接下来会继续把Activity启动过程及界面绘制流程解析写完。先前的打算是在这篇文章里面再介绍一下handler相关知识的,毕竟在activity启动的过程中handler也同样重要,但是写binder的时候,发现知识还是有很多缺陷,只能留待下一次了。



——————
参考链接:https://www.jianshu.com/p/c98c6ca47c4d
 

猜你喜欢

转载自blog.csdn.net/csdn_aiyang/article/details/81592394