当前位置: 代码迷 >> Android >> Android 暖更新——非侵入AOP框架
  详细解决方案

Android 暖更新——非侵入AOP框架

热度:18   发布时间:2016-04-27 23:12:32.0
Android 热更新——非侵入AOP框架

Android 客户端应用上线以后,一旦出现Bug,一般的解决思路是发修复包升级应用,这种方式不仅耗时,更重要的是用户需要频繁的升级版本,体验不好,所以优化的思路是在不发版本的情况下热更新,以期提高用户体验。

近期GitHub新出一种非侵入运行期AOP框架Dexposed, 下面简单了解一下这个框架,GitHub地址。

简要说明:

该框架基于AOP思想,支持经典的AOP使用场景,可应用于日志记录,性能统计,安全控制,事务处理,异常处理等方面。
针对Android平台,Dexposed支持函数级别的在线热更新,例如对已经发布在应用市场上的宿主APK,当我们从crash统计平台上发现某个函数调用有bug,导致经常性crash,这时,可以在本地开发一个补丁APK,并发布到服务器中,宿主APK下载这个补丁APK并集成后,就可以很容易修复这个crash。
Dexposed是基于久负盛名的开源Xposed框架实现的一个Android平台上功能强大的无侵入式运行时AOP框架。Dexposed的AOP实现是完全非侵入式的,没有使用任何注解处理器,编织器或者字节码重写器。

Patch原理

首先从GitHub上拉下来代码有几个坑需要注意:

  • 如果想直接拿过来用,你可能会失望而归,因为patchsample这个android程序就没有入口Activity,所以需要自行新建。
  • 注意Lib包如何添加,不能直接添加在libs这个工程自建的文件中,应该放在lib文件中,并手动添加java build依赖
  • 注意使用过程中不是像GitHub上那样介绍的如此精简,需要自行添加sunApkPatch代码并适当添加捕获异常操作,防止Patch包引起Crash。
  • patchsample只是个简单的patch,如果需要增加比较复杂的patch,需要有依赖关系,为了缩小patch体积,这个依赖只能是部分依赖,所以推荐方式是将需要补丁的class文件打jar包引入依赖即可。

接下来我们看看具体的流程:

首先需要我们动态监测AOP环境

runPatchApk();

这里需要注意的是PatchMain.load()这个方法,该方法的主要用途是加载patch APK的所有类,并将实现IPatch的类添加到List中去,然后通过匹配加载的类或者类方法来实现非侵入式AOP。

public void runPatchApk() {    if (android.os.Build.VERSION.SDK_INT == 21) {        return;    }    if (!DexposedBridge.canDexposed(this)) {        Log.d("Hotpatch", "This device doesn't support dexposed!");        return;    }    File cacheDir = getExternalCacheDir();    if (cacheDir != null) {        String fullpath = cacheDir.getAbsolutePath() + File.separator + "PATCH_NAME.apk";        PatchResult result = PatchMain.load(this, fullpath, null);        if (result.isSuccess()) {            Log.e("Hotpatch", "patch success!");        } else {            Log.e("Hotpatch", "patch error is " + result.getErrorInfo());        }    }}

Patch实践

public class Activity extends BaseSherlockSubActivity implements    OnNewIconUIRefreshListener {    private void showDialog() {    final AlertDialog.Builder builder = new AlertDialog.Builder(this);    builder.setTitle("Dexposed sample")            .setMessage("Please clone patchsample project to generate apk, and copy it to \"/Android/data/PACKAGE_NAME/cache/PATCH_NAME.apk\"")            .setPositiveButton("ok", new DialogInterface.OnClickListener() {                public void onClick(DialogInterface dialog, int whichButton) {                }            }).create().show();    }}

假如我们上线的代码中如上所示,在弹层中出现文案bug,那么该如何热更新。
代码修复操作在Patch工程中,添加如下代码:

public class DialogPatch implements IPatch {    @Override    public void handlePatch(final PatchParam arg0) throws Throwable {        Class<?> cls = null;        try {            cls = arg0.context.getClassLoader().loadClass("com.android.activity.Activity");        } catch (ClassNotFoundException e) {            e.printStackTrace();            return;        }        DexposedBridge.findAndHookMethod(cls, "showDialog", new XC_MethodReplacement() {            @Override            protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {                final Activity mainActivity = (Activity) param.thisObject;                AlertDialog.Builder builder = new AlertDialog.Builder(mainActivity);                builder.setTitle("Fanli Dexposed sample").setMessage("The dialog is shown from patch apk!").setPositiveButton("ok", new OnClickListener() {                    public void onClick(DialogInterface dialog, int whichButton) {                        Class<?> clsInner;                        try {                            clsInner = arg0.context.getClassLoader().loadClass("com.android.activity.OutObject");                        } catch (ClassNotFoundException e) {                            e.printStackTrace();                            return;                        }                        try {                            OutObject outObject = (OutObject) clsInner.newInstance();                            if (outObject.callFromOutMethod()) {                                AlertDialog.Builder builder = new AlertDialog.Builder(mainActivity);                                builder.setTitle("Fanli Dexposed sample").setMessage("com.android.activity.OutObject is Worked!")                                        .setPositiveButton("ok", new OnClickListener() {                                            @Override                                            public void onClick(DialogInterface dialog, int which) {                                                dialog.dismiss();                                            }                                        }).create().show();                            }                        } catch (InstantiationException e) {                            e.printStackTrace();                        } catch (IllegalAccessException e) {                            e.printStackTrace();                        }                    }                }).create().show();                return null;            }        });    }}

然后将这个patch APK 传到Server,在主APK中通过下载patch apk到指定目录,然后动态监测AOP环境并loadPatch即可实现热更新。
接下来如果应用到实际项目中需要完善的有以下几点:

  1. 动态监测AOP环境 (android server主动监测 patch包)
  2. 动态加载Patch文件 (更新pathc以后第一时间加载patch)

Patch测试结果:

基于以上实现方案测试的环境包括:
Dalvik 4.0-4.4均已经通过
目前 ART 5.0 以及以上版本 尚未通过。(待更新Native包和Jar包)

版权声明:本文为博主原创文章,未经博主允许不得转载。

  相关解决方案