2018-05-18 HOOK拦截View的创建

// 换肤

appcompatactivity和activity的区别


1 LayoutInflater源码 (布局填充)

https://blog.csdn.net/u012702547/article/details/52628453?locationNum=10&fps=1

1.1 LayoutInflater.from(this)获取源码

//获取系统的服务

LayoutInflater LayoutInflater =

        (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

LayoutInflater.from(this)是系统服务(HashMap 保存一些系统服务) 它是我们的单例设计模式  在内存中只有一个实例

//走自己的Factory

LayoutInflater layoutInflater=LayoutInflater.from(this);

LayoutInflaterCompat.setFactory(layoutInflater, new LayoutInflaterFactory() {

    @Override

    public View onCreateView(View parent, String name, Context context, AttributeSet attrs) {

        if(name.equals("Button")){  //把Button控件设置自己的Textview

            TextView tv=new TextView(MainActivity.this);

            tv.setText("拦截");

            return  tv;

        }

        return null;

    }

});

1.2 LayoutInflater中inflate方法两个参数和三个参数的区别

inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot)  

root不为null,attachToRoot为true

第三个参数设置为了true,表示将第一个参数所指定的布局添加到第二个参数的View中

root不为null,attachToRoot为false

表示不将第一个参数所指定的View添加到root中,那么这个时候有的小伙伴可能就有疑问了,既然不添加到root中,那我还写这么多干嘛?我第二个参数直接给null不就可以了?其实不然,这里涉及到另外一个问题:我们在开发的过程中给控件所指定的layout_width和layout_height到底是什么意思?该属性的表示一个控件在容器中的大小,就是说这个控件必须在容器中,这个属性才有意义,否则无意义。这就意味着如果我直接将linearlayout加载进来而不给它指定一个父布局,则inflate布局的根节点的layout_width和layout_height属性将会失效(因为这个时候linearlayout将不处于任何容器中,那么它的根节点的宽高自然会失效)。如果我想让linearlayout的根节点有效,又不想让其处于某一个容器中,那我就可以设置root不为null,而attachToRoot为false。 即root会协助linearlayout的根节点生成布局参数,只有这一个作用。

root为null

当root为null时,不论attachToRoot为true还是为false,显示效果都是一样的。当root为null表示我不需要将第一个参数所指定的布局添加到任何容器中,同时也表示没有任何容器来来协助第一个参数所指定布局的根节点生成布局参数。

2 代理设计模式使用场景

2.1 mvp架构  

        静态代理动态代理都要用   p 绑定 v, p 解绑 v    每个方面都需要判断v还在不在(动态代理)

2.2 DL插件化架构

        Activity生命周期都是由代理的Activity调用的,静态代理。

2.3 数据库懒加载

        就要使用静态代理。用到了才去加载

2.4 xutils的源码也用到了

        setOnclickLstener setOntouchListner等等  都是动态代理设计模式

      Man man = new Man("张");

//        BankWorker bankWorker = new BankWorker(man);

//        bankWorker.applyBank();

        //返回目标接口IBank的实例对象    代理了所有方法而且都会走BankInvocationHandler里面的 invoke方法

        IBank bank = (IBank) Proxy.newProxyInstance(IBank.class.getClassLoader(),//ClassLoader

//                new Class[]{IBank.class},  //目标接口数组

                man.getClass().getInterfaces(), //目标接口数组

                new BankInvocationHandler(man)

        );

    // 动态代理    解析interface 所有方法 新建一个class  class的名  包名+$Proxy  实例化proxy对象

      proxy里面包含InvocationHandler,每次调方法 其实执行的是InvocationHandler的invoke方法

        // 调这个方法时  来到BankInvocationHandler的invoke方法

        bank.applyBank();

        bank.lostBank();

3.静态代理

        重点:事例代码,现在去银行办理业务,业务员(代理),速度,流程发生变化,好处

        不好的地方:每个方法其实类似,代码量会上去

//替换Intent过安检

public HookStartActivityUtil(Context context, Class proxyClass) {

    this.mContent = context.getApplicationContext();

    this.mProxyClass = proxyClass;

}

public void hookStartActivity() throws Exception {

    // 获取ActivityManagerNative里面的gDefault

    Class amnClass = Class.forName("android.app.ActivityManagerNative");

    //获取属性

    Field gDefaultField = amnClass.getDeclaredField("gDefault");

    gDefaultField.setAccessible(true);

    Object gDefault = gDefaultField.get(null);

    // 获取gDefault 中的instance属性

    Class singletonClass = Class.forName("android.util.Singleton");

    Field mInstanceField = singletonClass.getDeclaredField("mInstance");

    mInstanceField.setAccessible(true);

    Object iamInstance = mInstanceField.get(gDefault);

    Class iamclass = Class.forName("android.app.IActivityManager");

    iamInstance = Proxy.newProxyInstance(HookStartActivityUtil.class.getClassLoader(),

            new Class[]{iamclass},

            new StartInvocationHandler(iamInstance)

    );

    //重新指定

    mInstanceField.set(gDefault, iamInstance);

}

private class StartInvocationHandler implements InvocationHandler {

    private Object object2;

    public StartInvocationHandler(Object object) {

        this.object2 = object;

    }

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        Log.e(TAG, method.getName());

        if (method.getName().equals("startActivity")) {

            //首先获取原來的intent

            Intent originItent = (Intent) args[2];

            //创建一个安全

            Intent safeIntent = new Intent(mContent, mProxyClass);

            args[2] = safeIntent;

            //绑定原来的intent

            safeIntent.putExtra(EXTRA_ORIGIN_INTENT, originItent);

        }

        return method.invoke(object2, args);

    }

}

// 重新设置原来的Intent

public void hookLaunchActivity() throws Exception {

    //获取ActivityThread实例

    Class atClass = Class.forName("android.app.ActivityThread");

    Field atField = atClass.getDeclaredField("sCurrentActivityThread");

    atField.setAccessible(true);

    Object sCurrentActivityThread = atField.get(null);  //sCurrentActivityThread  就是ActivityThread

    //获取ActivityThread中的mH

    Field mHField = atClass.getDeclaredField("mH");

    mHField.setAccessible(true);

    Object mHandler = mHField.get(sCurrentActivityThread);

    //给Handler设置CallBack回调 反射

    Class handlerClass = Class.forName("android.os.Handler");

    Field mCallbackField = handlerClass.getDeclaredField("mCallback");

    mCallbackField.setAccessible(true);

    mCallbackField.set(mHandler, new HandlerCallBack());

}

private class HandlerCallBack implements Handler.Callback {

    @Override

    public boolean handleMessage(Message msg) {

        //没发一个消息  都会走callback方法

        if (msg.what == 100) {

            Log.e(TAG,"handmessage");

            handLaunchActivity(msg);

        }

        return false;

    }

    /**

    * 启动创建activity拦截

    *

    * @param msg

    */

    private void handLaunchActivity(Message msg) {

        try {

            Object record = msg.obj;//ActivityThread中的ActivityClientRecord

            //1 从record获取过安检的Intent

            Field intentField = record.getClass().getDeclaredField("intent");

            intentField.setAccessible(true);

            Intent safeIntent = (Intent) intentField.get(record);

            // 2 从safeIntent中获取原来的intent

            Intent originIntent = safeIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT);

            // 3 重新设置回去

            if (originIntent != null) {

                intentField.set(record, originIntent);

            }

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

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

推荐阅读更多精彩内容