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();
}
}
}