Android多进程之Binder的使用
Binder是什么
- Binder是Android的一个类,实现了IBinder接口
- 从IPC角度来说,Binder是Android中的一种跨进程通信方式
- Binder还可以理解为一种虚拟的物理设备,设备驱动是/dev/binder,Linux中没有
- 从Android Framework角度来说,Binder是ServiceManger连接各种Manger(ActivityManager、WindowManager等)和相应的ManagerService的桥梁
- 从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候会返回服务端的Binder对象,通过这个Binder对象可以调用服务端的服务
使用Binder
生成Binder类
- 可以通过2中方式生成Binder:通过AIDL文件让系统自动生成;我们自己手动编写Binder
通过AIDL文件生成Binder
- 首先需要准备一个Parcelable对象
public class Book implements Parcelable { public int bookId; public String bookName; public Book(int bookId, String bookName) { this.bookId = bookId; this.bookName = bookName; } 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]; } }; protected Book(Parcel in) { bookId = in.readInt(); bookName = in.readString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(bookId); dest.writeString(bookName); } }
- 然后编写AIDL文件
// Book.aidl package com.xxq2dream.aidl; parcelable Book;
// IBookManager.aidl package com.xxq2dream.aidl; // Declare any non-default types here with import statements //必须显示导入需要的Parcelable对象 import com.xxq2dream.aidl.Book; //除了基本类型,其他参数都要标上方向,in表示输入型参数,out表示输出型参数,inout表示输入输出参数 interface IBookManager { List<Book> getBookList(); void addBook(in Book book); }
- 文件编写完成以后通过Make Project命令就可以生成对应的Binder类
模拟客户端和服务端的进程间通信
- 首先编写一个Service类,并设置android:process属性,开启在不同的进程
public class BookManagerService extends Service { private static final String TAG = "BookManagerService"; private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>(); private Binder mBinder = new BookManagerImpl(){ @Override public List<Book> getBookList() throws RemoteException { Log.e(TAG, "getBookList-->"+ System.currentTimeMillis()); return mBookList; } @Override public void addBook(Book book) throws RemoteException { Log.e(TAG, "addBook-->"); mBookList.add(book); } }; @Nullable @Override public IBinder onBind(Intent intent) { Log.e(TAG, "onBind-->"+ System.currentTimeMillis()); return mBinder; } @Override public void onCreate() { super.onCreate(); Log.e(TAG, "onCreate-->"+ System.currentTimeMillis()); mBookList.add(new Book(1, "Android")); mBookList.add(new Book(2, "IOS")); } }
- 注册Service
//AndroidManifest.xml <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <service android:name="com.xxq2dream.service.BookManagerService" android:process=":remote" /> </application>
- 客户端的话就简单在activity中通过bindService方法绑定服务端,然后通过返回的Binder调用服务端的方法
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private ServiceConnection mConnection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { Log.e(TAG, "ServiceConnection-->"+ System.currentTimeMillis()); //通过服务端回传的Binder得到客户端所需要的AIDL接口类型的对象,即我们上面的IBookManager IBookManager bookManager = BookManagerImpl.asInterface(iBinder); try { // 通过AIDL接口类型的对象bookManager调用服务端方法 List<Book> list = bookManager.getBookList(); Log.e(TAG, "query book list, list type:" + list.getClass().getCanonicalName()); Log.e(TAG, "query book list:" + list.toString()); Book newBook = new Book(3, "Android 进阶"); bookManager.addBook(newBook); Log.e(TAG, "add book:" + newBook); List<Book> newList = bookManager.getBookList(); Log.e(TAG, "query book list:" + newList.toString()); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName componentName) { Log.e(TAG, "binder died"); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Intent intent = new Intent(this, BookManagerService.class); //绑定服务,后面会回调onServiceConnected方法 bindService(intent, mConnection, Context.BIND_AUTO_CREATE); } @Override protected void onDestroy() { //解绑服务 unbindService(mConnection); super.onDestroy(); } }
- 通过以上的几个步骤我们就实现了一个简单的进程间通信的例子
调用过程
- 通过打印的日志我们可以大概分析上面的例子中各个方法的调用过程,这里我们分析下调用服务端获取书本列表的过程
- 客户端调用bindService方法后,BookManagerService创建,调用Binder类的构造方法创建Binder
- 然后BookManagerService的onBind方法将创建的Binder返回给客户端
- 客户端的onServiceConnected方法被调用,然后调用Binder的asInterface方法得到AIDL接口类型的对象bookManager
- 调用bookManager的getBookList方法实际上调用的是Binder类中的Proxy类对应的getBookList方法
- 在Proxy类对应的getBookList方法中调用Binder的transact方法发起远程过程调用请求,同时当前线程挂起,服务端的onTransact方法会被调用
- 服务端的onTransact方法被调用,通过code找到具体要调用的方法,这里是TRANSACTION_getBookList
- 最后会调用BookManagerService中的mBinder对象对应的getBookList方法,将书籍列表mBookList返回,返回的结果在Parcel变量reply中
- Proxy类中的getBookList方法通过result = reply.createTypedArrayList(Book.CREATOR);获取到服务端返回的数据
- 客户端onServiceConnected方法中接收到数据,调用过程结束
需要注意的地方
- 客户端调用远程请求时客户端当前的线程会被挂起,直到服务端进程返回数据,所以不能在UI线程中发起远程请求
- 客户端的onServiceConnected和onServiceDisconnected方法都运行在UI线程中,不可以在里面直接调用服务端的耗时方法
- 服务端的Binder方法运行在Binder线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现
- 同上面一点,服务端的Binder方法需要处理线程同步的问题,上面的例子中CopyOnWriteArrayList支持并发读写,自动处理了线程同步
- AIDL中能够使用的List只有ArrayList,但AIDL支持的是抽象的List。因此虽然服务端返回的是CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端
结语
- 以上只是Binder的简单应用,Binder的使用过程中还是有很多问题需要注意的
- 比如Binder意外死亡以后怎么办?
- 如何注册监听回调,当服务端有新消息后马上通知注册回调的客户端?如何解除注册?
- 如何进行权限验证等
欢迎关注我的微信公众号,和我一起学习一起成长!
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Kotlin 技术书籍集锦-推荐
Kotlin 技术书籍推荐 国内中文书籍: 《Kotlin 极简教程》 编辑推荐 本书由阿里Java程序员撰写,带你快速进入Kotlin世界。本书基于Kotlin 1.1版本,从Kotlin基础知识到动手实战,包含了大量精选示例代码和应用案例。 内容简介 本书从Kotlin的HelloWorld开始,生动讲解Kotlin的基础知识和实战案例,分为两大部分:“基础知识”部分包括语言基础、基本数据类型与类型变量、集合类、泛型、面向对象编程、函数式编程、协程等;“实战”部分包括Kotlin与Java互操作、使用Kotlin集成Spring Boot开发WEB服务端、使用Kotlin集成Gradle开发、使用Kotlin和Anko的Android开发、使用Kotlin DSL、Kotlin文件IO操作与多线程、使用Kotlin Native。本书包含了大量精选示例代码和应用案例,为初学者提供了实用指南,也帮助熟练的Java开发者快速切换到Kotlin。 《Spring Boot 开发实战》(基于 Spring Boot 2.0 和 Kotlin 、Gradle 的企业级开发最佳实践) 本书以案...
- 下一篇
27.FFmpeg+OpenGLES+OpenSLES播放器实现(一.音视频基础知识概要)
FFmpeg开发文档 前言 之前写了很多关于ndk的文章,也以FFmpeg的编译为例大概的讲述了一次ndk开发从编译到使用的过程,但是内容过于分散,而且有些东西已经过时,比如eclipse工具,比如ndk-build的编译方式以及之前ffmpeg使用的2.x的版本,这些都已经渐渐的不被使用了,这次打算在Android Studio的工具下使用Cmake的编译方式,编译较新的ffmpeg版本,完成一次完整的编译开发过程,实现一次功能较完备的ffmpeg播放器。目前最新的ffmpeg是4.x,我这次打算使用3.x的进行编译,因为每一个版本对应的编译脚本也是有所不同的,使用相同的脚本并不能成功的适应所有的version。所以我们必须针对它版本的更新去调整我们的脚本。ffmpeg功能强大,我也是在学习的过程中,有些地方可能写的不够完善,还望各位多多指正。 本文这次主要以音视频的基础内容为主 MPEG-4 一套用于音频、视频信息的压缩编码标准,MPEG4于1998 年11 月公布,原预计1999 年1月投入使用的国际标准MPEG4不仅是针对一定比特率下的视频、音频编码,更加注重多媒体系统的交互性...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS8安装Docker,最新的服务器搭配容器使用
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- CentOS关闭SELinux安全模块
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- SpringBoot2全家桶,快速入门学习开发网站教程
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程
- CentOS8编译安装MySQL8.0.19