综合技术

使用CrashHandler来获取应用的crash信息

首先需要实现一个UncaughtExceptionHandler对象,在它的uncaughtException方法中获取异常信息并将其存储在SD卡中或者上传到服务器供开发人员分析,然后调用Thread的setDefaultUncaughtExceptionHandler方法将它设置为线程默认的异常处理器,由于默认异常处理器是Thread类的静态成员,因此它的作用对象是当前进程的所有线程。

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 {
            //导出异常信息到SD卡中
            dumpExceptionToSDCard(ex);
            //这里可以上传异常信息到服务器,便于开发人员分析日志从而解决bug
            uploadExceptionToServer();
        } catch(IOException e){
            e.printStackTrace();
        }

        ex.printStackTrace();
        //如果系统提供了默认的异常处理器,则交给系统去结束程序,否则就由自己结束自己
        if(mDefaultCrashHandler != null) {
            mDefaultCrashHandler.uncaughtException(thread,ex);
        } else {
            Process.killProcess(Process.myPid());
        }
    }

    private void dumpExceptionToSDCard(Throwable ex) throws IOException {
        //如果SD卡不存在或无法使用,则无法把异常信息写入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版本号
        pw.print("OS Version:" );
        pw.print(Build.VERSION.RELEASE);
        pw.print("_");
        pw.println(Build.VERSION.SDK_INT);
        
        //手机制造商
        pw.print("Vendor: ");
        pw.println(Build.MANUFACTURER);
        
        //手机型号
        pw.print("Model: ");
        pw.println(Build.MODEL);
        
        //CPU架构
        pw.print("CPU ABI:");
        pw.println(Build.CPU_ABI);
    }
    
    private void uploadExceptionToServer() {
        
    }
}

64K方法数限制原理与解决方案

64k方法数问题本质上是指Android Dalvik可执行文件.dex中的Java方法数引用超过65536,64k计算方法是65536除以1024。

64K限制的原因

Android APK文件本质上是一个压缩文件,它里面包含的classes.dex文件是可执行的Dalvik字节码文件,这个.dex文件中存放的时所有编译后的Java代码。Dalvik可执行文件规范限制了单个.dex文件最多能引用的方法数是65536个,这其中包含了Android Framework,APP引用的第三方函数库以及APP自身的方法。

使用multidex解决64K限制的问题:

Google推出了一个名为MultiDex Support Library的函数库,当我们下载了Android
Support Libraries之后,可以在<sdk>/extras/android/support/multidex/目录中找到这个函数库。
将应用的方法数降低到64K以下:

  • 检查应用的直接和间接第三方依赖
  • 使用Proguard移除无用的代码
配置MultiDex

首先需要配置Application Module的build.gradle文件,在defaultConfig中添加multiDexEnabled true这个配置项,接着在dependencies中添加multidex依赖:compile 'com.android.support:multidex:1.0.0'
在代码中加入支持multidex的功能,三选一

  • 在manifest文件中指定Application为MultiDexApplication
  • 让应用的Application继承MultiDexApplication
  • 重写Application的attachBaseContext方法,在该方法中加入MultiDex.install(this);
//这个方法是在onCreate之前执行的,是开发者可以控制的应用最早执行的方法。
protected void attachBaseContext(Context base) {
    super.attachBaseContext(base);
    MultiDex.install(this);
}
在开发阶段优化MultiDex的构建

MultiDex会增加构建APK的时间,原因在于构建系统需要经过复杂的计算决定哪些类要包含在主dex文件中,哪些类可以包含在从dex文件中。
在工程主模块的build.gradle文件中使用productFlavors来创建两个flavor:一个是开发阶段使用的,一个是生产阶段使用的。开发阶段的flavor中,设置minSdkVersion为21,这使得构建系统使用ART支持的格式更快的生成MultiDex的输出,生成阶段的flavor中,设置minSdkVersion为应用实际支持的最低版本号。

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文件,接着通过dex2jar反编译classes.dex,然后通过jd-gui来打开反编译后的jar包。

《Android开发艺术探讨》综合技术
《Android高级进阶》第21章 64K方法数限制原理与解决方案

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,539评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,911评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,337评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,723评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,795评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,762评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,742评论 3 416
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,508评论 0 271
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,954评论 1 308
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,247评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,404评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,104评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,736评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,352评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,557评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,371评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,292评论 2 352

推荐阅读更多精彩内容