有谁使用过阿里巴巴的AndFix框架,android 如何使用框架热修复

在 SegmentFault,解决技术问题
每个月,我们帮助 1000 万的开发者解决各种各样的技术问题。并助力他们在技术能力、职业生涯、影响力上获得提升。
一线的工程师、著名开源项目的作者们,都在这里:
获取验证码
已有账号?
标签:至少1个,最多5个
一直关注App的热修复的技术发展,之前做的应用也没用使用到什么热修复开源框架。在App的热修复框架没有流行之前,做的应用上线后发现一个小小的Bug,就要马上发一个新的版本。我亲身经历过一周发两个版本,真的折腾用户的节奏~~所以,要开始考虑引入热修复。下面记录使用开源框架阿里巴巴的AndFix过程。
实现的原理
这里说的不是热修复怎么实现修bug的原理,这里说的是怎么使用AndFix。如果你想了解更多的andFix实现原理,你可以参考下面的文章:
(AndFix的官网)
(Android大神鸿洋的Bolg文章)
应用启动的时候,在 onCreate() 方法中获取友盟的在线参数来判断当前的应用版本是否有补丁需要下载,有则通过ThinDonloadManager来下载到SD下并且通过使用AndFix来加载到应用中。
使用极光推送消息到该应用的版本需要下载补丁,如果应用收到了消息后,应用判断当前的版本是否需要下载补丁。如果应用没有收到消息的通知,则下次启动App的时候,获取友盟在线参数来判断是否需要下载补丁。
在gradle文件中增加相应的依赖。这里我使用thindownlaodmanager来下载补丁,使用极光推送来推送自定义消息下载补丁通知,使用友盟在线参数来获取补丁包的信息。也许你会问为了修复一个补丁而增加这么多的依赖,值得吗?我认为还可以吧,因为我的项目一般会使用到这些。
compile 'com.alipay.euler:andfix:0.3.1@aar'
导入AndFix的so库文件以及极光推送的so库文件;极光推送集成参考文档:
注意:导入AndFix的so文件时,可以先阅读这下个:
接着,集成友盟在线参数参考官方文档:
配置友盟在线参数的参数以及推光推送自定义的内容
友盟在线参数
极光推送自定义消息(自定义消息有长度限制,所以补丁的下载url写成拼接形式:站点+下载资源名称)
定义相对应的Bean
在启动的自定义Application类进行初始化工作(AndFix、极光的初始化)
在程序的入口类进行友盟补丁的检测:
private void getUmengParamAndFix() {
//获取友盟在线参数对应key的values
String pathInfo = OnlineConfigAgent.getInstance().getConfigParams(this, UMENG_ONLINE_PARAM);
if (!TextUtils.isEmpty(pathInfo)){
PatchBean onLineBean = GsonUtils.getInstance().parseIfNull(PatchBean.class , pathInfo);
//进行判断当前版本是否有补丁需要下载更新
RepairBugUtil.getInstance().comparePath(this, onLineBean);
} catch (Exception e) {
e.printStackTrace();
再加上推送推送的自定义内容的处理:(当推送消息过来的时候应用处于运行状态的时候,程序会处理消息进行下载补丁包)
private WeakHandler mHandler = new WeakHandler(new Handler.Callback() {
public boolean handleMessage(Message msg) {
if (msg.what == MSG_WHAT_DOWNLOAD){
String message = (String) msg.
if (TextUtils.isEmpty(message))
PatchBean bean = GsonUtils.getInstance().parse(PatchBean.class, message);
RepairBugUtil.getInstance().comparePath(MainActivity.this, bean);
} catch (Exception e) {
e.printStackTrace();
//for receive customer msg from jpush server
private MessageReceiver mMessageR
public static final String MESSAGE_RECEIVED_ACTION = "com.zhw.andfix.MESSAGE_RECEIVED_ACTION";
public static final String KEY_MESSAGE = "message";
public void registerMessageReceiver() {
mMessageReceiver = new MessageReceiver();
IntentFilter filter = new IntentFilter();
filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
filter.addAction(MESSAGE_RECEIVED_ACTION);
registerReceiver(mMessageReceiver, filter);
public class MessageReceiver extends BroadcastReceiver {
public void onReceive(Context context, Intent intent) {
if (MESSAGE_RECEIVED_ACTION.equals(intent.getAction())) {
String message = intent.getStringExtra(KEY_MESSAGE);
Message msg = new Message();
msg.what = MSG_WHAT_DOWNLOAD;
mHandler.sendMessage(msg);
补丁包的生成
下载AndFix的补丁生成工具:
生成补丁的文件需要的文件有:原apk文件,修复Bug后生成的新apk,签名文件。
在解压apkpatch工具的目录下,打开命令行输入以下命令生成补丁包。
apkpatch -m &apatch_path...& -o &output& -k &keystore& -p &***& -a &alias& -e &***&
将生成的补丁放到指定服务器上。(这里我放到了七牛上)
自己测试一下成不成啦~
通过ThinDownloadManager下载补丁包,下载成功后使用AndFix加载补丁包的方法:
public void downloadAndLoad(Context context, final PatchBean bean, String downloadUrl) {
if (mLocalPreferencesHelper == null) {
mLocalPreferencesHelper = new LocalPreferencesHelper(context, SPConst.SP_NAME);
Uri downloadUri = Uri.parse(downloadUrl);
Uri destinationUri = Uri.parse(Environment.getExternalStorageDirectory()
.getAbsolutePath() + bean.url);
DownloadRequest downloadRequest = new DownloadRequest(downloadUri)
.setDestinationURI(destinationUri)
.setPriority(DownloadRequest.Priority.HIGH)
.setDownloadListener(new DownloadStatusListener() {
public void onDownloadComplete(int id) {
// add patch at runtime
// .apatch file path
String patchFileString = Environment.getExternalStorageDirectory()
.getAbsolutePath() + bean.
BaseApplication.mPatchManager.addPatch(patchFileString);
Log.d(TAG, "apatch:" + patchFileString + " added.");
//复制且加载补丁成功后,删除下载的补丁
File f = new File(patchFileString);
if (f.exists()) {
boolean result = new File(patchFileString).delete();
if (!result)
Log.e(TAG, patchFileString + " delete fail");
mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, false);
} catch (IOException e) {
Log.e(TAG, "", e);
} catch (Throwable throwable) {
public void onDownloadFailed(int id, int errorCode, String errorMessage) {
//下载失败的时候,标注标记位,等下次重新打开应用的时候重新下载
mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, true);
Log.e(TAG, "onDownloadFailed");
public void onProgress(int id, long totalBytes, int progress) {
Log.e(TAG, "progress:" + progress);
mDownloadManager = new ThinDownloadManager(THREAD_COUNT);
mDownloadManager.add(downloadRequest);
判断是否有补丁包需要下载的方法:
public void comparePath(Context context, PatchBean RemoteBean) throws Exception {
String pathInfo = mLocalPreferencesHelper.getString(SPConst.PATH_INFO);
final PatchBean localBean = GsonUtils.getInstance().parseIfNull(PatchBean.class, pathInfo);
//远程的应用版本跟当前应用的版本比较
if (BaseApplication.VERSION_NAME.equals(RemoteBean.app_v)) {
//远程的应用版本跟本地保存的应用版本一样,但补丁不一样,则需要下载重新
*第一种情况:当本地记录的Bean为空的时候(刚安装的时候可能为空)并且远程的Bean的path_v不为空的时候需要下载补丁。
第二种情况:当本地记录的path_v和远程Bean的path_v不一样的时候需要下载补丁。*/
(localBean == null && !TextUtils.isEmpty(RemoteBean.path_v)
|| localBean.app_v.equals(RemoteBean.app_v) &&
!localBean.path_v.equals(RemoteBean.path_v)) {
downloadAndLoad(context, RemoteBean,
SPConst.URL_PREFIX + RemoteBean.url);
String json = GsonUtils.getInstance().parse(RemoteBean);
mLocalPreferencesHelper.saveOrUpdate(SPConst.PATH_INFO, json);
} /*else {
mLocalPreferencesHelper.saveOrUpdate(SPConst.IsHavePathDownLoad, false);
项目GitHub地址:
0 收藏&&|&&7
你可能感兴趣的文章
1 收藏,105
4 收藏,1k
1 收藏,2.5k
分享到微博?
我要该,理由是:小憩之后,继续为你解读AndFix热修复框架,呵呵。
上一篇已经介绍了AndFix的使用,这篇主要介绍AndFix原理以及源码解析。
AndFix原理
AndFix的原理就是方法的替换,把有bug的方法替换成补丁文件中的方法。
注:在Native层使用指针替换的方式替换bug方法,已达到修复bug的目的。
使用AndFix修复热修复的整体流程:
方法替换过程:
解析源码从使用的方法一一解析。
在自定义Application中初始化PatchManger:
PatchManager mPatchManager = new PatchManager();
直接实例化了一个PatchManger实例对象,接下看PatchManager类源码:
public PatchManager(Context context) {
mContext =
mAndFixManager = new AndFixManager(mContext);
mPatchDir = new File(mContext.getFilesDir(), DIR);
mPatchs = new ConcurrentSkipListSet&Patch&();
mLoaders = new ConcurrentHashMap&String, ClassLoader&();
然后看AndFixManager的初始化:
public AndFixManager(Context context) {
mContext =
mSupport = Compat.isSupport();
if (mSupport) {
mSecurityChecker = new SecurityChecker(mContext);
mOptDir = new File(mContext.getFilesDir(), DIR);
if (!mOptDir.exists() && !mOptDir.mkdirs()) {
mSupport = false;
Log.e(TAG, "opt dir create error.");
} else if (!mOptDir.isDirectory()) {
mOptDir.delete();
mSupport = false;
public static synchronized boolean isSupport() {
if (isChecked)
return isS
isChecked = true;
boolean isYunOs = isYunOS();
boolean setup
=AndFix.setup();
boolean isSupportSDKVersion = isSupportSDKVersion();
if (!isYunOs && setup && isSupportSDKVersion) {
isSupport = true;
if (inBlackList()) {
isSupport = false;
return isS
private static boolean isSupportSDKVersion() {
if (android.os.Build.VERSION.SDK_INT &= 8
&& android.os.Build.VERSION.SDK_INT &= 23) {
return true;
return false;
public static boolean setup() {
final String vmVersion = System.getProperty("java.vm.version");
boolean isArt = vmVersion != null && vmVersion.startsWith("2");
int apilevel = Build.VERSION.SDK_INT;
return setup(isArt, apilevel);
} catch (Exception e) {
Log.e(TAG, "setup", e);
return false;
public SecurityChecker(Context context) {
mContext =
init(mContext);
private void init(Context context) {
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();
PackageInfo packageInfo = pm.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
CertificateFactory certFactory = CertificateFactory
.getInstance("X.509");
ByteArrayInputStream stream = new ByteArrayInputStream(
packageInfo.signatures[0].toByteArray());
X509Certificate cert = (X509Certificate) certFactory
.generateCertificate(stream);
mDebuggable = cert.getSubjectX500Principal().equals(DEBUG_DN);
mPublicKey = cert.getPublicKey();
} catch (NameNotFoundException e) {
Log.e(TAG, "init", e);
} catch (CertificateException e) {
Log.e(TAG, "init", e);
接下,看一下版本的初始化:
mPatchManager.init("version")
init方法源码:
public void init(String appVersion) {
if (!mPatchDir.exists() && !mPatchDir.mkdirs()) {
Log.e(TAG, "patch dir create error.");
} else if (!mPatchDir.isDirectory()) {
mPatchDir.delete();
SharedPreferences sp = mContext.getSharedPreferences(SP_NAME,
Context.MODE_PRIVATE);
String ver = sp.getString(SP_VERSION, null);
if (ver == null || !ver.equalsIgnoreCase(appVersion)) {
cleanPatch();
sp.edit().putString(SP_VERSION, appVersion).commit();
initPatchs();
private void cleanPatch() {
File[] files = mPatchDir.listFiles();
for (File file : files) {
mAndFixManager.removeOptFile(file);
if (!FileUtil.deleteFile(file)) {
Log.e(TAG, file.getName() + " delete error.");
private void initPatchs() {
File[] files = mPatchDir.listFiles();
for (File file : files) {
addPatch(file);
private Patch addPatch(File file) {
Patch patch = null;
if (file.getName().endsWith(SUFFIX)) {
patch = new Patch(file);
mPatchs.add(patch);
} catch (IOException e) {
Log.e(TAG, "addPatch", e);
Patch文件的加载
public Patch(File file) throws IOException {
@SuppressWarnings("deprecation")
private void init() throws IOException {
JarFile jarFile = null;
InputStream inputStream = null;
jarFile = new JarFile(mFile);
JarEntry entry = jarFile.getJarEntry(ENTRY_NAME);
inputStream = jarFile.getInputStream(entry);
Manifest manifest = new Manifest(inputStream);
Attributes main = manifest.getMainAttributes();
mName = main.getValue(PATCH_NAME);
mTime = new Date(main.getValue(CREATED_TIME));
mClassesMap = new HashMap&String, List&String&&();
Attributes.Name attrN
List&String&
for (Iterator&?& it = main.keySet().iterator(); it.hasNext();) {
attrName = (Attributes.Name) it.next();
name = attrName.toString();
if (name.endsWith(CLASSES)) {
strings = Arrays.asList(main.getValue(attrName).split(","));
if (name.equalsIgnoreCase(PATCH_CLASSES)) {
mClassesMap.put(mName, strings);
mClassesMap.put(
name.trim().substring(0, name.length() - 8),
} finally {
if (jarFile != null) {
jarFile.close();
if (inputStream != null) {
inputStream.close();
loadPatch方法源码
mPatchManager.loadPatch()
loadPatch源码:
public void loadPatch() {
mLoaders.put("*", mContext.getClassLoader());
Set&String& patchN
List&String&
for (Patch patch : mPatchs) {
patchNames = patch.getPatchNames();
for (String patchName : patchNames) {
classes = patch.getClasses(patchName);
mAndFixManager.fix(patch.getFile(), mContext.getClassLoader(),
public synchronized void fix(File file, ClassLoader classLoader,
List&String& classes) {
if (!mSupport) {
if (!mSecurityChecker.verifyApk(file)) {
File optfile = new File(mOptDir, file.getName());
boolean saveFingerprint = true;
if (optfile.exists()) {
if (mSecurityChecker.verifyOpt(optfile)) {
saveFingerprint = false;
} else if (!optfile.delete()) {
final DexFile dexFile = DexFile.loadDex(file.getAbsolutePath(),
optfile.getAbsolutePath(), Context.MODE_PRIVATE);
if (saveFingerprint) {
mSecurityChecker.saveOptSig(optfile);
ClassLoader patchClassLoader = new ClassLoader(classLoader) {
protected Class&?& findClass(String className)
throws ClassNotFoundException {
Class&?& clazz = dexFile.loadClass(className, this);
if (clazz == null
&& className.startsWith("com.alipay.euler.andfix")) {
return Class.forName(className);
if (clazz == null) {
throw new ClassNotFoundException(className);
Enumeration&String& entrys = dexFile.entries();
Class&?& clazz = null;
while (entrys.hasMoreElements()) {
String entry = entrys.nextElement();
if (classes != null && !classes.contains(entry)) {
clazz = dexFile.loadClass(entry, patchClassLoader);
if (clazz != null) {
fixClass(clazz, classLoader);
} catch (IOException e) {
Log.e(TAG, "pacth", e);
private void fixClass(Class&?& clazz, ClassLoader classLoader) {
Method[] methods = clazz.getDeclaredMethods();
MethodReplace methodR
for (Method method : methods) {
methodReplace = method.getAnnotation(MethodReplace.class);
if (methodReplace == null)
clz = methodReplace.clazz();
meth = methodReplace.method();
if (!isEmpty(clz) && !isEmpty(meth)) {
replaceMethod(classLoader, clz, meth, method);
private void replaceMethod(ClassLoader classLoader, String clz,
String meth, Method method) {
String key = clz + "@" + classLoader.toString();
Class&?& clazz = mFixedClass.get(key);
if (clazz == null) {
Class&?& clzz = classLoader.loadClass(clz);
clazz = AndFix.initTargetClass(clzz);
if (clazz != null) {
mFixedClass.put(key, clazz);
Method src = clazz.getDeclaredMethod(meth,
method.getParameterTypes());
AndFix.addReplaceMethod(src, method);
} catch (Exception e) {
Log.e(TAG, "replaceMethod", e);
public static void addReplaceMethod(Method src, Method dest) {
replaceMethod(src, dest);
initFields(dest.getDeclaringClass());
} catch (Throwable e) {
Log.e(TAG, "addReplaceMethod", e);
private static native void replaceMethod(Method dest, Method src);
由于Android4.4后才用的Art虚拟机,之前的系统都是Dalvik虚拟机,因此Natice层写了2个方法,对不同的系统做不同的处理方式。
extern void dalvik_replaceMethod(JNIEnv* env, jobject src, jobject dest);
extern void art_replaceMethod(JNIEnv* env, jobject src, jobject dest);
Dalvik replaceMethod的实现:
extern void __attribute__ ((visibility ("hidden"))) dalvik_replaceMethod(
JNIEnv* env, jobject src, jobject dest) {
jobject clazz = env-&CallObjectMethod(dest, jClassMethod);
ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr(
dvmThreadSelf_fnPtr(), clazz);
clz-&status = CLASS_INITIALIZED;
Method* meth = (Method*) env-&FromReflectedMethod(src);
Method* target = (Method*) env-&FromReflectedMethod(dest);
LOGD("dalvikMethod: %s", meth-&name);
meth-&jniArgInfo = 0x;
meth-&accessFlags |= ACC_NATIVE;
int argsSize = dvmComputeMethodArgsSize_fnPtr(meth);
if (!dvmIsStaticMethod(meth))
argsSize++;
meth-&registersSize = meth-&insSize = argsS
meth-&insns = (void*)
meth-&nativeFunc = dalvik_
Art replaceMethod的实现:
extern void __attribute__ ((visibility ("hidden"))) art_replaceMethod(
JNIEnv* env, jobject src, jobject dest) {
if (apilevel & 22) {
replace_6_0(env, src, dest);
} else if (apilevel & 21) {
replace_5_1(env, src, dest);
replace_5_0(env, src, dest);
void replace_5_0(JNIEnv* env, jobject src, jobject dest) {
art* smeth =
(art*) env-&FromReflectedMethod(src);
art* dmeth =
(art*) env-&FromReflectedMethod(dest);
dmeth-&declaring_class_-&class_loader_ =
smeth-&declaring_class_-&class_loader_;
dmeth-&declaring_class_-&clinit_thread_id_ =
smeth-&declaring_class_-&clinit_thread_id_;
dmeth-&declaring_class_-&status_ = (void *)((int)smeth-&declaring_class_-&status_-1);
smeth-&declaring_class_ = dmeth-&declaring_class_;
smeth-&access_flags_ = dmeth-&access_flags_;
smeth-&frame_size_in_bytes_ = dmeth-&frame_size_in_bytes_;
smeth-&dex_cache_initialized_static_storage_ =
dmeth-&dex_cache_initialized_static_storage_;
smeth-&dex_cache_resolved_types_ = dmeth-&dex_cache_resolved_types_;
smeth-&dex_cache_resolved_methods_ = dmeth-&dex_cache_resolved_methods_;
smeth-&vmap_table_ = dmeth-&vmap_table_;
smeth-&core_spill_mask_ = dmeth-&core_spill_mask_;
smeth-&fp_spill_mask_ = dmeth-&fp_spill_mask_;
smeth-&mapping_table_ = dmeth-&mapping_table_;
smeth-&code_item_offset_ = dmeth-&code_item_offset_;
smeth-&entry_point_from_compiled_code_ =
dmeth-&entry_point_from_compiled_code_;
smeth-&entry_point_from_interpreter_ = dmeth-&entry_point_from_interpreter_;
smeth-&native_method_ = dmeth-&native_method_;
smeth-&method_index_ = dmeth-&method_index_;
smeth-&method_dex_index_ = dmeth-&method_dex_index_;
LOGD("replace_5_0: %d , %d", smeth-&entry_point_from_compiled_code_,
dmeth-&entry_point_from_compiled_code_);
mPatchManager.addPatch(path)
public void addPatch(String path) throws IOException {
File src = new File(path);
File dest = new File(mPatchDir, src.getName());
if (!src.exists()) {
throw new FileNotFoundException(path);
if (dest.exists()) {
Log.d(TAG, "patch [" + path + "] has be loaded.");
FileUtil.copyFile(src, dest);
Patch patch = addPatch(dest);
if (patch != null) {
loadPatch(patch);
mPatchManager.removeAllPatch()
public void removeAllPatch() {
cleanPatch();
SharedPreferences sp = mContext.getSharedPreferences(SP_NAME,
Context.MODE_PRIVATE);
sp.edit().clear().commit();
到此源代码就解析结束。
反编译Patch dex文件代码
patch文件中.dex文件反编译后,看到源码效果如下:
红框标注的部分,就是补丁方法。方法的注解部分写了clazz和method的值,对应着apk包中的类名和方法名称。
本文已收录于以下专栏:
相关文章推荐
最近腾讯弄出一个Tinker热修复框架,那么本文先不介绍这个框架,先来介绍一下阿里的一个热修复框架AndFix,这个框架出来已经很长时...
Alibaba的AndFix热修复:
Alibaba-AndFix Bug热修复框架的使用
Alibaba-AndFix Bug热修复框架原理及源码解析上一篇中已经介绍了Alibaba-Dex...
AndFix这篇主要介绍alibaba的AndFix项目的使用,下一篇介绍 AndFix原理及源码解析。Github:/alibaba/AndFixAndFix介绍...
AndFix原理AndFix的原理就是方法的替换,把有bug的方法替换成补丁文件中的方法。 注:在Native层使用指针替换的方式替换bug方法,已达到修复bug的目的。使用AndFix修复热修复的整...
现在很多App里都内置了Web网页(Hyprid App),比如说很多电商平台,淘宝、京东、聚划算等等,如下图 那么这种该如何实现呢?其实这是Android里一个叫WebView的组件实现的。今...
当你的应用发布后第二天却发现一个重要的bug要修复,头疼的同时你可能想着赶紧修复重新打个包发布出去,让用户收到自动更新重新下载。但是万事皆有可能,万一隔一天又发现一个急需修复的bug呢?难道再次发布打...
Binder是Android系统进程间通信(IPC)方式之一
现在很多App里都内置了Web网页(Hyprid App),比如说很多电商平台,淘宝、京东、聚划算等等,如下图上述功能是由Android的WebView实现的,其中涉及到Android客户端与W...
简介AndFix,全称是Android hot-fix。是阿里开源的一个Android热补丁框架,允许APP在不重新发布版本的情况下修复线上的bug。支持Android 2.3 到 6.0。大致原理a...
他的最新文章
讲师:姜飞俊
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)AndFix原理AndFix的原理就是通过c++指针进行方法的替换,把有bug的方法替换成补丁文件中的方法。&注:在Native层使用指针替换的方式替换bug方法,已达到修复bug的目的。使用AndFix修复热修复的整体流程:方法替换过程:源码解析解析源码从使用的方法一一解析。在自定义Application中初始化PatchManger:PatchManager mPatchManager = new PatchManager();11直接实例化了一个PatchManger实例对象,接下看PatchManager类源码:public PatchManager(Context context) {
mContext =
mAndFixManager = new AndFixManager(mContext);
mPatchDir = new File(mContext.getFilesDir(), DIR);
mPatchs = new ConcurrentSkipListSet&Patch&();
mLoaders = new ConcurrentHashMap&String, ClassLoader&();
}12345671234567然后看AndFixManager的初始化:public AndFixManager(Context context) {
mContext =
mSupport = Compat.isSupport();
if (mSupport) {
mSecurityChecker = new SecurityChecker(mContext);
mOptDir = new File(mContext.getFilesDir(), DIR);
if (!mOptDir.exists() && !mOptDir.mkdirs()) {
mSupport = false;
Log.e(TAG, &opt dir create error.&);
} else if (!mOptDir.isDirectory()) {
mOptDir.delete();
mSupport = false;
public static synchronized boolean isSupport() {
if (isChecked)
return isS
isChecked = true;
boolean isYunOs = isYunOS();
boolean setup
=AndFix.setup();
boolean isSupportSDKVersion = isSupportSDKVersion();
if (!isYunOs && setup && isSupportSDKVersion) {
isSupport = true;
if (inBlackList()) {
isSupport = false;
return isS
private static boolean isSupportSDKVersion() {
if (android.os.Build.VERSION.SDK_INT &= 8
&& android.os.Build.VERSION.SDK_INT &= 23) {
return true;
return false;
public static boolean setup() {
final String vmVersion = System.getProperty(&java.vm.version&);
boolean isArt = vmVersion != null && vmVersion.startsWith(&2&);
int apilevel = Build.VERSION.SDK_INT;
return setup(isArt, apilevel);
} catch (Exception e) {
Log.e(TAG, &setup&, e);
return false;
public SecurityChecker(Context context) {
mContext =
init(mContext);
private void init(Context context) {
PackageManager pm = context.getPackageManager();
String packageName = context.getPackageName();
PackageInfo packageInfo = pm.getPackageInfo(packageName,
PackageManager.GET_SIGNATURES);
CertificateFactory certFactory = CertificateFactory
.getInstance(&X.509&);
ByteArrayInputStream stream = new ByteArrayInputStream(
packageInfo.signatures[0].toByteArray());
X509Certificate cert = (X509Certificate) certFactory
.generateCertificate(stream);
mDebuggable = cert.getSubjectX500Principal().equals(DEBUG_DN);
mPublicKey = cert.getPublicKey();
} catch (NameNotFoundException e) {
Log.e(TAG, &init&, e);
} catch (CertificateException e) {
Log.e(TAG, &init&, e);
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778接下,看一下版本的初始化:mPatchManager.init(&version&)11init方法源码:public void init(String appVersion) {
if (!mPatchDir.exists() && !mPatchDir.mkdirs()) {
Log.e(TAG, &patch dir create error.&);
} else if (!mPatchDir.isDirectory()) {
mPatchDir.delete();
SharedPreferences sp = mContext.getSharedPreferences(SP_NAME,
Context.MODE_PRIVATE);
String ver = sp.getString(SP_VERSION, null);
if (ver == null || !ver.equalsIgnoreCase(appVersion)) {
cleanPatch();
sp.edit().putString(SP_VERSION, appVersion).commit();
initPatchs();
private void cleanPatch() {
File[] files = mPatchDir.listFiles();
for (File file : files) {
mAndFixManager.removeOptFile(file);
if (!FileUtil.deleteFile(file)) {
Log.e(TAG, file.getName() + & delete error.&);
private void initPatchs() {
File[] files = mPatchDir.listFiles();
for (File file : files) {
addPatch(file);
private Patch addPatch(File file) {
Patch patch = null;
if (file.getName().endsWith(SUFFIX)) {
patch = new Patch(file);
mPatchs.add(patch);
} catch (IOException e) {
Log.e(TAG, &addPatch&, e);
}12345678910111213141516171819202122232425262728293031323334353637383940414243444546471234567891011121314151617181920212223242526272829303132333435363738394041424344454647Patch文件的加载public Patch(File file) throws IOException {
@SuppressWarnings(&deprecation&)
private void init() throws IOException {
JarFile jarFile = null;
InputStream inputStream = null;
jarFile = new JarFile(mFile);
JarEntry entry = jarFile.getJarEntry(ENTRY_NAME);
inputStream = jarFile.getInputStream(entry);
Manifest manifest = new Manifest(inputStream);
Attributes main = manifest.getMainAttributes();
mName = main.getValue(PATCH_NAME);
mTime = new Date(main.getValue(CREATED_TIME));
mClassesMap = new HashMap&String, List&String&&();
Attributes.Name attrN
List&String&
for (Iterator&?& it = main.keySet().iterator(); it.hasNext();) {
attrName = (Attributes.Name) it.next();
name = attrName.toString();
if (name.endsWith(CLASSES)) {
strings = Arrays.asList(main.getValue(attrName).split(&,&));
if (name.equalsIgnoreCase(PATCH_CLASSES)) {
mClassesMap.put(mName, strings);
mClassesMap.put(
name.trim().substring(0, name.length() - 8),
} finally {
if (jarFile != null) {
jarFile.close();
if (inputStream != null) {
inputStream.close();
}123456789101112131415161718192021222324252627282930313233343536373839404142434445464748123456789101112131415161718192021222324252627282930313233343536373839404142434445464748loadPatch方法源码mPatchManager.loadPatch()11loadPatch源码:public void loadPatch() {
mLoaders.put(&*&, mContext.getClassLoader());
Set&String& patchN
List&String&
for (Patch patch : mPatchs) {
patchNames = patch.getPatchNames();
for (String patchName : patchNames) {
classes = patch.getClasses(patchName);
mAndFixManager.fix(patch.getFile(), mContext.getClassLoader(),
}1234567891011121312345678910111213fix bugpublic synchronized void fix(File file, ClassLoader classLoader,
List&String& classes) {
if (!mSupport) {
if (!mSecurityChecker.verifyApk(file)) {
File optfile = new File(mOptDir, file.getName());
boolean saveFingerprint = true;
if (optfile.exists()) {
if (mSecurityChecker.verifyOpt(optfile)) {
saveFingerprint = false;
} else if (!optfile.delete()) {
final DexFile dexFile = DexFile.loadDex(file.getAbsolutePath(),
optfile.getAbsolutePath(), Context.MODE_PRIVATE);
if (saveFingerprint) {
mSecurityChecker.saveOptSig(optfile);
ClassLoader patchClassLoader = new ClassLoader(classLoader) {
protected Class&?& findClass(String className)
throws ClassNotFoundException {
Class&?& clazz = dexFile.loadClass(className, this);
if (clazz == null
&& className.startsWith(&com.alipay.euler.andfix&)) {
return Class.forName(className);
if (clazz == null) {
throw new ClassNotFoundException(className);
Enumeration&String& entrys = dexFile.entries();
Class&?& clazz = null;
while (entrys.hasMoreElements()) {
String entry = entrys.nextElement();
if (classes != null && !classes.contains(entry)) {
clazz = dexFile.loadClass(entry, patchClassLoader);
if (clazz != null) {
fixClass(clazz, classLoader);
} catch (IOException e) {
Log.e(TAG, &pacth&, e);
private void fixClass(Class&?& clazz, ClassLoader classLoader) {
Method[] methods = clazz.getDeclaredMethods();
MethodReplace methodR
for (Method method : methods) {
methodReplace = method.getAnnotation(MethodReplace.class);
if (methodReplace == null)
clz = methodReplace.clazz();
meth = methodReplace.method();
if (!isEmpty(clz) && !isEmpty(meth)) {
replaceMethod(classLoader, clz, meth, method);
private void replaceMethod(ClassLoader classLoader, String clz,
String meth, Method method) {
String key = clz + &@& + classLoader.toString();
Class&?& clazz = mFixedClass.get(key);
if (clazz == null) {
Class&?& clzz = classLoader.loadClass(clz);
clazz = AndFix.initTargetClass(clzz);
if (clazz != null) {
mFixedClass.put(key, clazz);
Method src = clazz.getDeclaredMethod(meth,
method.getParameterTypes());
AndFix.addReplaceMethod(src, method);
} catch (Exception e) {
Log.e(TAG, &replaceMethod&, e);
public static void addReplaceMethod(Method src, Method dest) {
replaceMethod(src, dest);
initFields(dest.getDeclaringClass());
} catch (Throwable e) {
Log.e(TAG, &addReplaceMethod&, e);
private static native void replaceMethod(Method dest, Method src);123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111由于Android4.4后才用的Art虚拟机,之前的系统都是Dalvik虚拟机,因此Natice层写了2个方法,对不同的系统做不同的处理方式。extern void dalvik_replaceMethod(JNIEnv* env, jobject src, jobject dest);
extern void art_replaceMethod(JNIEnv* env, jobject src, jobject dest);1212Dalvik replaceMethod的实现:extern void __attribute__ ((visibility (&hidden&))) dalvik_replaceMethod(
JNIEnv* env, jobject src, jobject dest) {
jobject clazz = env-&CallObjectMethod(dest, jClassMethod);
ClassObject* clz = (ClassObject*) dvmDecodeIndirectRef_fnPtr(
dvmThreadSelf_fnPtr(), clazz);
clz-&status = CLASS_INITIALIZED;
Method* meth = (Method*) env-&FromReflectedMethod(src);
Method* target = (Method*) env-&FromReflectedMethod(dest);
LOGD(&dalvikMethod: %s&, meth-&name);
meth-&jniArgInfo = 0x;
meth-&accessFlags |= ACC_NATIVE;
int argsSize = dvmComputeMethodArgsSize_fnPtr(meth);
if (!dvmIsStaticMethod(meth))
argsSize++;
meth-&registersSize = meth-&insSize = argsS
meth-&insns = (void*)
meth-&nativeFunc = dalvik_
}1234567891011121314151617181920212212345678910111213141516171819202122Art replaceMethod的实现:
extern void __attribute__ ((visibility (&hidden&))) art_replaceMethod(
JNIEnv* env, jobject src, jobject dest) {
if (apilevel & 22) {
replace_6_0(env, src, dest);
} else if (apilevel & 21) {
replace_5_1(env, src, dest);
replace_5_0(env, src, dest);
void replace_5_0(JNIEnv* env, jobject src, jobject dest) {
art* smeth =
(art*) env-&FromReflectedMethod(src);
art* dmeth =
(art*) env-&FromReflectedMethod(dest);
dmeth-&declaring_class_-&class_loader_ =
smeth-&declaring_class_-&class_loader_;
dmeth-&declaring_class_-&clinit_thread_id_ =
smeth-&declaring_class_-&clinit_thread_id_;
dmeth-&declaring_class_-&status_ = (void *)((int)smeth-&declaring_class_-&status_-1);
smeth-&declaring_class_ = dmeth-&declaring_class_;
smeth-&access_flags_ = dmeth-&access_flags_;
smeth-&frame_size_in_bytes_ = dmeth-&frame_size_in_bytes_;
smeth-&dex_cache_initialized_static_storage_ =
dmeth-&dex_cache_initialized_static_storage_;
smeth-&dex_cache_resolved_types_ = dmeth-&dex_cache_resolved_types_;
smeth-&dex_cache_resolved_methods_ = dmeth-&dex_cache_resolved_methods_;
smeth-&vmap_table_ = dmeth-&vmap_table_;
smeth-&core_spill_mask_ = dmeth-&core_spill_mask_;
smeth-&fp_spill_mask_ = dmeth-&fp_spill_mask_;
smeth-&mapping_table_ = dmeth-&mapping_table_;
smeth-&code_item_offset_ = dmeth-&code_item_offset_;
smeth-&entry_point_from_compiled_code_ =
dmeth-&entry_point_from_compiled_code_;
smeth-&entry_point_from_interpreter_ = dmeth-&entry_point_from_interpreter_;
smeth-&native_method_ = dmeth-&native_method_;
smeth-&method_index_ = dmeth-&method_index_;
smeth-&method_dex_index_ = dmeth-&method_dex_index_;
LOGD(&replace_5_0: %d , %d&, smeth-&entry_point_from_compiled_code_,
dmeth-&entry_point_from_compiled_code_);
}1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484912345678910111213141516171819202122232425262728293031323334353637383940414243444546474849添加PatchmPatchManager.addPatch(path)11源码:public void addPatch(String path) throws IOException {
File src = new File(path);
File dest = new File(mPatchDir, src.getName());
if (!src.exists()) {
throw new FileNotFoundException(path);
if (dest.exists()) {
Log.d(TAG, &patch [& + path + &] has be loaded.&);
FileUtil.copyFile(src, dest);
Patch patch = addPatch(dest);
if (patch != null) {
loadPatch(patch);
}1234567891011121314151612345678910111213141516移除PatchmPatchManager.removeAllPatch()11源码:public void removeAllPatch() {
cleanPatch();
SharedPreferences sp = mContext.getSharedPreferences(SP_NAME,
Context.MODE_PRIVATE);
sp.edit().clear().commit();
}123456123456到此源代码就解析结束。反编译Patch dex文件代码patch文件中.dex文件反编译后,看到源码效果如下:&红框标注的部分,就是补丁方法。方法的注解部分写了clazz和method的值,对应着apk包中的类名和方法名称。
本文已收录于以下专栏:
相关文章推荐
apk这个字眼大家肯定在清楚不过了,可是好多的程序员确仅仅知道使用或者说知道这个的表面含义,但是我们不能只会吃面包,确不去了解面包是如何生产出来的,我还是建议大家还是多多了解下apk的生成过程以及ap...
现在很多App里都内置了Web网页(Hyprid App),比如说很多电商平台,淘宝、京东、聚划算等等,如下图 那么这种该如何实现呢?其实这是Android里一个叫WebView的组件实现的。今...
当你的应用发布后第二天却发现一个重要的bug要修复,头疼的同时你可能想着赶紧修复重新打个包发布出去,让用户收到自动更新重新下载。但是万事皆有可能,万一隔一天又发现一个急需修复的bug呢?难道再次发布打...
小憩之后,继续为你解读AndFix热修复框架,呵呵。上一篇Alibaba-AndFix Bug热修复框架的使用已经介绍了AndFix的使用,这篇主要介绍AndFix原理以及源码解析。AndFix原理A...
简介AndFix,全称是Android hot-fix。是阿里开源的一个Android热补丁框架,允许APP在不重新发布版本的情况下修复线上的bug。支持Android 2.3 到 6.0。大致原理a...
现在很多App里都内置了Web网页(Hyprid App),比如说很多电商平台,淘宝、京东、聚划算等等,如下图上述功能是由Android的WebView实现的,其中涉及到Android客户端与W...
Binder是Android系统进程间通信(IPC)方式之一
如果没有看过上篇的建议从上篇看起。先大概回忆下,上一篇分析了mPatchManager.init(&1.0&),addPatch()方法。还有通过分析打补丁工具,了解补丁文件是怎么生成的。下面就来讲讲...
小憩之后,继续为你解读AndFix热修复框架,呵呵。
上一篇Alibaba-AndFix Bug热修复框架的使用已经介绍了AndFix的使用,这篇主要介绍AndFix原理以及源码解析。
1.不知道如何使用的同学,建议看看我上一篇写的介绍热补丁和Andfix的使用,这样你才有一个大概的框架。通过使用Andfix,其实我们心中会有一个大概的轮廓,它的工作原理,大概就是,所谓的补丁文件,就...
他的最新文章
讲师:姜飞俊
您举报文章:
举报原因:
原文地址:
原因补充:
(最多只允许输入30个字)

我要回帖

更多关于 android 图片加载框架 的文章

 

随机推荐