前言

动态加载系列文章
Android 动态加载(一) - 基础篇(一)
Android 动态加载(二) - 基础篇(二)
Android 动态加载(三) - 类的加载流程源码分析
Android 动态加载(四) - 简单demo实现
Android 动态加载(五) - 借尸还魂之代理Activity模式
Android 动态加载(六) - 360开源框架DroidPlugin的使用介绍

1. 插件定义?


插件可以提供一种动态扩展的能力,让app在运行时候可以加载原本不属于该应用的功能,可以做到动态更新和替换;

2. 插件化定义?


把核心业务模块封装成独立的插件,根据不同业务需求进行不同组合,动态进行替换,可以对插件进行管理、更新;

3. 插件化架构主流框架?


1>:Small;
2>:DL动态加载框架;
3>:360的RePlugin;
3>:360的DroidPlugin;
4>:滴滴的VirtualAPK;

4. 插件化架构


插件化架构就是:点击一个 Button按钮,然后从服务器中下载一个 功能的apk,保存到本地,它是单独的一个apk并且是没有运行的,我们需要把它启动起来,并且做到参数传递。比如像早期的微信里边的一些功能:比如 摇一摇、漂流瓶、附近的人、等等其他功能,如下图所示:


Android 动态加载(五) - 借尸还魂之代理Activity模式_第1张图片 插件化架构简介.png

这里我们为了演示方便,就直接 写一个 YaoYIYao的demo,然后运行把它打包,直接放到我们的手机存储目录中,就表示我们已经从服务器中下载了一个 摇一摇的apk,然后我们还需要做的就是:
1>:启动一个 Activity,这个 插件Activity是没有在 清单文件中注册的;
2>:

5. 拦截启动


进入 AndroidPluginDemo项目中,真正执行的类其实是 Singleton里边的 mInstance属性;
1>:获取 ActivityManagerNative里面的 gDefault;
2>:获取gDefault中的 mInstance属性;

3>:这个时候会报错,报错是因为TestActivity没有注册,这个时候我们重新写一个 ProxyActivity先占一个坑,待会就让 ProxyActivity代替 TestActivity去过检测,意思就是让 ProxyActivity在清单文件中代替 TestActivity注册,此时从MainActivity已经可以跳转过来到TestActivity;
4>:最后还需要换回来,hook ActivityManager里面的 mH是一个 Handler:
4.1>>:获取ActivityThread的实例;
4.2>>:获取ActivityThread中的mH;
4.3>>:hook 使用 handleLaunchActivity

流程就是:

从MainActivity中 点击跳转 然后跳转到 TestActivity界面,但是在 清单文件中不需要配置 TestActivity,直接使用hook启动流程 LaunchActivity即可:

效果如下图所示:
Android 动态加载(五) - 借尸还魂之代理Activity模式_第2张图片 图片.png
Android 动态加载(五) - 借尸还魂之代理Activity模式_第3张图片 图片.png

6. 代码如下


1>:MainActivity代码如下:
public class MainActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void click(View view){        Intent intent = new Intent(MainActivity.this , TestActivity.class) ;        startActivity(intent);    }}
2>:TestActivity代码如下:
/** * Email: 2185134304@qq.com * Created by Novate 2018/4/30 17:34 * Version 1.0 * Params: * Description:*/public class TestActivity extends Activity {    @Override    public void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_test);    }}
3>:代理过安检的 ProxyActivity代码如下:
/** * Email: 2185134304@qq.com * Created by Novate 2018/4/30 17:44 * Version 1.0 * Params: * Description:   只是代理过检测的Activity*/public class ProxyActivity extends Activity{}
4>:HookStartActivityUtil代码如下:
/** * Email: 2185134304@qq.com * Created by Novate 2018/4/30 10:34 * Version 1.0 * Params: * Description: * *      对于 源码中的某个类 如果现实 {hide},就表示只能系统去new ,如果自己我们想要创建对象,只能通过下边方式获取 *      Class<?> amnClass = Class.forName("android.app.ActivityManagerNative") ; forName("该类最上边的包名 + 类名")*/public class HookStartActivityUtil {    private Context mContext ;    private Class<?> mProxyClass ;    private final String EXTER_ORIGIN_INTENT = "EXTER_ORIGIN_INTENT";    public HookStartActivityUtil(Context context , Class<?> proxyClass){        this.mContext = context.getApplicationContext() ;  // 防止内存泄露        this.mProxyClass = proxyClass ;    }    public void hookLaunchActivity() throws Exception{        // 1:获取ActivityThread的实例;        Class<?> atClass = Class.forName("android.app.ActivityThread") ;        // 获取ActivityThread中的属性        Field scatThread = atClass.getDeclaredField("sCurrentActivityThread");        scatThread.setAccessible(true);        Object sCurrentActivityThread = scatThread.get(null) ;  // 静态的可以传递 null        // 2:获取ActivityThread中的mH;        Field mHField = atClass.getDeclaredField("mH");        mHField.setAccessible(true);        Object mHandler = mHField.get(sCurrentActivityThread);        // 3:hook  使用 handleLaunchActivity        // 给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){  // 根据Handler源码可知                handleLaunchMessage(msg) ;            }            return false;        }    }    /**     * 开始启动创建Activity拦截     */    private void handleLaunchMessage(Message msg) {        try {            Object record = msg.obj ;            // 1. 从ActivityClientRecord中获取过安检的 intent            Field intentField = record.getClass().getDeclaredField("intent") ;            intentField.setAccessible(true);            Intent safeIntent = (Intent) intentField.get(record);            // 2. 获取到过安检的intent之后 ,从safeIntent中获取原来的 originIntent            Intent originIntent = safeIntent.getParcelableExtra(EXTER_ORIGIN_INTENT) ;            // 3. 重新设置回去            if (originIntent != null){                intentField.set(record , originIntent);            }        } catch (Exception e) {            e.printStackTrace();        }    }    public void hookStartActivity() throws Exception{        // 1>:获取 ActivityManagerNative里面的 gDefault;        Class<?> amnClass = Class.forName("android.app.ActivityManagerNative") ;        // 通过 ActivityManagerNative 类 获取 gDefault属性        Field gDefaultField = amnClass.getDeclaredField("gDefault");        gDefaultField.setAccessible(true);  // 设置权限        Object gDefault = gDefaultField.get(null) ;        // 2>:获取gDefault中的 mInstance属性;        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} ,                // InvocationHandler:必须有一个执行者,就是谁去执行这个方法                new StartActivityInvocationHandler(iamInstance)) ;        // 3>:重新指定        mInstanceField.set(gDefault , iamInstance);    }    private class StartActivityInvocationHandler implements InvocationHandler{        // 这个才是方法的执行者        private Object mObject ;        // 通过构造方法把mObject传递进来        public StartActivityInvocationHandler(Object object){            this.mObject = object ;        }        @Override        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {            // 在这里可以 hook到 IActivityManager中所有的方法            Log.e("TAG" , method.getName()) ;            // 替换intent ,过AndroidManifest.xml 检测            if (method.getName().equals("startActivity")){                // 1. 首先获取原来的intent                Intent originIntent = (Intent) args[2];                // 2. 创建一个安全的intent                Intent safeIntent = new Intent(mContext , mProxyClass) ;                // 3. 替换第二个参数                args[2] = safeIntent ;                // 4. 绑定原来的Intent                safeIntent.putExtra(EXTER_ORIGIN_INTENT , originIntent) ;            }            return method.invoke(mObject , args);        }    }}
5>:BaseApplication代码如下:
/** * Email: 2185134304@qq.com * Created by Novate 2018/4/30 8:41 * Version 1.0 * Params: * Description:*/public class BaseApplication extends Application {    @Override    public void onCreate() {        super.onCreate();        HookStartActivityUtil hookStartActivityUtil = new HookStartActivityUtil(this , ProxyActivity.class) ;        try {            hookStartActivityUtil.hookStartActivity();            hookStartActivityUtil.hookLaunchActivity();        } catch (Exception e) {            e.printStackTrace();        }    }}
6>:AndroidManifest.xml文件如下:
<?xml version="1.0" encoding="utf-8"?>                                                                                        
以上代码就可以实现不需要在 清单文件中注册 TestActivity,直接通过代理过安检的 ProxyActivity 就可以 从MainActivity 跳转到 TestActivity,但是有一个问题,上边的 TestActivity是继承 自Activity的,如果让 TestActivity继承 AppCompatActivity就会报错,下边就来解决这个问题。

7. 解决 TestActivity继承自 AppCompatActivity绕不过去的问题


ActivityThread源码中的 getPackageManager()方法如下:


Android 动态加载(五) - 借尸还魂之代理Activity模式_第4张图片 图片.png
思路就是:

因为上边的 getPackageManager()方法是静态的方法,可以在 开始启动创建Activity拦截 handleLaunchActivity方法中先调用一次,这个时候 sPackageManager就会有实例,那么我再次进来就获取的是已经实例好的 sPackageManager,代码如下:

            /**             * 兼容AppCompatActivity报错问题             */            Class<?> forName = Class.forName("android.app.ActivityThread");            Field field = forName.getDeclaredField("sCurrentActivityThread");            field.setAccessible(true);            Object activityThread = field.get(null);            // 我自己执行一次那么就会创建PackageManager,系统再获取的时候就是下面的iPackageManager            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 = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),                    new Class<?>[]{iPackageManagerIntercept}, handler);            // 获取 sPackageManager 属性            Field iPackageManagerField = activityThread.getClass().getDeclaredField("sPackageManager");            iPackageManagerField.setAccessible(true);            iPackageManagerField.set(activityThread, proxy);
class PackageManagerHandler implements InvocationHandler {        private Object mActivityManagerObject;        public PackageManagerHandler(Object iActivityManagerObject) {            this.mActivityManagerObject = iActivityManagerObject;        }        @Override        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {            // Log.e("TAG", "methodName = " + method.getName());            if (method.getName().startsWith("getActivityInfo")) {                ComponentName componentName = new ComponentName(mContext, mProxyClass);                args[0] = componentName;            }            return method.invoke(mActivityManagerObject, args);        }    }

下一节会讲解360开源框架DroidPlugin的使用介绍,代码会在下一节中上传至github。

更多相关文章

  1. 【Android NDK 开发】Ubuntu 函数库交叉编译 ( Android 动态库交
  2. 通过xml加载菜单Menus
  3. android之webView加载javascropt
  4. Android插件配置-Android Extension介绍
  5. android广播动态注册与发送流程分析
  6. Android ListView列表 刷新和加载更多
  7. PullToRefreshLayout +RecyclerView 实现上拉加载下拉刷新
  8. 图片加载的几种模式
  9. ImageView下载图片加载

随机推荐

  1. Android(安卓)吸入动画效果实现分解
  2. Android(安卓)Studio上使用GBK编码
  3. Android(安卓)四大组件 —— 服务
  4. 各版本安卓手机USB调试模式打开方法
  5. Mac OS X 下部分Android手机无法连接adb
  6. Android的事件分发机制以及滑动冲突的解
  7. iOS程序员学习android之一
  8. Android设备上opencv开发:获得Camera数据
  9. android dialog省市区三级联动滚动选取效
  10. 【Android(安卓)开发】:UI控件之 AlertDia