前言
https://ke.qq.com/webcourse/index.html#course_id=130901&term_id=100146035&taid=1287279008153429&vid=r1417pykrgc
學(xué)習(xí)了下這個視頻的熱修復(fù)方法葵陵。成功實現(xiàn),但是還是遇到了一些問題陕壹。這里記錄下张弛,幫助大家學(xué)習(xí)臣镣。
代碼分析
首先先簡單的代碼分析一下。
//問題代碼
class Calculator {
fun calc(): Int {
return 100 / 0
}
}
//修復(fù)后代碼
class Calculator {
@Replace(clazz = "**.Calculator", method = "calc")
fun calc(): Int {
return 100
}
}
原理:替換Art虛擬機中有問題的類的方法茎毁,為修復(fù)后的方法切蟋。(Android 5.0以上)
視頻中已有詳細(xì)介紹,這里就不多贅述了滚停。
fun loadDex(context: Context, dexName: String) {
val cacheFile: File
if (Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED) {
cacheFile = context.externalCacheDir
} else {
cacheFile = context.cacheDir
}
val file = File(cacheFile, "dxtest")
file.deleteOnExit()
val dexFile = DexFile(File("/storage/emulated/0/", dexName).absolutePath)
val iterator = dexFile.entries()
while (iterator.hasMoreElements()) {
val clazzName = iterator.nextElement()
val clazz = Class.forName(clazzName)
//遍歷類方法根據(jù)注解替換
clazz.declaredMethods.forEach { method ->
val replacObj = method.getAnnotation(Replace::class.java)
val replaceClazz = Class.forName(replacObj.clazz)
val replaceMethod = replaceClazz.getDeclaredMethod(replacObj.method, *method.parameterTypes)
fixMethod(replaceMethod, method)
}
}
}
....
private external fun fixMethod(replaceMethod: Method, method: Method)
先單獨把修復(fù)后的Calculator.class打包沃粗,利用dx命令打包成out.dex,然后放到/storage/emulated/0/
目錄下。
JNIEXPORT void JNICALL
Java_**_fixMethod(JNIEnv *env, jobject thiz,jobject replaceMethod, jobject method) {
art::mirror::ArtMethod *artReplaceMethod = (art::mirror::ArtMethod *) env->FromReflectedMethod(
replaceMethod);
art::mirror::ArtMethod *artMethod = (art::mirror::ArtMethod *) env->FromReflectedMethod(
method);
artReplaceMethod->access_flags_ = artMethod->access_flags_;
artReplaceMethod->declaring_class_ = artMethod->declaring_class_;
artReplaceMethod->dex_code_item_offset_ = artMethod->dex_code_item_offset_;
artReplaceMethod->dex_method_index_ = artMethod->dex_method_index_;
artReplaceMethod->hotness_count_ = artMethod->hotness_count_;
artReplaceMethod->method_index_ = artMethod->method_index_;
artReplaceMethod->ptr_sized_fields_.dex_cache_resolved_methods_ = artMethod->ptr_sized_fields_.dex_cache_resolved_methods_;
artReplaceMethod->ptr_sized_fields_.dex_cache_resolved_types_ = artMethod->ptr_sized_fields_.dex_cache_resolved_types_;
artReplaceMethod->ptr_sized_fields_.entry_point_from_jni_ = artMethod->ptr_sized_fields_.entry_point_from_jni_;
artReplaceMethod->ptr_sized_fields_.entry_point_from_quick_compiled_code_ = artMethod->ptr_sized_fields_.entry_point_from_quick_compiled_code_;
}
按照視頻中所說的键畴,把ArtMehod
中的變量一一替換最盅。
問題記錄
- 困擾我最久的是,訪問sdcard起惕,6.0以上需要手動申請涡贱,這也是自己粗心。視頻中貌似也沒有詳細(xì)提到惹想。而且最讓我頭疼的是
logcat
中的報錯信息问词。
Caused by: java.io.IOException: No original dex files found for dex location /storage/emulated/0/out.dex
...
于是開始百度,google
image.png
沒有講到權(quán)限問題的嘀粱。
這里值得一提的是曾今在使用VirtualApk
插件化技術(shù)的時候激挪,也是遇到這種問題,必須要手到申請下權(quán)限锋叨,不然就會報錯垄分。
后面靈光一閃。添加了權(quán)限申請就好了娃磺。
- 視頻中提到了需要使用到art_method.h薄湿,但是里面的東西又不能全部拿來用,因為會涉及到很多其他的頭文件偷卧,所以只要
ArtMethod
的結(jié)構(gòu)體就好了豺瘤。
android源碼很大,這里我沒有去下載涯冠,直接訪問http://androidxref.com/7.0.0_r1/xref/art/runtime/art_method.h#mirror
查看自己需要的內(nèi)容。
/*
* Copyright (C) 2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_RUNTIME_ART_METHOD_H_A
#define ART_RUNTIME_ART_METHOD_H_A
namespace art {
union JValue;
class OatQuickMethodHeader;
class ProfilingInfo;
class ScopedObjectAccessAlreadyRunnable;
class StringPiece;
class ShadowFrame;
namespace mirror {
class Array;
class Class;
class IfTable;
class PointerArray;
class ImtConflictTable {
enum MethodIndex {
kMethodInterface,
kMethodImplementation,
kMethodCount, // Number of elements in enum.
};
private:
union {
uint32_t data32_[0];
uint64_t data64_[0];
};
};
class ArtMethod {
public:
uint32_t declaring_class_;
uint32_t access_flags_;
uint32_t dex_code_item_offset_;
uint32_t dex_method_index_;
uint16_t method_index_;
uint16_t hotness_count_;
struct PtrSizedFields {
ArtMethod **dex_cache_resolved_methods_;
uint32_t *dex_cache_resolved_types_;
void *entry_point_from_jni_;
void *entry_point_from_quick_compiled_code_;
} ptr_sized_fields_;
};
}
} // namespace art
#endif // ART_RUNTIME_ART_METHOD_H_A
我這里去掉了一些不需要的內(nèi)容逼庞,最重要的是要把ArtMethod結(jié)構(gòu)體中的參數(shù)列出來蛇更。
- 測試,在華為榮耀v9中可以。在內(nèi)置的android虛擬機中派任,debug模式下可以砸逊,正常運行后無法替換,比較奇怪掌逛,后續(xù)有發(fā)現(xiàn)原因师逸,再補充。