了解了Java的动态代理设计模式以后,配合上一期的文章Android插件化架构 - Activity的启动流程分析,那么接下来就须要亲自操刀去拦截Activity的启动流程了。前面好事没少干,那么如今就来干干坏事,到底怎样才能让没有注册的Activity启动不报错呢?答案就是Hook下钩子。 android
怎么样去找Hook点是个问题,把钩子下在哪里呢?通常的套路确定最好是静态,而后是接口,配合反射注入就能够了。Activity启动流程的源码我就再也不贴了,若是不了解请移步这里Android插件化架构 - Activity的启动流程分析,我这里直接下钩子。设计模式
/**
* hook start activity
*/
public void hookStartActivity() throws Exception{
// 先获取ActivityManagerNative中的gDefault
Class<?> amnClazz = Class.forName("android.app.ActivityManagerNative");
Field defaultField = amnClazz.getDeclaredField("gDefault");
defaultField.setAccessible(true);
Object gDefaultObj = defaultField.get(null);
// 获取Singleton里面的mInstance
Class<?> singletonClazz = Class.forName("android.util.Singleton");
Field amsField = singletonClazz.getDeclaredField("mInstance");
amsField.setAccessible(true);
Object amsObj = amsField.get(gDefaultObj);
// 动态代理Hook下钩子
amsObj = Proxy.newProxyInstance(mContext.getClass().getClassLoader(),
amsObj.getClass().getInterfaces(),
new StartActivityInvocationHandler(amsObj));
// 注入
amsField.set(gDefaultObj,amsObj);
}
/**
* Start Activity Invocation Handler
*/
private class StartActivityInvocationHandler implements InvocationHandler{
private Object mAmsObj;
public StartActivityInvocationHandler(Object amsObj){
this.mAmsObj = amsObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 拦截到全部ActivityManagerService的方法
Log.e("TAG","methodName"+method.getName());
return method.invoke(mAmsObj,args);
}
}
复制代码
上面咱们已经拦截到了Activity的启动了,也可以看到startActivity方法的打印。可是若是不作任何处理仍是会蹦,那么咱们须要有一个Activity预先在AndroidMnifest.xml中注册一下,它是不怕太阳的,经过它能够作到借尸还魂。bash
/**
* Start Activity Invocation Handler
*/
private class StartActivityInvocationHandler implements InvocationHandler{
private Object mAmsObj;
public StartActivityInvocationHandler(Object amsObj){
this.mAmsObj = amsObj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 拦截到全部ActivityManagerService的方法
Log.e("TAG","methodName"+method.getName());
if(method.getName().equals("startActivity")){
// 启动Activity的方法,找到原来的Intent
Intent realIntent = (Intent) args[2];
// 代理的Intent
Intent proxyIntent = new Intent();
proxyIntent.setComponent(new ComponentName(mContext,mProxyActivity));
// 把原来的Intent绑在代理Intent上面
proxyIntent.putExtra("realIntent",realIntent);
// 让proxyIntent去晒太阳,借尸
args[2] = proxyIntent;
}
return method.invoke(mAmsObj,args);
}
}
复制代码
还魂架构
/**
* hook Launch Activity
*/
public void hookLaunchActivity() throws Exception{
// 获取ActivityThread
Class<?> activityThreadClazz = Class.forName("android.app.ActivityThread");
Field sCurrentActivityThreadField = activityThreadClazz.getDeclaredField("sCurrentActivityThread");
sCurrentActivityThreadField.setAccessible(true);
Object sCurrentActivityThreadObj = sCurrentActivityThreadField.get(null);
// 获取Handler mH
Field mHField = activityThreadClazz.getDeclaredField("mH");
mHField.setAccessible(true);
Handler mH = (Handler) mHField.get(sCurrentActivityThreadObj);
// 设置Callback
Field callBackField = Handler.class.getDeclaredField("mCallback");
callBackField.setAccessible(true);
callBackField.set(mH, new ActivityThreadHandlerCallBack());
}
class ActivityThreadHandlerCallBack implements Handler.Callback {
@Override
public boolean handleMessage(Message msg) {
if (msg.what == LAUNCH_ACTIVITY) {
handleLaunchActivity(msg);
}
return false;
}
}
// 还魂
private void handleLaunchActivity(Message msg) {
// final ActivityClientRecord r = (ActivityClientRecord) msg.obj;
try {
Object obj = msg.obj;
Field intentField = obj.getClass().getDeclaredField("intent");
intentField.setAccessible(true);
Intent proxyIntent = (Intent) intentField.get(obj);
// 代理意图
Intent originIntent = proxyIntent.getParcelableExtra(EXTRA_ORIGIN_INTENT);
if (originIntent != null) {
// 替换意图
intentField.set(obj, originIntent);
}
} catch (Exception e) {
e.printStackTrace();
}
}
复制代码
继承自Activity是百试百灵,不再须要在AndroidMnifest中注册了,可是发现继承AppCompatActivity仍是会报错,我都不记得当时是怎么解决这个问题的,反正搞了好几天,我选择遗忘那段操蛋的时光。app
// 兼容AppCompatActivity报错问题
Class<?> forName = Class.forName("android.app.ActivityThread");
Field field = forName.getDeclaredField("sCurrentActivityThread");
field.setAccessible(true);
Object activityThread = field.get(null);
Method getPackageManager = activityThread.getClass().getDeclaredMethod("getPackageManager");
Object iPackageManager = getPackageManager.invoke(activityThread);
PackageManagerHandler handler = new PackageManagerHandler(iPackageManager);
Class<?> iPackageManagerIntercept = Class.forName("android.content.pm.IPackageManager");
Object proxy = newProxyInstance(Thread.currentThread().getContextClassLoader(),
new Class<?>[]{iPackageManagerIntercept}, handler);
// 获取 sPackageManager 属性
Field iPackageManagerField = activityThread.getClass().getDeclaredField("sPackageManager");
iPackageManagerField.setAccessible(true);
iPackageManagerField.set(activityThread, proxy);
复制代码
总算走出了插件化架构的一小步,过程对于通常人来说仍是有点痛苦的,可是结果带来那种成就感仍是值得的,后面咱们解决一下资源和布局的加载问题,而后介绍一下360开源的插件化框架DroidPlugin,分析一下它的源码就直接拿过用吧。框架
全部分享大纲:2017Android进阶之路与你同行ide
视频讲解地址:http://pan.baidu.com/s/1o8bPZ9C布局