Android--进程间通信(Binder)

简介: Android系统提供了一些通用服务,比如音乐打电话发短信,WIFI,定位,输入法,传感器等。应用程序与这些通用服务运行在不同的进程中,如果应用程序想要与这些通用服务交互就要涉及到进程间通信,Binder就是为了Android进程间通信而设计的。
 
 

Android系统提供了一些通用服务,比如音乐打电话发短信,WIFI,定位,输入法,传感器等。应用程序与这些通用服务运行在不同的进程中,如果应用程序想要与这些通用服务交互就要涉及到进程间通信,Binder就是为了Android进程间通信而设计的。

Binder框架


Binder是一种架构,这种架构提供了服务端接口、Binder驱动、客户端接口三个模块。

服务端 Binder服务端相当于一个Binder类对象。当创建该对象时,其内部会启动一个线程不断接收Binder驱动发送的消息。收到消息后会执行Binder.onTransact()方法。所以要实现一个Binder服务就必须重载onTransact()方法。

onTransact()方法通常用来做数据格式转换,按约定的顺序取出Binder客户端发送来的数据并转换成服务端识别的数据格式。

Binder驱动 Binder驱动运行在内核态,其所有操作都是基于内存而非硬件,客户端与服务端通信时需要Binder驱动进行中转。

当一个服务端Binder被创建时其在Binder驱动中同时会创建一个mRemote引用指向服务端。客户端要访问服务端时首先要获取服务端在Binder中的引用mRemote,获取引用后就可以通过mRemote.transact()方法向服务端发送消息。

transact()方法实现了以下几个功能:

  • 以线程间消息通信的模式向服务端发送客户端传递过来的参数。
  • 挂起当前客户端线程,等待服务端线程执行完毕后的通知(notify)
  • 接收服务端线程通知,继续执行客户端线程,并返回执行结果

客户端 这里就是指我们需要和系统服务交互的应用程序。应用程序使用startService()与应用程序Service建立连接,使用getSystemService()与系统Service建立连接,从而进行通信。

数据格式 在进行IPC通信时需要约定客户端与服务端通信的数据格式。Android使用AIDL(Android Interface Definition Language)约定数据格式。

Android提供了一个AIDL工具,可以把AIDL文件转换成一个Java类,在该Java类中同时重载了onTransact()方法和transact()方法,统一了存入和读取参数。

实现Binder服务端


public class MusicService extends Binder{
    @Override
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException{
        return super.onTransact(code,data,reply, flags);
    }
    
    public void start(String filePath){

    }
    public void stop(){}
}

以上代码基于Binder创建了一个Service,使用startService()方法启动后就可以看到DDMS中多了一个线程。

重载onTransact(int code, Parcel data, Parcel reply, int flags)方法:

switch(code){
     code 1000:
            data.enforceInterface("MusicService");// 数据校验与客户端writeInterfaceToken()对应
            String filePath = data.readString();//读取一个字符串
            start(filePath);
            // reply.writeXXX();//如果需要返回结果则写入reply中
            break;
}

data中保存着客户端传递过来的参数,onTransact()方法内部需要从data中读取客户端传递的参数。参数的位置格式需要在AIDL文件中约束。
reply中保存服务端返回客户端的结果,如果需要返回则调用Parcel提供的相关方法写入结果。参数的位置格式需要在AIDL文件中约束。
code变量标识客户端希望服务端执行哪个方法,这里假定1000执行start()方法。
flags表示IPC调用模式:0代表"双向模式",服务端在执行完指定服务后会返回数据;1代表"单向模式",服务端在执行完指定服务后不反回任何数据。

实现Binder客户端


首先在startService(),getSystemService()中获取服务端Binder的引用,然后将参数按AIDL定义的格式写入data中并调用mRemote.transact()方法传入参数,服务端会在onTransact()方法中取出这里出入的参数:

IBinder mRemote = null;
String filePath = "/sdcard/music/xxx.mp3";
int code = 1000;
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken("MusicService");
data.writeString(filePath);
mRemote.transact(code,data,reply,0);
IBinder binder = reply.readStrongBinder();
reply.recycle();
data.recycle();

调用mRemote.transact()方法后,Binder驱动会挂起当前客户端线程,并向服务端发送一个消息,这个消息就包括客户端传递的参数。服务端接收到消息,解析数据执行完相关任务后会把执行结果写入reply中,然后向Binder驱动发送一个notify消息,Binder驱动从挂起处唤醒客户端线程继续执行。

系统Service与应用程序Service


Service也就是服务分为两种,一种是系统创建的Service,另一种是应用程序自定义的Service。这两种Service都是Binder服务端。

系统Service
系统Service在系统系统初始化时从SystemServer进程中启动。常见的有PowerManagerService(电源管理服务),VibratorService(振动传感器服务),WindowManagerService(窗口管理服务)NotificationManagerService等等,每个系统服务都是一个Binder,都运行在一个独立的线程中。

系统提供了一个ServiceManager类来管理系统服务ServiceManager本身也是一个Binder,运行在一个独立的进程中,其提供了一个全局Binder供应用程序使用,应用程序获取其他系统服务都是通过ServiceManager的全局Binder来获取的。

这设计的好处仅仅暴露一个全局Binder引用,其他系统服务则隐藏起来,从而有助于系统扩展,以及调用系统服务的安全检查。

系统Service在启动时首先向ServiceManager注册Service(注册自己),当调用getSystemService(serviceName)获取系统服务时,会间接调用到ServiceManager.getService(String name)方法。getService()实现如下所示:

public static IBinder getService(String name){
    try{
        IBinder service = sCache.get(name);
        if(service != null){
            return service;
        }else{
            return getIServiceManager().getService(name);
        }
    } catch(RemoteException e){
        Log.e(TAG,"error in get Service", e);
    }
}

首先从缓存中获取,没获取到则从getIServiceManager()中获取,getIServiceManager()返回的是系统唯一ServiceManager的Binder。

应用程序Service
系统在ActivityManagerService中提供了startService()方法来启动应用程序自定义Service。客户端使用以下方法和应用程序Service建立连接:

public ComponentName startService(Intent intent)
public boolean bindService(Intent service, ServiceConnection conn,int flags)

startService用于启动一个服务,bindService用于绑定一个服务,bindService的第二个参数是获取服务端Binder的关键所在:

public interface ServiceConnection{
     public void onServiceConnected(ComponentName name,IBinder service);
     public void onServiceDisconnected(ComponentName name);    
}

onServiceConnected()方法的第二个变量就是我们需要的服务端Binder的引用,当客户端请求AmS启动某个Service,如果该Service正常启动,那么AmS就会远程调用ActivityThread类中的ApplicationThread对象,调用的参数包括Service的Binder引用,然后在ApplicationThread中会回调bindService()方法中的conn接口。因此,在客户端中,可以在onServiceConnected()方法中将其参数Service保存为一个全局变量,从而在客户端任何地方都可以随时调用该服务。



作者:Gooooood
链接:https://www.jianshu.com/p/ef69025eb7f3
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
目录
相关文章
|
12天前
|
安全 Java 定位技术
Android 浅度解析:AIDL & Binder (1)
Android 浅度解析:AIDL & Binder (1)
38 0
|
1月前
|
安全 Linux API
Android进程与线程
Android进程与线程
20 0
|
8月前
|
Java Linux Android开发
理解Android进程创建流程
理解Android进程创建流程
53 0
|
9月前
|
Java API Android开发
Android中Binder在项目中的具体使用详解
Android中Binder在项目中的具体使用详解
97 0
|
Unix Linux Android开发
Android C++系列:Linux进程间通信(二)
mmap可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存 地址,对文件的读写可以直接用指针来做而不需要read/write函数。
73 0
|
Linux Android开发 C++
Android C++系列:Linux进程间通信(一)
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不 到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用 户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程 间通信(IPC,InterProcess Communication)。
59 0
|
Shell Linux Android开发
Android C++系列:Linux进程(三)
如果一个进程已经终止,但是它的父进程尚未调用wait或waitpid对它进行清理,这时 的进程状态称为僵尸(Zombie)进程。任何进程在刚终止时都是僵尸进程,正常情况下,僵 尸进程都立刻被父进程清理了,为了观察到僵尸进程
98 0
|
Linux Android开发 C++
Android C++系列:Linux进程(二)
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支), 子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的 用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建 新进程,所以调用exec前后该进程的id并未改变。
139 0
|
Shell Linux C语言
Android C++系列:Linux进程(一)
我们知道,每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信 息,Linux内核的进程控制块是task_struct结构体。现在我们全面了解一下其中都有哪 些信息。
99 0
|
Linux 调度 Android开发
Android开启多进程及进程间通信的几种方式
开启多进程及进程间通信的几种方式
261 0

相关实验场景

更多