Android-Binder 简析

前言

对于Android来说,Binder的重要性怎么说都不为过。不管是我们的四大组件Activity、Service、BroadcastReceiver、ContentProvider,还是经常在应用中使用到的各种ServiceManager,其背后都是Binder在支撑。然而Binder机制又不是三言两语能够描述得清楚的,因此本文通过对一个简单的AIDL Demo进行分析,让读者对Binder有个初步的认识,要想深入了解Binder背后的原理,可以参考最后的延伸阅读。

Demo

首先我们通过AIDL新建一个跨进程通信的Demo,然后在代码中简单分析Binder的运行过程。

Server Module

我们先新建一个提供接口的AIDL服务端module,服务端主要提供AddBook和getBookList两个功能,其目录如下:

p2.png

p2.png

  • IBookManager.AIDL

// IAIDLServer.aidl
package com.nancyyihao.aidlserver;
import  com.nancyyihao.aidlserver.Book;// Declare any non-default types here with import statementsinterface IBookManager {List<Book> getBookList();void addBook(in Book book);
}
  • Book.java

package com.nancyyihao.aidlserver;import android.os.Parcel;
import android.os.Parcelable;/*** Created by jumper on 2016/9/7.*/
public class Book implements Parcelable {private String bookName;private int bookId;public Book(){}public Book(int bookId, String bookName){this.bookId = bookId ;this.bookName = bookName ;}public Book(Parcel parcel){bookName = parcel.readString();bookId = parcel.readInt();}public String getBookName() {return bookName;}public void setBookName(String bookName) {this.bookName = bookName;}public int getBookId() {return bookId;}public void setBookId(int bookId) {this.bookId = bookId;}@Overridepublic int describeContents() {return 0;}@Overridepublic void writeToParcel(Parcel dest, int flags) {dest.writeString(bookName);dest.writeInt(bookId);}public static final Parcelable.Creator<Book> CREATOR = new Creator<Book>(){@Overridepublic Book createFromParcel(Parcel source) {return new Book(source);}@Overridepublic Book[] newArray(int size) {return new Book[size];}};
}
  • Book.AIDL

package com.nancyyihao.aidlserver;
parcelable Book;
  • BookManagerService

package com.nancyyihao.aidlserver;import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;/*** Created by jumper on 2016/9/7.*/
public class BookManagerService extends Service {private static final String TAG = "BMS";private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();@Overridepublic IBinder onBind(Intent intent) {return mBinder;}@Overridepublic void onCreate() {super.onCreate();// to distinguish with client module, we set the book id different from client modulemBookList.add(new Book(3,"Android"));mBookList.add(new Book(4,"iOS"));}private Binder mBinder = new IBookManager.Stub() {@Overridepublic List<Book> getBookList() throws RemoteException {return mBookList;}@Overridepublic void addBook(Book book) throws RemoteException {mBookList.add(book);}} ;
}

Client Module

把Server module的代码拷贝一份(AIDL包名不能变),然后新建一个MainActivity即可

  • MainActivity

package com.nancyyihao.aidlserver;import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;import com.nancyyihao.R;import java.util.List;public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {IBookManager bookManager = IBookManager.Stub.asInterface(service);Log.e("trace", "onServiceConnected");try {List<Book> bookList = bookManager.getBookList();Log.e(TAG, "query book list, list type:" + bookList.getClass().getCanonicalName());Log.e(TAG, "query book list:" + bookList.toString());} catch (Exception e) {e.printStackTrace();}}@Overridepublic void onServiceDisconnected(ComponentName name) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);final Intent intent = new Intent();intent.setAction("com.nancyyihao.startservice");intent.setPackage("com.nancyyihao.aidlserver"); // server's package nameLog.e("trace", "bindService");bindService(intent, mConnection, BIND_AUTO_CREATE);}@Overrideprotected void onDestroy() {super.onDestroy();unbindService(mConnection);}
}

分析

把代码写好后,build一下,就能看到自动生成了一个IBookManager.Java文件

  • IBookManager.Java

p3.png

p3.png

package com.nancyyihao.aidlserver;
// Declare any non-default types here with import statementspublic interface IBookManager extends android.os.IInterface {/*** Local-side IPC implementation stub class.*/public static abstract class Stub extends android.os.Binder implements com.nancyyihao.aidlserver.IBookManager {private static final java.lang.String DESCRIPTOR = "com.nancyyihao.aidlserver.IBookManager"; // Binder Indentifierpublic Stub() {this.attachInterface(this, DESCRIPTOR);}/*** Cast an IBinder object into an com.nancyyihao.aidlserver.IBookManager interface,* generating a proxy if needed.*/public static com.nancyyihao.aidlserver.IBookManager asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.nancyyihao.aidlserver.IBookManager))) {return ((com.nancyyihao.aidlserver.IBookManager) iin);  // local Binder}return new com.nancyyihao.aidlserver.IBookManager.Stub.Proxy(obj);  // remote Binder}@Overridepublic android.os.IBinder asBinder() {return this;}@Overridepublic 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_getBookList: {data.enforceInterface(DESCRIPTOR);java.util.List<com.nancyyihao.aidlserver.Book> _result = this.getBookList();reply.writeNoException();reply.writeTypedList(_result);return true;}case TRANSACTION_addBook: {data.enforceInterface(DESCRIPTOR);com.nancyyihao.aidlserver.Book _arg0;if ((0 != data.readInt())) {_arg0 = com.nancyyihao.aidlserver.Book.CREATOR.createFromParcel(data);} else {_arg0 = null;}this.addBook(_arg0);reply.writeNoException();return true;}}return super.onTransact(code, data, reply, flags);}private static class Proxy implements com.nancyyihao.aidlserver.IBookManager {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic java.util.List<com.nancyyihao.aidlserver.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.nancyyihao.aidlserver.Book> _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);_reply.readException();_result = _reply.createTypedArrayList(com.nancyyihao.aidlserver.Book.CREATOR);} finally {_reply.recycle();_data.recycle();}return _result;}@Overridepublic void addBook(com.nancyyihao.aidlserver.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();}}}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<com.nancyyihao.aidlserver.Book> getBookList() throws android.os.RemoteException;public void addBook(com.nancyyihao.aidlserver.Book book) throws android.os.RemoteException;
}

Client先调用bindService启动服务,会调用BookManagerService的onCreate方法,接着调用onBind方法,该方法会返回远程的Binder---mBinder,该Binder包含getBookList和AddBook两个方法的具体实现。

    @Overridepublic IBinder onBind(Intent intent) {return mBinder;}private Binder mBinder = new IBookManager.Stub() {@Overridepublic List<Book> getBookList() throws RemoteException {return mBookList;}@Overridepublic void addBook(Book book) throws RemoteException {mBookList.add(book);}} ;

当Client和server成功建立连接时,就会调用Client的onServiceConnected(name, service)方法把远程的mBinder回调给Client,此时的service就是远程的mBinder对象

private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {IBookManager bookManager = IBookManager.Stub.asInterface(service);Log.e("trace", "onServiceConnected");try {List<Book> bookList = bookManager.getBookList();} catch (Exception e) {e.printStackTrace();}}

Client端通过asInterface方法把mBinder转成AIDL接口,如果是本进程内的Binder就直接返回,否则返回内部代理类proxy

 public static com.nancyyihao.aidlserver.IBookManager asInterface(android.os.IBinder obj) {if ((obj == null)) {return null;}android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin != null) && (iin instanceof com.nancyyihao.aidlserver.IBookManager))) {return ((com.nancyyihao.aidlserver.IBookManager) iin);  // local Binder}return new com.nancyyihao.aidlserver.IBookManager.Stub.Proxy(obj);  // remote Binder}

接着执行

 try {List<Book> bookList = bookManager.getBookList();} catch (Exception e) {e.printStackTrace();}

此时的bookManager通过asInterface方法转换后,返回的实际上是本地的proxy类

private static class Proxy implements com.nancyyihao.aidlserver.IBookManager {private android.os.IBinder mRemote;Proxy(android.os.IBinder remote) {mRemote = remote;}@Overridepublic android.os.IBinder asBinder() {return mRemote;}public java.lang.String getInterfaceDescriptor() {return DESCRIPTOR;}@Overridepublic java.util.List<com.nancyyihao.aidlserver.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.nancyyihao.aidlserver.Book> _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);_reply.readException();_result = _reply.createTypedArrayList(com.nancyyihao.aidlserver.Book.CREATOR);} finally {_reply.recycle();_data.recycle();}return _result;}@Overridepublic void addBook(com.nancyyihao.aidlserver.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();}}}static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);}

通过代码我们可以看到proxy类其实也是一个IBookManager接口,调用bookManager.getBookList()其实是调用Proxy.getBookList

 @Overridepublic java.util.List<com.nancyyihao.aidlserver.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.nancyyihao.aidlserver.Book> _result;try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);_reply.readException();_result = _reply.createTypedArrayList(com.nancyyihao.aidlserver.Book.CREATOR);} finally {_reply.recycle();_data.recycle();}return _result;}

Proxy.getBookList方法中调用了mRemote.trasact()

    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;}

Client就是在这里和Server进行远程通信的!把需要的参数放data中,服务端执行完后把接口写到result里。从代码中可以看到transact方法中调用了onTransact方法。我们再看看onTransact方法有啥东西。

  @Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {......case TRANSACTION_getBookList: {data.enforceInterface(DESCRIPTOR);java.util.List<com.nancyyihao.aidlserver.Book> _result = this.getBookList();reply.writeNoException();reply.writeTypedList(_result);return true;}......}

直接调用了this.getBookList方法返回结果,这个this到底是哪个对象?前面提到mRemote其实是远程中的Binder对象,其代码如下

 private Binder mBinder = new IBookManager.Stub() {@Overridepublic List<Book> getBookList() throws RemoteException {return mBookList;}@Overridepublic void addBook(Book book) throws RemoteException {mBookList.add(book);}} ;

this.getBookList其实就是mRemote.getBookList,就是上面代码中的mBinder.getBookList!然后把返回结果放到result中即可。至此,整个通信过程分析完毕。

总结

  • Client是通过本地的Proxy类像Server发起通信

  • Client通过ServerConnection接口回调收到Server远程Binder对象

  • IPC过程发生在transact方法中,该方法会挂起直到服务端返回结果,因此不能在主线程调用远程服务。

Binder工作机制

p1.jpg

图片和文中Demo来自《Android开发技术探索》

源码下载

  • Android-AIDL-Demo

延伸阅读

  • Binder学习指南

  • Android Bander设计与实现 - 设计篇

  • Android进程间通信(IPC)机制Binder简要介绍和学习计划

转载于:https://www.cnblogs.com/jasonkent27/p/5860680.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/373911.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

tplink 703刷固件

1.软件下载: ImageBuilder链接 如果是全新刷机的话,使用:http://downloads.openwrt.org/snapshots/trunk/ar71xx/generic/openwrt-ar71xx-generic-tl-wr703n-v1-squashfs-factory.bin 如果是系统升级的话,使用:http://downloads.openwrt.org/snapshots/trunk/ar71xx/generic/op…

编程反模式

您是否曾经进行过代码审查&#xff0c;记录了非常高的WTF / m&#xff1f; 您是否想知道所有这些错误代码的原因是什么&#xff1f; 在大多数情况下&#xff0c;导致原因1的主要原因是使用设计和编码反模式。 如果您喜欢定义&#xff0c;请参见以下内容&#xff1a;AntiPatter…

sql 插入text字段包含特殊字符_Kettle(PDI)转换中输出之插入/更新详解

概述Insert / update(插入 / 更新)此步骤首先使用一个或多个查询关键字查找表中的一行。如果找不到该行&#xff0c;则插入该行。如果可以找到它&#xff0c;并且要更新的字段相同&#xff0c;则不执行任何操作。如果它们不完全相同&#xff0c;则更新表中的行。注意&#xff1…

一张图让你看清Java集合类(Java集合类的总结)

如今关于Java集合类的文章非常多&#xff0c;可是我近期看到一个非常有意思图片&#xff0c;基本上把Java集合的整体框架都给展现出来了。非常直观。 假设发现图片看不清楚。点此处看大图 在这里&#xff0c;集合类分为了Map和Collection两个大的类别。 处于图片左上角的那一块…

【P1835】小红花

很简单的题&#xff0c;然而我没想到&#xff0c;在NOIP上怎么办嘛QAQ 话说这题不知道怎么分类啊……先扔到玄学里边把…… 原题&#xff1a; Fj在圣诞节来临之际&#xff0c;决定给他的奶牛发一些小红花。现在Fj一共有N头奶牛&#xff0c;这N头牛按照编号1..N&#xff0c;排成…

Cocos2d-x 3.2 Lua演示样例FontTest(字体測试)

Cocos2d-x 3.2 Lua演示样例FontTest&#xff08;字体測试&#xff09;本篇博客介绍Cocos2d-x 3.2中Lua測试项目中的FontTest样例&#xff0c;主要使用了字体文件来创建我们想要的字体样式&#xff1a;第一个參数为文本。第二參数为ttf字体文件&#xff0c;第三个參数为字体大小…

linux上安装memcached步骤

libevent: http://libevent.org/ 服务器端&#xff1a;https://code.google.com/archive/p/memcached/downloads 客户端&#xff1a; http://pecl.php.net/package/memcache 和 http://pecl.php.net/package/memcached 二选一 http://chenzhou123520.iteye.com/blog/1…

具有GlassFish和一致性的高性能JPA –第2部分

在我的四部分系列的第二部分中&#xff0c;我将解释将Coherence与EclipseLink和GlassFish一起使用的策略第一。这描述了配置Coherence的JPA支持的Cache所必须采取的步骤&#xff0c;以及如何在GlassFish中使用它。高性能数据存储。 一般的做法 您可以将Coherence API与通过JPA映…

arm板telnetd为什么运行不了_一种基于ARM的嵌入式系统开发的方案详细讲解

背景介绍在日益信息化的社会中&#xff0c;各种各样的嵌入式系统已经全面渗透到日常生活的每一个角落。嵌入式系统的功能越来越复杂&#xff0c;这就使得一个嵌入式系统产品从市场需求立项到方案选择、样机研制、定型量产所需要的开发费用越来越多&#xff0c;所需开发时间越来…

反素数 -- 数学

反素数就是区间内约数个数最多的那个数。 在ACM题目里&#xff0c; 一般是求约数最多而且数字最小的那个数&#xff0c;【1--n】 二是求约数刚好等于n的最小的那个数 三是求区间里的最小反素数【beign&#xff0c;end】 1和3有区别吗&#xff1f;有&#xff0c;1可以加速&#…

上传文件---未能找到路径“D:\MyProject\Files\”的一部分

C# 使用控件FileUpload 上传文件&#xff0c;简单实例&#xff1a; protected void btnUpload_Click(object sender, EventArgs e){string path Server.MapPath("~/Files/");if (fileUpload.HasFile true){string filename fileUpload.FileName.ToLower();fileUpl…

关于string转整数

又是leetcode的easy级别题&#xff0c;很基本的题目&#xff0c;却漏考虑很多情况&#xff0c;动手前一定要考虑清楚呀&#xff01;&#xff01;&#xff01; 就当做锻炼写作能力吧&#xff0c;先上题目&#xff01; 将文本转换成整数&#xff0c;注意一下几点&#xff1a; 1.文…

数字三角形——递归、递推、记忆化搜索

数字三角形 描述: 有一个由非负整数组成的三角形&#xff0c;第一行只有一个数&#xff0c;除了最下行之外每个数的左下方和右下方各有一个数。 问题&#xff1a; 从第一行的数开始&#xff0c;每次可以往左下或右下走一格&#xff0c;直到走到最下行…

水晶报表分组分栏_web报表可视化设计器工具推荐

古往今来&#xff0c;信息就是决胜的关键。在科技时代的今天亦是如此。企业的数据管理在帮助企业加强管控、提高竞争力等方面具有不可或缺的作用。这就不得不说到报表工具。企业想要将储存于各种商业信息系统中的数据转化成有用的信息&#xff0c;最终帮助决策者做出更快、更好…

Twisted

Twisted定义Twisted是一个基于事件驱动的网络引擎框架网络框架&#xff0c;别人预先定义好的一个框架&#xff08;一个项目&#xff09;&#xff0c;如.net某个web框架有25个class&#xff0c;从BeginRequest依次执行类里的process方法&#xff0c;程序员自己定义一个类&#x…

Centos 6.5 搭建php环境(nginx+mariadb+php7)

1.mariaDb vim /etc/yum.repos.d/MariaDB.repo [mariadb] name MariaDB baseurl http://yum.mariadb.org/5.5/centos5-x86 gpgkeyhttps://yum.mariadb.org/RPM-GPG-KEY-MariaDB gpgcheck1#如果服务器已经安装了MariaDB-Galera-server包&#xff0c;你可能需要在安装MariaDB-s…

读取nas_NAS怎么玩?除了存放小姐姐,它竟然还有这些功能

自从有了电脑&#xff0c;就一直在折腾"存储那点事儿"&#xff0c;说到底&#xff0c;电脑的本质就是存储&#xff0c;而自己弄家用存储方面的东西算下来也有几年了。单机的硬盘存储比较简单&#xff0c;但是随着家里各种设备的增多&#xff0c;各个设备间的文件共享…

OC第一讲:类和对象

今天终于开始进行OC的学习了 一.首先讲了NSLog NSLog是oc里面的输出语句&#xff0c;其用法和printf差不多&#xff0c;但是还是有差别的 1&#xff0c;NSLog是自动换行的&#xff0c;不用像printf那样还需要加\n&#xff1b; 2&#xff0c;NSLog在引号面前需要添加符号&#x…

PL/SQL Developer跑在Oracle 64位数据库上初始化错误

安装完Oracle(64位)、PL/SQL Developer后运行PL/SQL出现如下的错误&#xff1a; 网上查资料说&#xff0c;我的PL/SQL Developer与ORACLE不兼容&#xff0c;即PL/SQL不支持64位的ORACLE&#xff0c;因此得下一个32位的ORCALE客户端并配置相应的参数&#xff1a; 解决步骤小记&a…

gis 联合 融合_GIS技术进化 | 我们为何需要跨平台GIS技术体系?

10月30日&#xff0c;超图在2019 GIS 软件技术大会上发布了SuperMap GIS 10i系列产品。SuperMap GIS 10i全面融入人工智能(AI)技术&#xff0c;创新并构建了GIS基础软件“BitCC”五大技术体系&#xff0c;即大数据GIS、人工智能GIS、新一代三维GIS、云原生GIS和跨平台GIS&#…