綜合技術(shù)

使用CrashHandler來(lái)獲取應(yīng)用的crash信息

首先需要實(shí)現(xiàn)一個(gè)UncaughtExceptionHandler對(duì)象,在它的uncaughtException方法中獲取異常信息并將其存儲(chǔ)在SD卡中或者上傳到服務(wù)器供開(kāi)發(fā)人員分析,然后調(diào)用Thread的setDefaultUncaughtExceptionHandler方法將它設(shè)置為線程默認(rèn)的異常處理器,由于默認(rèn)異常處理器是Thread類的靜態(tài)成員,因此它的作用對(duì)象是當(dāng)前進(jìn)程的所有線程。

public class CrashHandler implements 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 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();
    }

    @Override
    public void uncaughtException(Thread thread,Throwable ex) {
        try {
            //導(dǎo)出異常信息到SD卡中
            dumpExceptionToSDCard(ex);
            //這里可以上傳異常信息到服務(wù)器,便于開(kāi)發(fā)人員分析日志從而解決bug
            uploadExceptionToServer();
        } 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 {
        //如果SD卡不存在或無(wú)法使用,則無(wú)法把異常信息寫入SD卡
        if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
            if (DEBUG) {
                Log.w(TAG,"sdcard unmounted,skip dump exception");
                return;
            }
        }

        File dir = new File(PATH);
        if (!dir.exists()) {
            dir.mkdirs();
        }
        long current = System.currentTimeMillis();
        String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
        File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
        
        try {
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
            pw.println(time);
            dumpPhoneInfo(pw);
            pw.println();
            ex.printStackTrace(pw);
            pw.close();
        } catch (Exception e) {
            Log.e(TAG,"dump crash info failed");
        }
    }
    
    private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
        PackageManager pm = mContext.getPackageManager();
        PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(),PackageManager.GET_ACTIVITIES);
        pw.print("App Version:");
        pw.print(pi.versionName);
        pw.print('-');
        pw.print(pi.versionCode);
        
        //Android版本號(hào)
        pw.print("OS Version:" );
        pw.print(Build.VERSION.RELEASE);
        pw.print("_");
        pw.println(Build.VERSION.SDK_INT);
        
        //手機(jī)制造商
        pw.print("Vendor: ");
        pw.println(Build.MANUFACTURER);
        
        //手機(jī)型號(hào)
        pw.print("Model: ");
        pw.println(Build.MODEL);
        
        //CPU架構(gòu)
        pw.print("CPU ABI:");
        pw.println(Build.CPU_ABI);
    }
    
    private void uploadExceptionToServer() {
        
    }
}

64K方法數(shù)限制原理與解決方案

64k方法數(shù)問(wèn)題本質(zhì)上是指Android Dalvik可執(zhí)行文件.dex中的Java方法數(shù)引用超過(guò)65536,64k計(jì)算方法是65536除以1024。

64K限制的原因

Android APK文件本質(zhì)上是一個(gè)壓縮文件,它里面包含的classes.dex文件是可執(zhí)行的Dalvik字節(jié)碼文件,這個(gè).dex文件中存放的時(shí)所有編譯后的Java代碼。Dalvik可執(zhí)行文件規(guī)范限制了單個(gè).dex文件最多能引用的方法數(shù)是65536個(gè),這其中包含了Android Framework,APP引用的第三方函數(shù)庫(kù)以及APP自身的方法。

使用multidex解決64K限制的問(wèn)題:

Google推出了一個(gè)名為MultiDex Support Library的函數(shù)庫(kù),當(dāng)我們下載了Android
Support Libraries之后,可以在<sdk>/extras/android/support/multidex/目錄中找到這個(gè)函數(shù)庫(kù)。
將應(yīng)用的方法數(shù)降低到64K以下:

  • 檢查應(yīng)用的直接和間接第三方依賴
  • 使用Proguard移除無(wú)用的代碼
配置MultiDex

首先需要配置Application Module的build.gradle文件,在defaultConfig中添加multiDexEnabled true這個(gè)配置項(xiàng),接著在dependencies中添加multidex依賴:compile 'com.android.support:multidex:1.0.0'
在代碼中加入支持multidex的功能,三選一

  • 在manifest文件中指定Application為MultiDexApplication
  • 讓應(yīng)用的Application繼承MultiDexApplication
  • 重寫Application的attachBaseContext方法,在該方法中加入MultiDex.install(this);
//這個(gè)方法是在onCreate之前執(zhí)行的,是開(kāi)發(fā)者可以控制的應(yīng)用最早執(zhí)行的方法。
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    MultiDex.install(this);
}
在開(kāi)發(fā)階段優(yōu)化MultiDex的構(gòu)建

MultiDex會(huì)增加構(gòu)建APK的時(shí)間,原因在于構(gòu)建系統(tǒng)需要經(jīng)過(guò)復(fù)雜的計(jì)算決定哪些類要包含在主dex文件中,哪些類可以包含在從dex文件中。
在工程主模塊的build.gradle文件中使用productFlavors來(lái)創(chuàng)建兩個(gè)flavor:一個(gè)是開(kāi)發(fā)階段使用的,一個(gè)是生產(chǎn)階段使用的。開(kāi)發(fā)階段的flavor中,設(shè)置minSdkVersion為21,這使得構(gòu)建系統(tǒng)使用ART支持的格式更快的生成MultiDex的輸出,生成階段的flavor中,設(shè)置minSdkVersion為應(yīng)用實(shí)際支持的最低版本號(hào)。

android {
    productFlavors {
        dev {
            minSdkVersion 21
        }  
        prod {
            minSdkVersion 14
        }
    }
    ...
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles getDefaultProguardFile('proguard-android.txt'),'proguard-rules.pro'
        }
    }
}
反編譯初步

apktool
dex2jar
jd-gui

  • 使用dex2jar和jd-gui反編譯apk
    首先將apk解壓后提取classes.dex文件,接著通過(guò)dex2jar反編譯classes.dex,然后通過(guò)jd-gui來(lái)打開(kāi)反編譯后的jar包。

《Android開(kāi)發(fā)藝術(shù)探討》綜合技術(shù)
《Android高級(jí)進(jìn)階》第21章 64K方法數(shù)限制原理與解決方案

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容