插件化知识详细分解及原理 之Binder机制

转http://blog.csdn.net/yulong0809/article/details/56841993。

最近一直在研究插件化的东西,我看了网上大多都是直接上来就开始讲解原理然后写个demo,这样对于很多没有入门的朋友不是很好的理解,下面我会通过自己的研究过程,一步一步循序渐进的将插件化需要的知识点都梳理一遍及讲解,其实学习插件化的好处并不全因为它是一门热门的技术,插件化涉及的知识点很多,可以让我们对android的理解及境界上都会有一个质的飞跃,在我将所有设计的知识点都大概讲一遍后会用一个demo来实现插件化,里面将设计所有讲过的知识。

插件化其实就是动态加载,动态加载又包括了代码加载和资源加载。

可以干什么:

插件化最早出现是因为65535问题出现的,用于查分多个dex并动态加载dex来防止65535问题

现在很多公司用插件化来做模块的动态加载,这样既能实现web端一样的随时更新,还能减少apk包的体积,其实就是加载一个未安装的apk。

热修复,热修复其实也是动态加载原理

换肤,使用动态的资源加载可以实现换肤功能

还可以通过hook系统的一些类做一些你想做的坏事。

目前比较有名的插件化框架:

任玉刚的:dynamic-load-apk,这个项目使用的是一种代理的方式去实现 

https://github.com/singwhatiwanna/dynamic-load-apk

360的:DroidPlugin,这个项目是通过hook系统类来实现 

https://github.com/Qihoo360/DroidPlugin

目前比较火的热修复框架:

阿里的:andfix,用于对方法的修复,可以立即生效,不支持资源及类替换 

https://github.com/alibaba/AndFix

腾讯的:tinker,除了不支持立即生效,全部支持

https://github.com/Tencent/tinker

美团的:robust,不开源

如果要使用插件化来作为模块化的话,那么就需要解决两个问题

代码的加载,就是使用ClassLoader加载代码

资源的加载,使用AssetManager的隐藏方法,addAsssetPath方法加入一个资源路径来获取这个资源的Resource资源

还有一个问题就是对四大组件的生命周期管理

准备:

在了解插件化之前首先需要了解及掌握的知识点 

一、Binder机制 

二、代理模式, 

三、反射 

四、类加载及dex加载 

五、应用启动过程及类加载过程 

六、实现插件化完整demo及思路分析 

七、动态加载资源及解决资源冲突问题

Binder机制:

其实Binder看你怎么去理解,如果从代码角度的话他是一个类,如果从硬件角度的话他是一个驱动,如果从IPC角度的话他是一种通信机制,是framework层的各种ServiceManager的链接桥梁, 

我们知道我们平时使用的系统服务对象其实都是系统的,他们存在的进程和我们的应用并不在一个进程中,但是为什么我们能直接使用呢?其实就是因为Binder的存在,跨进程通信,再说大白话一点就是使用了我们经常说的aidl,Binder很复杂,这里只是为了插件化做铺垫,想深入理解请自行查阅资料。

进程间通信过程

1.首先客户端要链接服务端

2.然后服务端会返回一个客户端的对象(代理对象)

3.然后客户端使用这个代理对象其中的方法时,系统会先调用服务端的方法,然后将运算的结果返回给客户端(要知道其实并不是用了这个对象的方法,而是去服务端里运算,然后在返回给客户端的)

我们通过自己写一个aidl,然后和系统的源码进行对比

//我们自己写的aidl的接口

//IMyAidlInterface.aidl

package com.huanju.chajianhuatest;

import com.huanju.chajianhuatest.aidlmode.TestBean;

interface IMyAidlInterface {

    /**

    * Demonstrates some basic types that you can use as parameters

    * and return values in AIDL.

    */

    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,

            double aDouble, String aString);

    String getS(in TestBean s);

    TestBean getInfoBean(out TestBean b);

}

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

系统会帮我们自动创建一个IMyAidlInterface.java的文件,我们去看看

>

package com.huanju.chajianhuatest;

public interface IMyAidlInterface extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements com.huanju.chajianhuatest.IMyAidlInterface {

        private static final java.lang.String DESCRIPTOR = "com.huanju.chajianhuatest.IMyAidlInterface";

        public Stub() {

            this.attachInterface(this, DESCRIPTOR);

        }

        public static com.huanju.chajianhuatest.IMyAidlInterface asInterface(android.os.IBinder obj) {

            if ((obj == null)) {

                return null;

            }

            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);

            if (((iin != null) && (iin instanceof com.huanju.chajianhuatest.IMyAidlInterface))) {

                return ((com.huanju.chajianhuatest.IMyAidlInterface) iin);

            }

            return new com.huanju.chajianhuatest.IMyAidlInterface.Stub.Proxy(obj);

        }

        @Override

        public android.os.IBinder asBinder() {

            return this;

        }

        @Override

        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {

            switch (code) {

                case INTERFACE_TRANSACTION: {

                    reply.writeString(DESCRIPTOR);

                    return true;

                }

                case TRANSACTION_basicTypes: {

                    data.enforceInterface(DESCRIPTOR);

                    int _arg0;

                    _arg0 = data.readInt();

                    long _arg1;

                    _arg1 = data.readLong();

                    boolean _arg2;

                    _arg2 = (0 != data.readInt());

                    float _arg3;

                    _arg3 = data.readFloat();

                    double _arg4;

                    _arg4 = data.readDouble();

                    java.lang.String _arg5;

                    _arg5 = data.readString();

                    this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);

                    reply.writeNoException();

                    return true;

                }

                case TRANSACTION_getS: {

                    data.enforceInterface(DESCRIPTOR);

                    com.huanju.chajianhuatest.aidlmode.TestBean _arg0;

                    if ((0 != data.readInt())) {

                        _arg0 = com.huanju.chajianhuatest.aidlmode.TestBean.CREATOR.createFromParcel(data);

                    } else {

                        _arg0 = null;

                    }

                    java.lang.String _result = this.getS(_arg0);

                    reply.writeNoException();

                    reply.writeString(_result);

                    return true;

                }

                case TRANSACTION_getInfoBean: {

                    data.enforceInterface(DESCRIPTOR);

                    com.huanju.chajianhuatest.aidlmode.TestBean _arg0;

                    _arg0 = new com.huanju.chajianhuatest.aidlmode.TestBean();

                    com.huanju.chajianhuatest.aidlmode.TestBean _result = this.getInfoBean(_arg0);

                    reply.writeNoException();

                    if ((_result != null)) {

                        reply.writeInt(1);

                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);

                    } else {

                        reply.writeInt(0);

                    }

                    if ((_arg0 != null)) {

                        reply.writeInt(1);

                        _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);

                    } else {

                        reply.writeInt(0);

                    }

                    return true;

                }

            }

            return super.onTransact(code, data, reply, flags);

        }

        private static class Proxy implements com.huanju.chajianhuatest.IMyAidlInterface {

            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {

                mRemote = remote;

            }

            @Override

            public android.os.IBinder asBinder() {

                return mRemote;

            }

            public java.lang.String getInterfaceDescriptor() {

                return DESCRIPTOR;

            }

            @Override

            public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException {

                android.os.Parcel _data = android.os.Parcel.obtain();

                android.os.Parcel _reply = android.os.Parcel.obtain();

                try {

                    _data.writeInterfaceToken(DESCRIPTOR);

                    _data.writeInt(anInt);

                    _data.writeLong(aLong);

                    _data.writeInt(((aBoolean) ? (1) : (0)));

                    _data.writeFloat(aFloat);

                    _data.writeDouble(aDouble);

                    _data.writeString(aString);

                    mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);

                    _reply.readException();

                } finally {

                    _reply.recycle();

                    _data.recycle();

                }

            }

            @Override

            public java.lang.String getS(com.huanju.chajianhuatest.aidlmode.TestBean s) throws android.os.RemoteException {

                android.os.Parcel _data = android.os.Parcel.obtain();

                android.os.Parcel _reply = android.os.Parcel.obtain();

                java.lang.String _result;

                try {

                    _data.writeInterfaceToken(DESCRIPTOR);

                    if ((s != null)) {

                        _data.writeInt(1);

                        s.writeToParcel(_data, 0);

                    } else {

                        _data.writeInt(0);

                    }

                    mRemote.transact(Stub.TRANSACTION_getS, _data, _reply, 0);

                    _reply.readException();

                    _result = _reply.readString();

                } finally {

                    _reply.recycle();

                    _data.recycle();

                }

                return _result;

            }

            @Override

            public com.huanju.chajianhuatest.aidlmode.TestBean getInfoBean(com.huanju.chajianhuatest.aidlmode.TestBean b) throws android.os.RemoteException {

                android.os.Parcel _data = android.os.Parcel.obtain();

                android.os.Parcel _reply = android.os.Parcel.obtain();

                com.huanju.chajianhuatest.aidlmode.TestBean _result;

                try {

                    _data.writeInterfaceToken(DESCRIPTOR);

                    mRemote.transact(Stub.TRANSACTION_getInfoBean, _data, _reply, 0);

                    _reply.readException();

                    if ((0 != _reply.readInt())) {

                        _result = com.huanju.chajianhuatest.aidlmode.TestBean.CREATOR.createFromParcel(_reply);

                    } else {

                        _result = null;

                    }

                    if ((0 != _reply.readInt())) {

                        b.readFromParcel(_reply);

                    }

                } finally {

                    _reply.recycle();

                    _data.recycle();

                }

                return _result;

            }

        }

        static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

        static final int TRANSACTION_getS = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);

        static final int TRANSACTION_getInfoBean = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);

    }

    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;

    public java.lang.String getS(com.huanju.chajianhuatest.aidlmode.TestBean s) throws android.os.RemoteException;

    public com.huanju.chajianhuatest.aidlmode.TestBean getInfoBean(com.huanju.chajianhuatest.aidlmode.TestBean b) throws android.os.RemoteException;

}

看着代码好像很多,但是其实没什么,我们分析一下结构

1.我们根据上面的代码,创建的类继承了IInterface接口

2.内部类Stub继承Binder

3.看asInterface方法,判断如果不是一个进程会返回代理类

4.每个方法都对应一个id,用于在跨进程访问时确定访问的是哪个方法,通过transact方法再调用服务端的onTransact方法

我们再看一下系统的类,就看我们最熟悉的ActivityManager,要知道ActivityManager其实是ActivityManagerService在我们进程中的一个代理包装类,他内部全部使用 ActivityManagerNative.getDefault()去进程操作,那么我么直接看 ActivityManagerNative的部分代码

1.asInterface

    /** {@hide} */

public abstract class ActivityManagerNative extends Binder implements IActivityManager

{

    /**

    * Cast a Binder object into an activity manager interface, generating

    * a proxy if needed.

    */

    static public IActivityManager asInterface(IBinder obj) {

        if (obj == null) {

            return null;

        }

        IActivityManager in =

            (IActivityManager)obj.queryLocalInterface(descriptor);

        if (in != null) {

            return in;

        }

        return new ActivityManagerProxy(obj);

    }


2.onTransact

@Override

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)

        throws RemoteException {

    switch (code) {

    case START_ACTIVITY_TRANSACTION:

    {

        data.enforceInterface(IActivityManager.descriptor);

        IBinder b = data.readStrongBinder();

        IApplicationThread app = ApplicationThreadNative.asInterface(b);

        String callingPackage = data.readString();

        Intent intent = Intent.CREATOR.createFromParcel(data);

        String resolvedType = data.readString();

        IBinder resultTo = data.readStrongBinder();

        String resultWho = data.readString();

        int requestCode = data.readInt();

        int startFlags = data.readInt();

        ProfilerInfo profilerInfo = data.readInt() != 0

                ? ProfilerInfo.CREATOR.createFromParcel(data) : null;

        Bundle options = data.readInt() != 0

                ? Bundle.CREATOR.createFromParcel(data) : null;

        int result = startActivity(app, callingPackage, intent, resolvedType,

                resultTo, resultWho, requestCode, startFlags, profilerInfo, options);

        reply.writeNoException();

        reply.writeInt(result);

        return true;

    }


代码太多我们就看这两个就好了,有没有感觉很熟悉,和我们自己写的aidl几乎没有区别,他虽然叫做ActivityManagerNative,其实他就是我们自己写的aidl里的内部类Stub。

下面我们分析一下aidl的运行过程

我们直接看如果是远程的话返回了代理对象,我们看代理对象的方法,这个方法就是aidl结果定义的方法,看他怎么实现的 

>

@Override

        public com.huanju.chajianhuatest.aidlmode.TestBean getInfoBean(com.huanju.chajianhuatest.aidlmode.TestBean b) throws android.os.RemoteException {

            android.os.Parcel _data = android.os.Parcel.obtain();

            android.os.Parcel _reply = android.os.Parcel.obtain();

            com.huanju.chajianhuatest.aidlmode.TestBean _result;

            try {

                _data.writeInterfaceToken(DESCRIPTOR);

                mRemote.transact(Stub.TRANSACTION_getInfoBean, _data, _reply, 0);

                _reply.readException();

                if ((0 != _reply.readInt())) {

                    _result = com.huanju.chajianhuatest.aidlmode.TestBean.CREATOR.createFromParcel(_reply);

                } else {

                    _result = null;

                }

                if ((0 != _reply.readInt())) {

                    b.readFromParcel(_reply);

                }

            } finally {

                _reply.recycle();

                _data.recycle();

            }

            return _result;

        }

    }


客户端发起请求

1.首先创建输出类型data

2.创建接受类型reply

3.创建需要的参数

4.将参数写入data中

5.发起远程调用,当前线程挂起调用mRemote.transact()方法,这个方法的实现在Binder中,他会调用服务端的onTransact方法,直到有返回结果

6.从_result中取回返回结果_result

服务端接到请求会走到onTransact方法,这个方法运行在服务端的Binder线程池中,我们再看看怎么实现的

>

  case TRANSACTION_getInfoBean: {

                data.enforceInterface(DESCRIPTOR);

                com.huanju.chajianhuatest.aidlmode.TestBean _arg0;

                _arg0 = new com.huanju.chajianhuatest.aidlmode.TestBean();

                com.huanju.chajianhuatest.aidlmode.TestBean _result = this.getInfoBean(_arg0);

                reply.writeNoException();

                if ((_result != null)) {

                    reply.writeInt(1);

                    _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);

                } else {

                    reply.writeInt(0);

                }

                if ((_arg0 != null)) {

                    reply.writeInt(1);

                    _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);

                } else {

                    reply.writeInt(0);

                }

                return true;

            }


1.通过id确定访问哪个方法

2.然后从目标参数data取出需要的参数

3.然后调用请求的相应方法

4.将返回值写入reply

5.返回true,这里需要说一下,如果返回false,代表客户端访问失败,我们实际当中可根据这个特性来做远程的校检,毕竟我们的远程方法并是不想让任何人都可以访问的。 

,通过id确定访问哪个方法,然后从目标参数data取出需要的参数,然后调用相应方法,将返回值写入reply,

好了,到这里Binder的通信过程就完了,其实我们看到了只要我们理解了我们自己写的aidl的流程及原理,那么系统层的通信也是这样的。下一篇我们继续说代理模式及反射。

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

推荐阅读更多精彩内容

  • 虽然跨进程通信的内在机制十分复杂。但是Android为我们提供了一套非常简单的机制,让我们仅需要很少的步骤就能完成...
    passerbywhu阅读 604评论 0 0
  • 上篇文章介绍了IPC机制的基本概念以及简单使用,文章链接:Android 关于IPC机制的理解(一) 这篇文章主要...
    老实任阅读 695评论 0 2
  • AIDL(Android Interface Definition Language),即Android接口定义语...
    小编阅读 1,453评论 4 7
  • 早上练了半个多小时的口语跟读联系。以前总想着要快点结束,总是草草了事。但内心总是有个声音再说,这种敷衍了事的事了行...
    小鱼记事录阅读 269评论 0 0
  • 散上几片 往事在杯中沉淀 青绿便诱惑着我们 细嚼每一滴清香 也不是茶叶的滋味 腾腾热气飘散的记忆 只在唇边留下丝丝甜蜜
    茜茜核桃妈阅读 223评论 0 0