移动客户端应用相对于Webapp的最大一个问题每次出现bug,不能像web一样在服务器就完成修复,不需要发版本。紧急或者有安全漏洞的问题,

如果是Webapp你可能最多花个1,2个小时紧急发布上线,但是app呢,打包,跪求市场发布几百个渠道,周末还发不了,app配置升级,你还不能配置

强制升级, 就算配置提示升级,用户心里肯定想前两天刚升级最新版,怎么又要升,而且升级要流量,这时候会很反感甚至卸载应用。所以安卓是否

有能力做到在线打补丁?dexposed给我们解决了这个问题。

1、淘宝Dexposed框架

喜欢刷机的人应该对xposed不陌生,Xposed框架是一款可以在不修改APK的情况下影响程序运行(修改系统)的框架服务,基于它 可以制作出许多功

能强大的模块。其实Xposed安卓的一个开源框架,在github上的下载地址xposed,有兴趣的可以去研究下,dexPosed也是基于Xposed的。

他有几个典型的使用场景:

a. Classic AOP programming (aop编程)

b. Instrumentation (for testing, performance monitoring and etc.)  测试,性能监控

c. Online hot patch to fix critical, emergent or security bugs 线上打补丁,解决一些严重的,紧急的或者安全漏洞的bug。

d. SDK hooking for a better development experience

对于aop编程我这里就不多说了,deXposed提供的 是无侵入性的,而且AOP就是用的java代码,相比国外比较流行的Aspectj有几点优势:

a、无侵入性,

b、使用Java代码编写,Aspectj使用脚本需要一定的学习成本

c、Aspectj有自己的编译器,需要编译下Aspectj代码,会注入他自己的代码

下面来看看如果写一些AOP的代码:

Attach a piece of code before and after all occurrences of Activity.onCreate(Bundle).


beforeHookedMethod和afterHookedMethod方法做一些具体的操作。我们可以看到,用dexposed可以实现不改变原函
数的执行,但是在原函数执行前后去做一些其他的额外处理,例如改变入参和返回值等等的一些事情。

  Replace the original body of the target method.DexposedBridge.findAndHookMethod(Activity.class, "onCreate", Bundle.class, new XC_MethodReplacement() {@Override protected Object replaceHookedMethod(MethodHookParam param) throws Throwable {// Re-writing the method logic outside the original method context is a bit tricky but still viable....}});

则是可以将原有要执行的函数替换成一个我们需要的新的执行函数。

这个框架目前对android 5.0系统支持度不高,不过框架更新的时间可以看出,持续维护中,不久将对art完全支持。他提供一个函数来检测你的android

系统是否支持 Dexposed,DexposedBridge.canDexposed(context)。你需要把补丁包打包成一个apk, 然后通过下面的代码来加载补丁包:

// Run taobao.patch apkpublic void runPatchApk() {if (android.os.Build.VERSION.SDK_INT == 21) {return;}if (!isSupport) {Log.d("dexposed", "This device doesn't support dexposed!");return;}File cacheDir = getExternalCacheDir();if (cacheDir != null) {String fullpath = cacheDir.getAbsolutePath() + File.separator + "patch.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());}}}

2.Dexposed原理

重点是搞清楚是怎样hook的。

1)首先通过ClassLoader把插件apk加载进入主工程

2)通过反射拿到具体的class类

3)DexposedBridge.findAndHookMethod定位到具体的方法中,对代码进行覆盖或者加入自定义功能

前面两点都是Java方法没什么可以说的,我这里重点分析下第三点:

a、DexposedBridge.findAndHookMethod()中调用了Xc_methdhook.hookMethos(),这个方法是通过传入的class,方法及签名去查找返回一个对应的Method。

然后把Method作为参数传入hookMethodNative(Method  ...);

b、hookMethodNative是进行hook的主要方法,分析源码发现,它里面做了这些事情

1、把Java层的变量,类型转换成c能识别的指针类型;

    // Save a copy of the original method and other hook infoDexposedHookInfo* hookInfo = (DexposedHookInfo*) calloc(1, sizeof(DexposedHookInfo));memcpy(hookInfo, method, sizeof(hookInfo->originalMethodStruct));hookInfo->reflectedMethod = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(reflectedMethodIndirect));hookInfo->additionalInfo = dvmDecodeIndirectRef(dvmThreadSelf(), env->NewGlobalRef(additionalInfoIndirect));

2、用我们自己的code代替,看到下面的nativeFun了吗,这个方法被dexposedCallHandler代替,也就是说被我们自己的回调方法代替了,这样我们就可以

随意的操作了。然后把要hook的对象参数保存到insns

   // Replace method with our own codeSET_METHOD_FLAG(method, ACC_NATIVE);method->nativeFunc = &dexposedCallHandler;method->insns = (const u2*) hookInfo;method->registersSize = method->insSize;method->outsSize = 0;

3、接下来我们去看看dexposedCallHandler做了哪些事情;

 //赋值
DexposedHookInfo* hookInfo = (DexposedHookInfo*) method->insns;Method* original = (Method*) hookInfo;Object* originalReflected = hookInfo->reflectedMethod;Object* additionalInfo = hookInfo->additionalInfo;
 // call the Java handler function
//对...这才是重点,dvmCallMethod方法回调了dexposedHandleHookedMethod方法,这个方法是在Java层实现的JValue result;dvmCallMethod(self, dexposedHandleHookedMethod, NULL, &result,originalReflected, (int) original, additionalInfo, thisObject, argsArray);dvmReleaseTrackedAlloc((Object *)argsArray, self);

4、我们在去看看handleHookMethod做了什么,一看你就明朗了;

private static Object handleHookedMethod(Member method, int originalMethodId, Object additionalInfoObj,Object thisObject, Object[] args) throws Throwable {AdditionalHookInfo additionalInfo = (AdditionalHookInfo) additionalInfoObj;if (disableHooks) {try {return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,additionalInfo.returnType, thisObject, args);} catch (InvocationTargetException e) {throw e.getCause();}}Object[] callbacksSnapshot = additionalInfo.callbacks.getSnapshot();final int callbacksLength = callbacksSnapshot.length;if (callbacksLength == 0) {try {return invokeOriginalMethodNative(method, originalMethodId, additionalInfo.parameterTypes,additionalInfo.returnType, thisObject, args);} catch (InvocationTargetException e) {throw e.getCause();}}MethodHookParam param = new MethodHookParam();param.method  = method;param.thisObject = thisObject;param.args = args;// call "before method" callbacksint beforeIdx = 0;do {try {((XC_MethodHook) callbacksSnapshot[beforeIdx]).beforeHookedMethod(param);} catch (Throwable t) {DexposedBridge.log(t);// reset result (ignoring what the unexpectedly exiting callback did)param.setResult(null);param.returnEarly = false;continue;}if (param.returnEarly) {// skip remaining "before" callbacks and corresponding "after" callbacksbeforeIdx++;break;}} while (++beforeIdx < callbacksLength);// call original method if not requested otherwiseif (!param.returnEarly) {try {param.setResult(invokeOriginalMethodNative(method, originalMethodId,additionalInfo.parameterTypes, additionalInfo.returnType, param.thisObject, param.args));} catch (InvocationTargetException e) {param.setThrowable(e.getCause());}}// call "after method" callbacksint afterIdx = beforeIdx - 1;do {Object lastResult =  param.getResult();Throwable lastThrowable = param.getThrowable();try {((XC_MethodHook) callbacksSnapshot[afterIdx]).afterHookedMethod(param);} catch (Throwable t) {DexposedBridge.log(t);// reset to last result (ignoring what the unexpectedly exiting callback did)if (lastThrowable == null)param.setResult(lastResult);elseparam.setThrowable(lastThrowable);}} while (--afterIdx >= 0);// returnif (param.hasThrowable())throw param.getThrowable();elsereturn param.getResult();}

判断是否hook成功,如果不成功执行invokeOriginalMethodNative,也就是执行原始函数;

成功则,这个函数查找被挂钩函数的挂钩 XC_MethodHook结构体,然后执行里边的 beforeHookedMethod函数,再通过 invokeOriginalMethodNative

执行挂钩前的原始函数,最后再执行 afterHookedMethod 函数。

findAndHookMethod 的实现就分析完了,

本质上仍然是寻找被挂钩函数的 Method 结构体,将Method属性改为native ,然后对其成员 nativeFunc,

registersize 等进行赋值,其中 insns 成员保存了挂钩的详细信息。所有被挂钩的函数,其nativeFunc都赋值为 dexposedCallHandler 函数,该函数最终执行 XposedBridge.class 里的 handleHookedMethod 。 handleHookedMethod 寻找dexposed模块及dexposed框架调用 findAndHookMethod 注册的 before,after

函数,如果有,就执行,再通过invokeOriginalMethodNative 执行挂钩前函数。

3、怎么使用dexposed

a、下载dexposed源码 dexposed

b、把下载的源码导入,只需要sample包,其中dexposedsample是有bug的主工程,patchsample为插件工程,(其他的两个为工具类源码,方便我们理解源码)

c、主工程配置gradle文件

apply plugin: 'com.android.application'
android {compileSdkVersion 21buildToolsVersion "21.1.2"defaultConfig {applicationId "com.taobao.dexposed"minSdkVersion 9targetSdkVersion 21versionName "1.0"}buildTypes {release {minifyEnabled falseproguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'}}android {packagingOptions {exclude 'AndroidManifest.xml'}}buildTypes {//Debug打开,使用测试环境接口debug {}}lintOptions {abortOnError false}sourceSets {main {manifest.srcFile 'src/main/AndroidManifest.xml'java.srcDirs = ['src/main/java']res.srcDirs = ['src/main/res']   //路径根据自己的对应修改}}
}//我使用的是本地导入so包,
task copyNativeLibs(type: Copy) {from fileTree(dir: 'libs', include: '*/*.so' )  into  'build/native-libs'
}
tasks.withType(JavaCompile) { compileTask -> compileTask.dependsOn copyNativeLibs }clean.dependsOn 'cleanCopyNativeLibs'tasks.withType(com.android.build.gradle.tasks.PackageApplication) {pkgTask ->pkgTask.jniFolders = new HashSet<File>()pkgTask.jniFolders.add(new File(projectDir, 'libs'))
}
//---------------------------------
//这段配置是把主工程打成jar包,这个jar包需要导入到patch工程中task clearJar(type: Delete) {delete 'libs/sdk.jar'
}task makeJar(type:org.gradle.api.tasks.bundling.Jar) {//指定生成的jar名baseName 'sdk'//从哪里打包class文件from('build/intermediates/classes/sample/debug/com')//打包到jar后的目录结构,根据自己工程需要写路径into('com')//去掉不需要打包的目录和文件exclude('test/', 'BuildConfig.class', 'R.class')//去掉R$开头的文件exclude{ it.name.startsWith('R$');}
}makeJar.dependsOn(clearJar, build)
//---------------------------------------------dependencies {compile fileTree(dir: 'libs', include: ['*.jar'])compile files('libs/dexposedbridge.jar')
}

patch工程gradle配置:

dependencies {
//        compile fileTree(dir: 'libs', include: ['*.jar'])provided files('libs/sdk.jar')provided files('libs/dexposedbridge.jar')provided files('libs/patchloader.jar')}

使用provided方式导入jar包,这样只会在编译时引用jar,不会把jar打包进patch.apk,防止与主工程引入包冲突;

用例代码就不贴了,自己去down一份


4、关于patch包的安全问题可以看Android Hotpatch

dexposed框架Android在线热修复相关推荐

  1. JAndFix: 基于Java实现的Android实时热修复方案

    简述 JAndFix是一种基于Java实现的Android实时热修复方案,它并不需要重新启动就能生效.JAndFix是在AndFix的基础上改进实现,AndFix主要是通过jni实现对method(A ...

  2. android Tinker 热修复 乐固加固后友盟打多渠道包之后的补丁失效

    继上一篇 android tinker 热修复使用及注意事项  生成了热修复的补丁; 现在的需求是这样的,我想把这个包用腾讯乐固加固,然后生成多渠道包,希望这个补丁能修复所有这些渠道的包,经过测试,直 ...

  3. Android—常用热修复框架

    前言 热修复即<打补丁>,当一个app上线后,如果发现重大的bug,需要紧急修复.常规的做法是修复bug,然后重新打包,再上线到各个渠道.这种方式的成本高,效率低. 于是热修复技术应运而生 ...

  4. Android之热修复框架Nuwa

    转载请标明出处: http://blog.csdn.net/hai_qing_xu_kong/article/details/70284239 本文出自:[顾林海的博客] ##前言 当热修复框架还没出 ...

  5. android的热修复,Android热修复原理

    热修复框架技术主要有三类,代码修复,资源修复,动态链接库修复. 资源修复 很多资源修复的框架参考了Instant Run资源修复的原理,所以先了解一下Instant Run Instant Run I ...

  6. 基于cydia Hook在线热修复补丁方案

    最近的在线热补丁修复的讨论相当激烈,从Xopsed到Dexposed,再到AndFix,再到QQ空间团队的Class补丁.可谓是各有特色.本文讨论的是基于Cydia Hook实现的在线Class热补丁 ...

  7. Android 微信热修复 Tinker 接入过程及使用方法

    一.前言 学习热修复 Tinker 的使用,主要有两个原因: 业务需要:项目会出现一些细小的bug,需要等到发布新版才能修复,因此一直有关注热修复这块的技术. 技术驱动:这是一件需要一直保持的事情,不 ...

  8. Android Andfix热修复原理

    正常开发流程 热修复开发流程 当线上的项目出现问题了,需要重新发布版本解决bug,重新发新版本apk; 但是随着技术不断的更新,线上项目出现bug,可以通过热修复,在不需要发布新版本的情况下进行bug ...

  9. Android Xposed热修复原理简析

    简单介绍下热修复,基于Xposed中的思想,通过修改c层的Method实例描述,来实现更改与之对应的java方法的行为,从而达到修复的目的. Xposed: 诞生于XDA论坛,类似一个应用平台,不同的 ...

  10. android的热修复,Android热修复之

    这里就有一个概念那就AndFix.apatch补丁用来修复方法,接下来我们看看到底是怎么实现的. 1.2 生成apatch包 假如我们收到了用户上传的崩溃信息,我们改完需要修复的Bug,这个时候就会有 ...

最新文章

  1. 【深度学习】单位高斯化
  2. [life]见证本届世界杯意大利的出局
  3. 字符函数和内存函数模拟实现
  4. File类的用法总结,及文件过滤器的介绍。
  5. 几个IE与Firefox的兼容性问题 (一、网络转载)
  6. android adb.exe端口占用
  7. 苹果悬浮球_手机轻松实现多个系统!安卓手机运行苹果iOS系统?期待!
  8. iOS自带的GPS 定位
  9. Mybatis学习笔记(二)
  10. android中文字体加粗,android TextView设置中文字体加粗实现方法
  11. 5个免费GitHub最强前端学习资源 程序员不花一分钱也能变很强
  12. pcode.linq
  13. 异地组网——ZeroTier
  14. 计算机视觉最新进展概览2021年8月15日到2021年8月21日
  15. 按回车键没有反应,不进行下一步
  16. JavaScript--【JS】排他思想
  17. 毕业设计英文文献java_计算机专业毕业设计论文外文文献中英文翻译——java对象...
  18. adapt和adopt的区别_相似词辨析:adapt, adopt 与 adept
  19. c语言做飞翔的小鸟简笔画图片大全,各种飞翔的小鸟简笔画图片
  20. 计算机毕业设计Node.js+Express亚健康人群健康管理系统(源码+程序+lw+远程调试)

热门文章

  1. eja智能压力变送器工作原理_EJA智能压力变送器
  2. Spring xml文件配置——创建bean的三种方式及作用范围、生命周期
  3. java的package需要大写吗,java 数字转大写的小程序
  4. firewalls 查看防火墙状态_5条命令玩转Linux系统防火墙
  5. ThinkPHP胜出Laravel 近4倍,主流框架性能测试
  6. Mysql事务,并发问题,锁机制
  7. 利用Eclipse中的Maven构建Web项目(一)
  8. iOS学习之单例模式
  9. 矩阵快速乘法---代码
  10. HP MSA2312 ERROR