CrashHandler(全局捕获异常)和应用方法数越界
题外话,虽然我已经干了2年了,但是确实很少用到这个类,个人觉得一个好的app应该有这个一个全局捕获的机制,因为 毕竟任何一个用户都不想看到那个程序异常退出的框,为了用户体验,个人觉得美工应该给程序做个漂亮的框提升体验,这个框就是平时有时候程序出问题了,会弹出一个发送报告的框,就是那一个,虽然自己写这个全局捕获异常的类很容易 第三方的有可能有广告啥的,但咋毕竟不是大神,可以用第三方腾讯的Bugly,自己写的话就如下,回到正题@_@.、
package com.robot.zhangyun.deme.md;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.support.v7.app.AlertDialog;
import android.util.Log;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.Thread.UncaughtExceptionHandler;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* Created by zhangyun on 2016/8/10.
*/
public class ZyCrashHandler implements UncaughtExceptionHandler {
private static final String TAG="ZyCrashHandler";
private static final Boolean DEBUG=true;
private static final String PATH= Environment.getExternalStorageDirectory().getPath()+"/TheOne/log/";
private static final String FILE_NAME="crash";
private static final String FILE_NAME_SUFFIX=".trace";
private static ZyCrashHandler sInstance=new ZyCrashHandler();
private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
private Context mContext;
ZyCrashHandler(){
}
public static ZyCrashHandler getsInstance(){
return sInstance;
}
public void init(Context context){
mDefaultCrashHandler=Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
mContext=context.getApplicationContext();
}
/**
*程序崩溃时会调用此方法
* @param thread 出现未捕获异常的线程
* @param throwable 未捕获的异常
*/
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
try {
dumpExceptionToSDCard(throwable);
//上传sd卡上的异常信息文件到服务器,这样开发人员就可以查看了
//uploadExceptionToServer();
new Thread() {
@Override
public void run() {
Looper.prepare();
new AlertDialog.Builder(mContext).setTitle("提示").setCancelable(false)
.setMessage("程序崩溃了...").setNeutralButton("我知道了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
System.exit(0);
}
})
.create().show();
Looper.loop();
}
}.start();
}catch (IOException e){
e.printStackTrace();
}
}
/**
* 导出异常信息和手机应用信息到sd卡
* @param throwable
* @throws IOException
*/
private void dumpExceptionToSDCard(Throwable throwable) throws IOException{
//Environment.MEDIA_MOUNTED存储媒体已经挂载,并且挂载点可读/写
if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
if(DEBUG){
Log.w(TAG,"SD卡不可用");
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();
throwable.printStackTrace(pw);
pw.close();
}catch (Exception E){
Log.e(TAG,"存储异常信息失败");
}
}
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.print(Build.VERSION.SDK_INT);
//手机制造商
pw.print("Vendor: ");
pw.println(Build.MANUFACTURER);
//手机型号
pw.print("Model: ");
pw.println(Build.MODEL);
//cpu架构
pw.print("CPU ABI: ");
pw.print(Build.CPU_ABI);
}
}
package com.robot.zhangyun.deme.md;
import android.app.Application;
/**
* Created by zhangyun on 2016/8/10.
*/
public class ZyApplication extends Application {
private static ZyApplication sInstance;
@Override
public void onCreate() {
super.onCreate();
sInstance=this;
ZyCrashHandler zyapplication=ZyCrashHandler.getsInstance();
zyapplication.init(this);
}
public static ZyApplication getsInstance(){
return sInstance;
}
}
为何方法数未超过65536但是在低版本手机上安装异常?
dexopt在应用安装时被系统用来优化dex文件,dexopt采用固定大小的缓冲区LinearAlloc存储应用中的所有方法的信息,虽然在新版本手机上这个缓冲区的大小有8MB或者16MB,但在2.x系列的手机上这个缓冲区大小为5MB,因此在低版本上时dexopt就会报错,导致安装应用失败。当整个应用的方法数超过65536越界抛出异常的时候怎么办或者说当方法数未超过65536但是在低版本手机上安装异
常终止怎么办?2.1 删除无用的代码和第三方库,使用multidex方案将dex文件拆分成多个dex来避免单个dex文件方法数越界。android5 .0以前需要引入android sdk目录下的extras/android/support/multidex/library/libs/android-support-multidex.jar,android5.0以后默认支持了multidex.
2.1.1 使用android sdk Build Tools 21.1及以上版本,
2.1.2 修改app下的build.gradle 添加 multiDexEnable true配置,
2.1.3 添加依赖 compile ‘com.android.support:multidex:1.0.0’,
2.1.4 将application的name换成”android.support.multidex.MultiDexApplication”或者写个类继承application,然后在attachBaseContext方法中调用 Multidex.install(this);
2.2 删除无用的代码和第三方库,使用动态加载.直接加载一个dex形式文件,将部分代码打包到一个单独的dex
(或dex格式的jar,apk),在程序 需要时动态加载dex中的类。具体查看https://github.com/singwhatiwanna/dyna
mic-load-apk,平时不怎么用到,就懒得写了,这里不解释。