开发者社区> xxq2dream> 正文

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文件

新建AIDL文件,AS中会自动创建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);
}

最好把AIDL相关的文件都放在一个目录下

  • 文件编写完成以后通过Make Project命令就可以生成对应的Binder类

通过make让系统自动生成Binder类

生成的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>

可以看到2个进程

  • 客户端的话就简单在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意外死亡以后怎么办?
  • 如何注册监听回调,当服务端有新消息后马上通知注册回调的客户端?如何解除注册?
  • 如何进行权限验证等

欢迎关注我的微信公众号,和我一起学习一起成长!
AntDream

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
【Android 逆向】Android 进程注入工具开发 ( Visual Studio 开发 Android NDK 应用 | 使用 Makefile 构建 Android 平台 NDK 应用 )(二)
【Android 逆向】Android 进程注入工具开发 ( Visual Studio 开发 Android NDK 应用 | 使用 Makefile 构建 Android 平台 NDK 应用 )(二)
17 0
【Android 逆向】Android 进程注入工具开发 ( 总结 | 源码编译 | 逆向环境搭建使用 | 使用进程注入工具进行逆向操作 ) ★★★(二)
【Android 逆向】Android 进程注入工具开发 ( 总结 | 源码编译 | 逆向环境搭建使用 | 使用进程注入工具进行逆向操作 ) ★★★(二)
17 0
Android 进程生命周期 Process Lifecycle
  进程的生命周期   Android系统会尽力保持应用的进程,但是有时为了给新的进程和更重要的进程回收一些内存空间,它会移除一些旧的进程。
753 0
简记:关于windows各种进程间通信方式的对比(下)。。。
上下之间拖了好几天。。我觉得以后我做事情,都应该尽量把事情收个结尾,否则这次放下的事情,就不知道什么时候才能再拾起来。
763 0
【Android 逆向】Android 进程注入工具开发 ( 总结 | 源码编译 | 逆向环境搭建使用 | 使用进程注入工具进行逆向操作 ) ★★★(一)
【Android 逆向】Android 进程注入工具开发 ( 总结 | 源码编译 | 逆向环境搭建使用 | 使用进程注入工具进行逆向操作 ) ★★★(一)
15 0
Linux下C编程,进程通信之无名管道通信
最近在看进程间的通信,下面说说管道通信之无名管道。 1.概述   管道是Linux中很重要的一种通信方式,他是把一个程序的输出直接连接到另一个程序的输入,并且管道具有队列的特性。如Linux命令,“ps -ef | grep root”。
903 0
android中AVD的使用
AVD路径设置    前面提到,不管用Eclipse还是从命令行创建Android Emulator时,相应的文件是被放置到 “C:\Documents and Settings\Administrator\.android“ 下面。
641 0
《UNIX网络编程 卷2:进程间通信(第2版)》——1.8 书中IPC例子索引表
生产者-消费者:一个或多个线程或进程(生产者)把数据放到一个共享缓冲区中,另有一个或多个线程或进程(消费者)对该共享缓冲区中的数据进行操作。序列号持续增1:一个或多个线程或进程给一个共享的序列号持续增1。该序列号有时在一个共享文件中,有时在共享内存区中。
1116 0
+关注
xxq2dream
Android开发一枚,偶尔搞点别的,哈哈!公众号:AntDream
54
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载