Android IPC机制详解(一)

0.598字数 1975阅读 6964

本文主要从以下几个方面来介绍IPC机制
1、什么是IPC
2、Binder机制原理
3、AIDL实现

一、什么是IPC

IPC是Inter-Process-Communication的缩写,意思是进程间通信或者跨进程通信;
说起进程间通信,我们应该首先来了解一下什么是进程。按照操作系统的描述,线程是CPU调度的最小单元,而进程一般指一个执行单元,在移动设备上指一个程序或应用;一个进程可以包含多个线程;

为什么要用到多进程?
在Android系统中一个应用默认只有一个进程,每个进程都有自己独立的资源和内存空间,其它进程不能任意访问当前进程的内存和资源,系统给每个进程分配的内存会有限制。如果一个进程占用内存超过了这个内存限制,就会报OOM的问题,很多涉及到大图片的频繁操作或者需要读取一大段数据在内存中使用时,很容易报OOM的问题,为了彻底地解决应用内存的问题,Android引入了多进程的概念,它允许在同一个应用内,为了分担主进程的压力,将占用内存的某些页面单独开一个进程,比如Flash、视频播放页面,频繁绘制的页面等。Android多进程使用很简单,只需要在AndroidManifest.xml的声明四大组件的标签中增加”android:process”属性即可,process分私有进程和全局进程,以“:”号开头的属于私有进程,其他应用组件不可以和他跑在同一个进程中;不以“:”号开头的属于全局进程,其他应用可以通过ShareUID的方式和他跑在同一个进程中;

但是多进程模式出现以下问题:
1、静态成员和单例模式完全失效
2、线程同步机制完全失效
3、SharedPreferences的可靠性下降
4、Application多次创建

因此为了避免这些问题Android中有多种IPC机制,如AIDL,Messenger,Socket,ContentProvider,但是这些机制底层全部都是用了Binder机制来实现的,什么是Binder?要了解Android系统中的IPC我们首先要了解的就是Binder;

二、Binder机制原理

1、Binder机制

Binder是Android系统中的一种IPC进程间通信结构。
Binder的整个设计是C/S结构,客户端进程通过获取服务端进程的代理,并通过向这个代理接口方法中读写数据来完成进程间的数据通信。
Android之所以选择Binder,有2个方面的原因。
1是安全,每个进程都会被Android系统分配UID和PID,不像传统的在数据里加入UID,这就让那些恶意进程无法直接和其他进程通信,进程间通信的安全性得到提升。
2是高效,像Socket之类的IPC每次数据拷贝都需要2次,而Binder只要1次,在手机这种资源紧张的情况下很重要。
Binder机制原理图:


1.客户端获取服务端的代理对象(proxy)。我们需要明确的是客户端进程并不能直接操作服务端中的方法,如果要操作服务端中的方法,那么有一个可行的解决方法就是在客户端建立一个服务端进程的代理对象,这个代理对象具备和服务端进程一样的功能,要访问服务端进程中的某个方法,只需要访问代理对象中对应的方法即可;
2.客户端通过调用代理对象向服务端发送请求。
3.代理对象将用户请求通过Binder驱动发送到服务器进程;
4.服务端进程处理客户端发过来的请求,处理完之后通过Binder驱动返回处理结果给客户端的服务端代理对象;
5.代理对象将请求结果进一步返回给客户端进程。
通过以上5个步骤,就完成了一次Binder通信。

2、Binder机制的组成

Binder机制由三部分组成,即:
1.Client;
2.Server;
3.ServiceManager。

三部分组件之间的关系:
1.Client、Server、ServiceManager均在用户空间中实现,而Binder驱动程序则是在内核空间中实现的;
2.在Binder通信中,Server进程先注册一些Service到ServiceManager中,ServiceManager负责管理这些Service并向Client提供相关的接口;
3.Client进程要和某一个具体的Service通信,必须先从ServiceManager中获取该Service的相关信息,Client根据得到的Service信息与Service所在的Server进程建立通信,之后Clent就可以与Service进行交互了;
4.Binder驱动程序提供设备文件/dev/binder与用户空间进行交互,Client、Server和ServiceManager通过open和ioctl文件操作函数与Binder驱动程序进行通信;
5.Client、Server、ServiceManager三者之间的交互都是基于Binder通信的,所以通过任意两者这件的关系,都可以解释Binder的机制。

三、AIDL的实现

在Android中有多种实现IPC的方式,各有各的优缺点,我们拿其中一种最常用方式来更深入的了解一下Android中IPC的实现方式,从而彻底理解Binder机制的工作方式;
我们用一个最简单的场景:服务端提供计算的方法计算两个数之和并返回;客户端调用服务端的方法得出结果并显示;
首先是服务端:我们首先创建一个AIDL文件,其实就是定义我们的方法接口

// IMyAdd.aidlpackage 
test.jiao.com.aidl;
interface IMyAdd {
    int add(int first,int second);
}

其中定义了一个两个数相加的方法接下来我们创建一个Service

package test.jiao.com.aidl;

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.annotation.Nullable;

/**
 * date :2016/12/26
 * author :SuperJiao
 * Description
 */
public class MyAddService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }


    private Binder mBinder = new IMyAdd.Stub() {
        @Override
        public int add(int first, int second) throws RemoteException {
            return first + second;
        }
    };
}

我们在Service中实现了我们定义的AIDL接口,并且服务端在绑定接口的时候将服务端的IBinder对象返回给客户端;客户端拿到服务端的IBinder对象就可以调用服务端的方法了;
客户端实现如下:
和服务端定义一个一模一样的aidl文件(包名、方法名,参数必须一致)

// IMyAdd.aidlpackage 
test.jiao.com.aidl;
interface IMyAdd {
    int add(int first,int second);
}

绑定服务端的服务

package test.jiao.com;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Button;
import android.widget.Toast;

import test.jiao.com.aidl.IMyAdd;

public class MainActivity extends Activity {
    private Button bt_aidl_add;
    private IMyAdd mIMyAdd;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        start();
    }


    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            Toast.makeText(MainActivity.this, "绑定服务成功", Toast.LENGTH_SHORT).show();
            mIMyAdd = IMyAdd.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    private void start() {
        bindService();//绑定服务
        bt_aidl_add = (Button) findViewById(R.id.bt_aidl_add);
        bt_aidl_add.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mIMyAdd != null) {
                    try {
                        int result = mIMyAdd.add(5, 5);
                        Toast.makeText(MainActivity.this, result + "", Toast.LENGTH_SHORT).show();
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                } else {
                    Toast.makeText(MainActivity.this, "计算失败", Toast.LENGTH_SHORT).show();
                }
            }
        });

    }


    //绑定服务
    private void bindService() {
        Intent intent = new Intent();
        intent.setAction("com.jiao.myaidl.action.MYADD_SERVICE");
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        unbindService(serviceConnection);//解绑服务
        super.onDestroy();
    }

}

接下来我们来分析一下整个AIDL的执行过程:
1、首先服务端的MyAddService在自己的进程中向Binder驱动申请创建了一个MyAddService的Binder实体;Binder驱动为MyAddService创建了位于内核中的Binder实体节点以及Binder的引用,并将名字和新建的引用打包传递给SM(实体没有传给SM),通知SM注册一个MyAddService;

2、SM收到数据包后,从中取出MyAddService名字和引用,填入一张查找表中。在启动服务的时候,SM就会从这张查找表中查找相应的服务;

3、客户端Client申请访问MyAddService,SM就会从请求数据包中获得MyAddService的名字,在查找表中找到该名字对应的条目,取出Binder的引用打包回复给client。之 后,Client就可以利用MyAddService的引用使用MyAddService的服务了。

推荐阅读更多精彩内容