IPC机制


目录

  • 1)多进程模式
  • 2)IPC基础概念
    • 2.1)Serializable和Parcelable
    • 2.2)Binder机制
      • 2.2.1)自定义服务的Binder分析
      • 2.2.2)系统服务的Binder分析
  • 3)Android中的IPC
    • 3.1)Bundle
    • 3.2)文件共享
    • 3.3)Messenger
    • 3.4)AIDL
    • 3.5)ContentProvider
    • 3.6)Socket
    • 3.7)广播
  • 4)Binder连接池
  • 5)选择合适的IPC方式

1)多进程模式

Android中使用多进程只有一种方法,就是给四大组件指定android:process属性
可以在Monitor视图中查看进程信息,还可以用shell来查看,命令为:adb shell ps 或者 adb shell ps|grep 包名

<activity  
    android:name=".SecondActivity"  
    <!-- 私有进程,进程名为 com.ryg.chapter_2:remote -->
    android:process=":remote" />  
<activity  
    android:name=".ThirdActivity"  
    <!-- 全局进程,进程名为 com.ryg.chapter_2.remote -->
    android:process="com.ryg.chapter_2.remote" />  
  • 多进程的问题 (不同进程的组件会拥有独立的虚拟机,Application和内存空间)
    • 静态变量和单例模式失效。(Android为每个应用分配了独立的虚拟机,不同的虚拟机在内存分配上有不同的地址空间。导致需要内存来共享数据都会失败)
    • 线程同步机制失效(同上)
    • SharedPreference不可靠(并发读写)
    • Application多次创建(Android为新的进程分配独立虚拟机,即启动新应用,会创建新的Application)

2)IPC基础概念

2.1) Serializable和Parcelable

类型 说明
Serializable Java的序列化接口,序列化过程中I/O开销大,可用在将对象序列化存储到设备中或网络传输
Parcelable Android的序列化接口,效率高,建议使用
//自动实现序列化过程
public class User implements Serializable{  
    private static final long serialVersionUID = 8723148825838841922L;  

使用ObjectOutputStream和ObjectInputStream可轻松实现序列化和反序列化过程

// 序列化过程:  
User user = new User(0,"jake",true);  
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));  
out.writeObject(user);  
out.close();  
// 反序列化过程:  
ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));  
User newUser = (User)in.readObject();  
in.close(); 

2.2) Binder机制

为什么 Android 要采用 Binder 作为 IPC 机制?

角度 说明
性能 Binder数据拷贝只需要一次,而管道、消息队列需要2次(即数据先从发送方缓存区拷贝到内核开辟的缓存区中copy_from_user,然后再从内核缓存区拷贝到接收方缓存区copy_to_user()),共享内存不需要拷贝。Socket传输效率低开销大,用于进程间低速通信和跨网络通信。
稳定性 Binder是基于C/S架构的,相对独立,而共享内存没有C/S之别, 需要考虑并发同步问题
安全性 为发送方添加UID/PID身份,Server端会根据权限控制策略,判断应用进程的UID/PID是否满足访问权限

Binder是IPC的重要机制,AMS,WMS等系统服务背后都是Binder。
Binder的架构包括服务端,客户端和Binder驱动

名称 说明
服务端 就是一个Binder对象,内部线程会接收Binder驱动发送的消息,收到消息后会执行onTranscat(),并按照参数执行不同的服务端代码
Binder驱动 工作在内核态,服务端Binder对象创建后,会在驱动中创建mRemote对象,其也是Binder对象,以供客户端访问
客户端 获取驱动中的mRemote引用,然后调用transcat()即可向服务端发送消息
Binder架构

Binder IPC底层原理 -Binder为什么只需要拷贝一次

Binder IPC底层原理

1、Binder驱动在内核空间创建数据接收缓存区
2、在内核空间中开辟一块内核缓存区,建立内核缓存区与数据接收缓存区的映射 以及数据接收缓存区与接收进程用户空间的映射关系。
3、发送方调用copy_from_user()函数将数据拷贝至内核缓存区,由于内核缓存区与接收进程用户空间存在内存映射,即相当于把数据发送至接收进程的用户空间,从而完成了进程通信。

PS:内存映射(mmap),mmap() 是操作系统中一种内存映射的方法,将用户一块内存区域映射至内核空间,建立映射关系后,用户对内存区域的修改可直接反应到内核空间,反之亦然。

2.2.1) 自定义服务的Binder分析

通过Service

public boolean bindService(Intent service, ServiceConnection conn, int flags) 
public interface ServiceConnection {  
  //第二个参数service即为远程服务在Binder驱动中的binder引用
  public void onServiceConnected(ComponentName name, IBinder service);  
  public void onServiceDisconnected(ComponentName name);  
} 
  • 自定义服务-使用Binder进行IPC通信:
//code 标识要调用服务端的哪个函数
//data 对输入参数打包
//reply 对返回值打包
//writeString<--->readString客户端打包一个Parcel对象,在服务端读取该Parcel对象数据
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;  
使用Binder进行IPC通信

客户端

public class MainActivity extends Activity {  
    private boolean isBound;  
    private Button btn_add;  
    private IBinder mRemote = null;  
    private ServiceConnection serviceConn = new ServiceConnection() {    
        @Override    
        public void onServiceConnected(ComponentName name, IBinder service) {  
            //获得该远程服务所对应的Binder驱动中的引用
            mRemote = service;  
        }  
        @Override    
        public void onServiceDisconnected(ComponentName name) {  
        }  
    };  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        bind();  
        btn_add = (Button)findViewById(R.id.btn_add);  
        btn_add.setOnClickListener(new OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                String result = null;  
                try {  
                    result = strcat("abc", "def");  
                } catch (RemoteException e) {  
                    Toast.makeText(MainActivity.this, "error", 0).show();  
                    e.printStackTrace();  
                }  
                Toast.makeText(MainActivity.this, result, 0).show();  
            }  
        });  
    }  
      
    private void bind() {  
        Intent intent = new Intent(MainActivity.this, ComputeService.class);    
        isBound = bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);  
    }  
      
    private void unbind() {  
        if (isBound) {  
            MainActivity.this.unbindService(serviceConn);  
            isBound = false;  
        }  
    }  
      
    private String strcat(String x, String y) throws RemoteException {  
        android.os.Parcel _data = android.os.Parcel.obtain();  
        android.os.Parcel _reply = android.os.Parcel.obtain();  
        String _result;  
        try {  
            _data.writeString(x);  
            _data.writeString(y);  
            mRemote.transact(1, _data, _reply, 0);  
            _result = _reply.readString();  
        } finally {  
            _reply.recycle();  
            _data.recycle();  
        }  
        return _result;  
    }  
  
    @Override  
    protected void onDestroy() {  
        unbind();  
        super.onDestroy();  
    }  
}  

服务端:

public class ComputeService extends Service {  
      
    private IBinder binder = new Binder(){  
        protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {  
            if (code == 1) {  
                String _arg0;  
                _arg0 = data.readString();  
                String _arg1;  
                _arg1 = data.readString();  
                String _result = this.strcat(_arg0, _arg1);  
                reply.writeString(_result);  
                return true;  
            }  
            return super.onTransact(code, data, reply, flags);  
        };  
          
        public String strcat(String x, String y){  
            return x + y;  
        }  
    };  
      
    @Override  
    public IBinder onBind(Intent arg0) {  
        return binder;  
    }  
}  
  • 自定义服务-使用AIDL进行IPC通信:
    AIDL(Android Interface Definition Language),编译器通过*.aidl文件的描述信息生成符合通信协议的Java代码
package org.qhyuan.aidl;  
interface ICompute {  
    String strcat (String x,String y);  
} 
interface ICompute extends IInterface  
{  
    strcat();  
  
    static abstract class Stub extends Binder implements ICompute {  
        static final int TRANSACTION_strcat;  
        static final String DESCRIPTOR;  
        static asInterface();  
        asBinder();  
        onTransact();  
  
        static class Proxy implements ICompute {  
            IBinder binder;  
            asBinder();  
            getInterfaceDescriptor();  
            strcat();  
        }  
    }  
}  

客户端

public class MainActivity extends Activity {  
    private ICompute compute = null;  
    private boolean isBound;  
    private Button btn_add;  
    private ServiceConnection serviceConn = new ServiceConnection() {    
        @Override    
        public void onServiceConnected(ComponentName name, IBinder service) {  
            //asInterface 将一个Binder转为实现接口
            compute = ICompute.Stub.asInterface(service);  
        }  
        @Override    
        public void onServiceDisconnected(ComponentName name) {  
        }  
    };  
      
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
        bind();  
        btn_add = (Button)findViewById(R.id.btn_add);  
        btn_add.setOnClickListener(new OnClickListener() {  
            @Override  
            public void onClick(View v) {  
                String result = null;  
                try {  
                    result = compute.strcat("abc", "def");  
                } catch (RemoteException e) {  
                    Toast.makeText(MainActivity.this, "error", 0).show();  
                    e.printStackTrace();  
                }  
                Toast.makeText(MainActivity.this, result, 0).show();  
            }  
        });  
    }  
      
    private void bind() {  
        Intent intent = new Intent(MainActivity.this, ComputeService.class);    
        isBound = bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);  
    }  
      
    private void unbind() {  
        if (isBound) {  
            MainActivity.this.unbindService(serviceConn);  
            isBound = false;  
        }  
    }  
  
    @Override  
    protected void onDestroy() {  
        unbind();  
        super.onDestroy();  
    }  
}  

服务端

public class ComputeService extends Service {  
      
    private IBinder binder = new ICompute.Stub() {  
        @Override  
        public String strcat(String x, String y) throws RemoteException {  
            return x+y;  
        }  
    };  
      
    @Override  
    public IBinder onBind(Intent arg0) {  
        return binder;  
    }  
}  

2.2.2)系统服务的Binder分析

SystemServer开启各种服务时,调用ServiceManager.addService(String name,IBinder service)将其保存起来,这样当查找服务的时候,就可ServiceManager.getService(String name)来获取远程服务的Binder。

public static void setSystemProcess() {  
    ActivityManagerService m = mSelf;  
    ServiceManager.addService("activity", m, true);  
    ServiceManager.addService("meminfo", new MemBinder(m));  
    ServiceManager.addService("gfxinfo", new GraphicsBinder(m));  
    ServiceManager.addService("dbinfo", new DbBinder(m));  
    // ....  
   }

AMS举例,客户端如何去获取AMS的Binder。
我们知道Activity启动,客户端调用服务器端是通过IActivityManager接口来实现。
IActivityManager对应IXXX接口
ActivityManagerNative对应IXXX.Stub类,继承自Binder类。
ActivityManagerProxy对应IXXX.Stub.Proxy类。

class ActivityManagerProxy implements IActivityManager{}
public final class ActivityManagerService extends ActivityManagerNative{}
public abstract class ActivityManagerNative extends Binder implements 

只要客户端获取远程服务的Binder就可以进行IPC通信了,在ActivityThread的attach方法里有下面两行代码

IActivityManager mgr = ActivityManagerNative.getDefault();  
mgr.attachApplication(mAppThread);  
static public IActivityManager getDefault() {  
    return gDefault.get();  
}  
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {  
        protected IActivityManager create() {  
            // 这里可以看到通过调用getService方法得到Binder  
            IBinder b = ServiceManager.getService("activity");  
            if (false) {  
                Log.v("ActivityManager", "default service binder = " + b);  
            }  
            //将一个IBinder对象转化为它实现的接口
            IActivityManager am = asInterface(b);  
            if (false) {  
                Log.v("ActivityManager", "default service = " + am);  
            }  
            return am;  
        }  
    };  

3)Android中的IPC

  • 3.1)Bundle (Intent传输)
  • 3.2)文件共享(参考上面序列化与反序列化)
  • 3.3)Messenger(轻量级IPC方案)

参考资料

简单说Binder!
《Android开发艺术探索》笔记
郭霖Service AIDL
Android Binder机制原理(史上最强理解,没有之一)
写给 Android 应用工程师的 Binder 原理剖析`

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,056评论 5 474
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,842评论 2 378
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 148,938评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,296评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,292评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,413评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,824评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,493评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,686评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,502评论 2 318
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,553评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,281评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,820评论 3 305
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,873评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,109评论 1 258
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,699评论 2 348
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,257评论 2 341

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,335评论 25 707
  • Android开发艺术探索 第二章IPC机制 Linux中IPC通信方式?答:命名管道,共享内存,信号量(具体再细...
    方木Rudy阅读 1,075评论 0 2
  • 2.1 Android IPC机制任何一个操作系统都需要IPC机制,Linux可以通过共享内存,管道,信号量来进行...
    shuixingge阅读 1,659评论 0 3
  • 一个读书的小Tips:如果对原书内容不是很了解的小伙伴,可以先看读书笔记,心中有个概要,然后再细读原书; 学习内容...
    HuDP阅读 3,324评论 10 27
  • 通过两周的商城项目,受益匪浅,以前自己根本没有项目经验,所以很多知识点都只是知识点而已,并没有在实践中加以熟悉和理...
    dagailv阅读 111评论 0 0