本章主要講解1. CrashHandler來監(jiān)視App的crash信息悍抑;2. Android65536問題鳄炉;3. Android動(dòng)態(tài)加載;4. 反編譯搜骡。
13.1 使用CrashHandler來獲取應(yīng)用的crash信息
如何檢測(cè)崩潰并了解詳細(xì)的crash信息拂盯? 首先需實(shí)現(xiàn)一個(gè)uncaughtExceptionHandler對(duì)象,在它的uncaughtException方法中獲取異常信息并將其存儲(chǔ)到SD卡或者上傳到服務(wù)器中记靡,然后調(diào)用Thread的setDefaultUncaughtExceptionHandler為當(dāng)前進(jìn)程的所有線程設(shè)置異常處理器谈竿。
public class CrashHandler implements Thread.UncaughtExceptionHandler {
private static final String TAG = "CrashHandler";
private static final boolean DEBUG = true;
private static final String PATH = Environment.getExternalStorageDirectory().getPath() + "/CrashTest/log/";
private static final String FILE_NAME = "crash";
private static final String FILE_NAME_SUFFIX = ".trace";
private static CrashHandler sInstance = new CrashHandler();
private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
private Context mContext;
private CrashHandler() {
}
public static CrashHandler getInstance() {
return sInstance;
}
public void init(Context context) {
mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
mContext = context.getApplicationContext();
}
/**
* 這個(gè)是最關(guān)鍵的函數(shù),當(dāng)程序中有未被捕獲的異常摸吠,系統(tǒng)將會(huì)自動(dòng)調(diào)用#uncaughtException方法
* thread為出現(xiàn)未捕獲異常的線程空凸,ex為未捕獲的異常,有了這個(gè)ex寸痢,我們就可以得到異常信息呀洲。
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
try {
//導(dǎo)出異常信息到SD卡中
dumpExceptionToSDCard(ex);
uploadExceptionToServer();
//這里可以通過網(wǎng)絡(luò)上傳異常信息到服務(wù)器,便于開發(fā)人員分析日志從而解決bug
} catch (IOException e) {
e.printStackTrace();
}
ex.printStackTrace();
//如果系統(tǒng)提供了默認(rèn)的異常處理器啼止,則交給系統(tǒng)去結(jié)束我們的程序道逗,否則就由我們自己結(jié)束自己
if (mDefaultCrashHandler != null) {
mDefaultCrashHandler.uncaughtException(thread, ex);
} else {
Process.killProcess(Process.myPid());
}
}
private void dumpExceptionToSDCard(Throwable ex) throws IOException {
//偽代碼 本方法用于實(shí)現(xiàn)將錯(cuò)誤信息存儲(chǔ)到SD卡中
}
private void uploadExceptionToServer() {
//偽代碼 本方法用于將錯(cuò)誤信息上傳至服務(wù)器
}
}
然后在Application初始化的時(shí)候?yàn)榫€程設(shè)置CrashHandler,這樣之后族壳,Crash就會(huì)通過我們自己的異常處理器來處理異常了憔辫。
public class BaseApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
CrashHandler crashHandler = CrashHandler.getInstance();
crashHandler.init(this);
}
}
13.2 使用multidex來解決方法數(shù)越界
- 在Android中單個(gè)dex文件所能包含的最大方法數(shù)為65536,這個(gè)是包含Android FrameWork仿荆、依賴jar包以及應(yīng)用本身代碼中的所有方法贰您。達(dá)到這個(gè)65536后,編譯器編譯時(shí)會(huì)拋出DexIndexOverflowException異常拢操。
- 如何解決锦亦?Google提供了multidex解決方案。在Android5.0之前需要引入Google提供的android-support-multidex.jar令境;從5.0開始系統(tǒng)默認(rèn)支持了multidex杠园,它可以從apk文件中加載多個(gè)dex文件。
- 使用步驟:
- 修改對(duì)應(yīng)工程目錄下的build.gradle文件舔庶,在defaultConfig中添加multiDexEnabled true這個(gè)配置項(xiàng)抛蚁。
- 在build.gradle的dependencies中添加multidex的依賴:compile 'com.android.support:multidex:1.0.0'
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
applicationId "cn.hudp.androiddevartnote"
minSdkVersion 14
targetSdkVersion 23
versionCode 1
versionName "1.0"
multiDexEnabled true //關(guān)鍵部分
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:23.4.0'
compile 'com.android.support:multidex:1.0.0' //關(guān)鍵部分
}
- 代碼中加入支持multidex功能陈醒。
第一種方案,在manifest文件中指定Application為MultiDexApplication瞧甩。
第二種方案钉跷,讓應(yīng)用的Application繼承MultiDexApplication。
第三種方案肚逸,重寫attachBaseContext方法爷辙,這個(gè)方法比onCreate還要先執(zhí)行。
public class BaseApplication extends Application {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
MultiDex.install(this);
}
}
采用上面的配置項(xiàng)后朦促,如果這個(gè)應(yīng)用方法數(shù)沒有越界膝晾,那么Gradle是不會(huì)生成多個(gè)dex文件的,當(dāng)方法數(shù)越界后务冕,Gradle就會(huì)在apk中打包2個(gè)或多個(gè)dex文件血当。當(dāng)需要指定主dex文件中所包含的類,這時(shí)候就需要通過--multi-dex-list來選項(xiàng)來實(shí)現(xiàn)這個(gè)功能禀忆。
//在對(duì)應(yīng)工程目錄下的build.gradle文件歹颓,加入
afterEvaluate {
println "afterEvaluate"
tasks.matching {
it.name.startsWith('dex')
}.each { dx ->
def listFile = project.rootDir.absolutePath + '/app/maindexlist.txt'
println "root dir:" + project.rootDir.absolutePath
println "dex task found: " + dx.name
if (dx.additionalParameters == null) {
dx.additionalParameters = []
}
dx.additionalParameters += '--multi-dex'
dx.additionalParameters += '--main-dex-list=' + listFile
dx.additionalParameters += '--minimal-main-dex'
}
}
//maindexlist.txt
com/ryg/multidextest/TestApplication.class
com/ryg/multidextest/MainActivity.class
// multidex 這9個(gè)類必須在主Dex中
android/support/multidex/MultiDex.class
android/support/multidex/MultiDexApplication.class
android/support/multidex/MultiDexExtractor.class
android/support/multidex/MultiDexExtractor$1.class
android/support/multidex/MultiDex$V4.class
android/support/multidex/MultiDex$V14.class
android/support/multidex/MultiDex$V19.class
android/support/multidex/ZipUtil.class
android/support/multidex/ZipUtil$CentralDirectory.class
需要注意multidex的jar中的9個(gè)類必須要打包到主dex中,因?yàn)锳pplication的attachBaseContext方法中需要用到MultiDex.install(this)需要用到MultiDex油湖。
Multidex的缺點(diǎn):
- 啟動(dòng)速度會(huì)降低,由于應(yīng)用啟動(dòng)時(shí)會(huì)加載額外的dex文件领跛,這將導(dǎo)致應(yīng)用的啟動(dòng)速度降低乏德,甚至產(chǎn)生ANR現(xiàn)象。
- 因?yàn)镈alvik linearAlloc的bug吠昭,可以導(dǎo)致使用multidex的應(yīng)用無法在Android4.0之前的手機(jī)上運(yùn)行喊括,需要做大量兼容性測(cè)試。
13.3 Android動(dòng)態(tài)加載技術(shù)
各種插件化方案都需要解決3個(gè)基礎(chǔ)性問題:
- 資源訪問矢棚,因?yàn)椴寮蟹彩且訰開頭的資源文件都不能訪問了郑什。
- Activity的生命周期管理,因?yàn)樗拗鲃?dòng)態(tài)將Activity.java加載到內(nèi)存的時(shí)候蒲肋,是不具備Activity的任何特性的蘑拯,只是一個(gè)普通的java類。
- ClassLoader的管理兜粘,為了避免多個(gè)ClassLoader加載了同一個(gè)類所引發(fā)的類型轉(zhuǎn)換錯(cuò)誤申窘。
介紹兩個(gè)具有代表性的插件化解決方案
- dynamic-load-apk
-
Droid Plugin
DL方案對(duì)Framework的表層做了處理,依賴that語法孔轴,編寫插件代碼和主程序代碼需單獨(dú)區(qū)分剃法;而DroidPlugin通過Hook增強(qiáng)了Framework層的很多系統(tǒng)服務(wù),開發(fā)插件就跟開發(fā)獨(dú)立app差不多路鹰;就拿Activity生命周期的管理來說贷洲,DL的代理方式就像是牽線木偶收厨,插件是操縱傀儡;而DroidPlugin則是借尸還魂优构,插件是有血有肉的系統(tǒng)管理的真正組件诵叁;DroidPlugin Hook了系統(tǒng)幾乎所有的Sevice,欺騙了大部分的系統(tǒng)API俩块;
(PS:這部分DL以及DP的分析來自于weishu)
13.4 反編譯初步
- 使用dex2jar和jd-gui反編譯apk
- 使用apktool對(duì)apk進(jìn)行二次打包
這塊不是難點(diǎn)黎休,網(wǎng)上資料也相當(dāng)多,跳過啦玉凯。
第十四章 JNI與NDK編程
參考博主的另一篇文章Android NDK初體驗(yàn)势腮,帶你快速進(jìn)入NDK的世界