欢迎访问我的个人博客,原文链接:http://wensibo.top/2017/07/03/Binder/ ,未经允许不得转载!
前言
大家好,好久不见,距离上篇文章已经有35天之久了,因为身体不舒服害了一场病,不过现在已经好多了;另外这个月是考试月,当然得花多点时间复习功课了;再者这段时间依旧在看书,同时也在研究Android源码,准备了不少干货想与大家一起分享。7月刚到,该放假的也都差不多放假了,该实习的也已经在实习了,而我。。。还是准备秋招吧!多看书多打码多提升自己的眼界。
今天想要和大家一起分享的是Android中的Binder机制,讲真这绝对是Android中很深奥的一个点,如果能够彻底弄懂它,这对初级程序员来说绝对会是一件具有里程碑意义的事件,当然我也研究了许久,终于琢磨出点所以然,所以就拿出来和大家一起分享分享。另外这篇文章将会通过一个小实例来讲解Binder,大家可以点击这里,也欢迎大家fork和star。话不多说让我们开始吧!
IPC
为了弄懂IPC的来龙去脉,我将从以下三个方面为大家来讲解,希望对大家理解IPC会有帮助
什么是IPC
IPC是Inter Process Communication
的缩写,其意思就是进程间的通信,也就是两个进程之间的通信过程。我们都知道在Android系统中,每个应用都运行在一个进程上,具有自己的DVM实例,而且进程之间是相互隔离的,也就是说各个进程之间的数据是互相独立,互不影响的,而如果一个进程崩溃了,也不会影响到另一个进程。
采取这样的设计是有一定道理的,例如这样的前提下将互相不影响的系统功能分拆到不同的进程里面去,有助于提升系统的稳定性,毕竟我们都不想自己的应用进程崩溃会导致整个手机系统的崩溃。
进程之间隔离是不错的选择,可是如果进程之间想要互相通信,进行数据交互的时候那该怎么办呢?例如我们在自己的应用中想要访问手机通讯录中的联系人,很显然这是两个不同的进程,如果Android没有提供一种进程之间交流的机制,那么这种功能将无法实现。
不过由于Android系统使用的是Linux内核,而在Linux系统中进程之间的交互是有一套机制的,所以Android也借鉴了其中的一些机制,从而形成了Android的IPC机制。
上面只是粗略的讲解了IPC是啥,关于它的使用和原理我将一一为大家呈上。
为什么要用IPC
上一点中我举了访问手机通讯录的例子。但你可能觉得我不需要用到这种功能,那么我就不用管IPC啦!其实不然,IPC在我们的应用开发过程中随处可见,下面我将举一个例子来说明他的重要性。
我们在MainActivity中修改一个静态变量,接着在另一个进程的SecondActivity中去访问该变量,看看能否读取已经修改过的变量。
1、新建一个Student类,并声明一个静态变量
public class Student { public static String name="BOB"; }
2、在MainActivity的onCreate方法中修改name的值,并打印log
@Overrideprotected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Student.name = "JACK"; Log.d("MainActivity:Sname=", Student.name); }
3、将SecondActivity设置为新进程,并在其onCreate方法中访问name
<!-- 在清单文件中通过android:process属性为SecondActivity指定特定的进程:com.bob.aidltest:second --><activity android:name=".SecondActivity" android:process=":second"></activity>
public class SecondActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.second_activity); Log.d("SecondActivity:Sname=" , Student.name); } }
4、通过log,可以看到在MainActivity中修改了name的值,但是在SecondActivity中却无法读取修改后的值
通过以上的实验,大家应该明白了一点:在不同的进程之间访问同一个静态变量是行不通的。其原因是:每一个进程都分配有一个独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间,这就导致在不同的虚拟机上访问同一个对象会产生多个副本。例如我们在MainActivity中访问的name的值只会影响当前进程,而对其他进程不会造成影响,所以在SecondActivity中访问name时依旧只能访问自己进程中的副本。
Android中解决IPC的方法
上面也讲到,为了解决这些跨进程的问题,Android沿用了一些Linux的进程管理机制,使得进程之间能够进行交互,下面我将列出一些常见的IPC方式,需要指出的是本文主要讲解Binder机制,所以会注重讲解AIDL,其他方式请读者自行查阅相关资料。
名称 | 特点 | 使用场景 |
---|---|---|
Bundle | 只能传输实现了Serializable或者Parcelable接口或者一些Android支持的特殊对象 | 适合用于四大组件之间的进程交互 |
文件 | 不能做到进程间的即时通信,并且不适合用于高并发的场景 | 适合用于SharedPreference以及IO操作 |
ContentProvider | 可以访问较多的数据,支持一对多的高并发访问,因为ContentProvider已经自动做好了关于并发的机制 | 适合用于一对多的数据共享并且需要对数据进行频繁的CRUD操作 |
Socket | 通过网络传输字节流,支持一对多的实时通信,但是实现起来比较复杂 | 适合用于网络数据共享 |
Messenger | 底层原理是AIDL,只是对其做了封装,但是不能很好的处理高并发的场景,并且传输的数据只能支持Bundle类型 | 低并发的一对多的即时通信 |
AIDL | 功能强大,使用Binder机制(接下来会讲解),支持一对多的高并发实时通信,但是需要处理好线程同步 | 一对多并且有远程进程通信的场景 |
Binder
终于来到这篇文章的重头戏了,上面讲到Android解决IPC的方法中有一种是AIDL,它使用的原理就是Binder,只有理解了Binder,我们才算是理解了Android跨进程通信的原理。在这里我会带大家看看Android中有哪一些重要的地方使用到了Binder,接着我们会通过一个实例来了解如何使用Binder,最后我们会分析Binder的源码来理解他的工作流程。
Binder在Android中的运用
说起Binder在Android的使用场景,可以说是无处不在,我列出一些最常见的场景:
四大组件的生命周期都是使用Binder机制进行管理的
View的工作原理也使用了Binder
WindowManager的工作机制同样使用了Binder
以上三个方面只是最常见的场景,但是却几乎包括了我们开发的整个流程。我们开发的应用都离不开四大组件,而四大组件也正是依靠Binder机制运行的;对于我们最常见的View,他是如何显示的,View又是如何响应我们的动作的,这其中也用到了Binder(关于这些内容我会在后续的文章中为大家分析)。可以说了解Binder对于我们的开发是很有帮助的,那接下来我们就来看看我们该如何使用Binder进行进程间的通信吧!
如何使用Binder
现在我们需要实现这样的功能:客户端与服务端位于不同的进程,客户端需要向服务端添加学生,同时客户端还可以向服务端发起查询学生列表的请求。
1、创建Student.java,Student.aidl,IStudentManager.aidl
Student.java
package com.bob.aidltest.aidl;import android.os.Parcel;import android.os.Parcelable;/** * Created by bob on 17-7-3. * 所有需要在Binder传递的数据类型都需要实现Parcelable接口 */public class Student implements Parcelable{ public static String name="BOB"; public int s_id; public String s_name; public String s_gender; public Student(Parcel in) { s_id = in.readInt(); s_name = in.readString(); s_gender = in.readString(); } public Student(int s_id, String s_name, String s_gender) { this.s_id = s_id; this.s_name = s_name; this.s_gender = s_gender; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(s_id); dest.writeString(s_name); dest.writeString(s_gender); } public static final Creator<Student> CREATOR = new Creator<Student>() { @Override public Student createFromParcel(Parcel in) { return new Student(in); } @Override public Student[] newArray(int size) { return new Student[size]; } }; @Override public String toString() { return String.format("[StudentID: %s , StudentName: %s , StudentGender: %s]", s_id, s_name, s_gender); } }
Student.aidl
// Student1.aidlpackage com.bob.aidltest.aidl;parcelable Student;
IStudentManager.aidl
// IStudentManager.aidlpackage com.bob.aidltest.aidl;import com.bob.aidltest.aidl.Student;interface IStudentManager { List<Student> getStudentList(); void addStudent(in Student student); }
创建完毕之后手动编译项目(Build-->ReBuild Project
),接着就会在app/build/generated/source/aidl/debug/com/bob/aidltest/aidl/IStudentManager.java
中看到自动生成的IStudentManager
接口,如下图:
2、分析IStudentManager.java
先来看看自动生成的代码:
public interface IStudentManager extends android.os.IInterface{ /** 内部类Stub,继承自Binder并且实现了IStudentManager接口,因此他也是一个Binder对象,这个内部类是需要在服务端手动实现的,并且会通过onBind方法返回给客户端 */ public static abstract class Stub extends android.os.Binder implements com.bob.aidltest.aidl.IStudentManager { private static final java.lang.String DESCRIPTOR = "com.bob.aidltest.aidl.IStudentManager"; /** 构造方法 */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * 将服务端的Binder对象转换为客户端的所需的AIDL接口类型的对象,客户端拿到这个对象就可以通过这个对象远程访问服务端的方法 */ public static com.bob.aidltest.aidl.IStudentManager asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.bob.aidltest.aidl.IStudentManager))) { return ((com.bob.aidltest.aidl.IStudentManager)iin); } return new com.bob.aidltest.aidl.IStudentManager.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } /** * 运行在服务端进程的Binder线程池中;当客户端进程发起远程请求时,远程请求会要求系统底层执行回调该方法 * @param code 客户端进程请求方法标识符。服务端进程会根据该标识确定所请求的目标方法 * @param data 目标方法的参数,他是客户端进程传进来的,当我们调用addStudent(Student student)方法时,参数就是Student对象 * @param reply 目标方法执行后的结果,将会返回给客户端,例如当我们调用getStudentList,返回的就是一个Student的列表 */ @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_getStudentList: { data.enforceInterface(DESCRIPTOR); java.util.List<com.bob.aidltest.aidl.Student> _result = this.getStudentList(); reply.writeNoException(); reply.writeTypedList(_result); return true; } case TRANSACTION_addStudent: { data.enforceInterface(DESCRIPTOR); com.bob.aidltest.aidl.Student _arg0; if ((0!=data.readInt())) { _arg0 = com.bob.aidltest.aidl.Student.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addStudent(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } /** * 代理的内部类,他实现了IStudentManager接口,这个代理类就是服务端返回给客户端的AIDL接口对象,客户端可以通过这个代理类访问服务端的方法 */ private static class Proxy implements com.bob.aidltest.aidl.IStudentManager { 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.bob.aidltest.aidl.Student> getStudentList() throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.bob.aidltest.aidl.Student> _result; try { _data.writeInterfaceToken(DESCRIPTOR); mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0); _reply.readException(); _result = _reply.createTypedArrayList(com.bob.aidltest.aidl.Student.CREATOR); } finally { http://www.cnblogs.com/ghylzwsb/p/Binder.html