Android 重写系统Crash处理类,保存Crash信息到SD卡
2016-07-04 14:28
393 查看
import java.io.File; import java.io.FileOutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.io.Writer; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Field; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; import java.util.HashMap; import java.util.Map; import com.yrz.atourong.AtrApplication; import com.yrz.atourong.impl.CrashHandlerImpl; import com.yrz.atourong.model.BaseJson; import com.yrz.atourong.service.HttpCrashHandlerService; import android.content.Context; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.os.Build; import android.os.Environment; import android.os.Looper; import android.widget.Toast; /** * @author Stay 在Application中统一捕获异常,保存到文件中下次再打开时上传 */ public class CrashHandler implements UncaughtExceptionHandler ,CrashHandlerImpl{ /** * 是否开启日志输出,在Debug状态下开启, 在Release状态下关闭以提示程序性能 * */ public static final boolean DEBUG = true; /** 系统默认的UncaughtException处理类 */ private Thread.UncaughtExceptionHandler mDefaultHandler; /** CrashHandler实例 */ private static CrashHandler INSTANCE; /** 程序的Context对象 */ private Context mContext; private AtrApplication mApplication; HttpCrashHandlerService hchs; private Map<String, String> infos = new HashMap<String, String>(); //用于格式化日期,作为日志文件名的一部分 private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); private static final String SDCARD_ROOT = Environment.getExternalStorageDirectory().toString(); /** 保证只有一个CrashHandler实例 */ private CrashHandler() { } /** 获取CrashHandler实例 ,单例模式 */ public static CrashHandler getInstance() { if (INSTANCE == null) { INSTANCE = new CrashHandler(); } return INSTANCE; } /** * 初始化,注册Context对象, 获取系统默认的UncaughtException处理器, 设置该CrashHandler为程序的默认处理器 * * @param ctx */ public void init(Context ctx,AtrApplication app) { mContext = ctx; hchs = new HttpCrashHandlerService(mContext, this); mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(this); mApplication = app; } /** * 当UncaughtException发生时会转入该函数来处理 */ @Override public void uncaughtException(Thread thread, Throwable ex) { if (ex != null) { //收集设备参数信息 collectDeviceInfo(mContext); //保存日志文件 saveCrashInfo2File(ex); String msg = ex.getLocalizedMessage(); if(null!=msg&&msg.indexOf("com.baidu.android.pushservice.PushService")!=-1){ return; } } if (!handleException(ex) && mDefaultHandler != null) { // 如果用户没有处理则让系统默认的异常处理器来处理 // android.os.Process.killProcess(android.os.Process.myPid()); // Intent i = mContext.getPackageManager().getLaunchIntentForPackage( // mContext.getPackageName()); // i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // mContext.startActivity(i); mDefaultHandler.uncaughtException(thread, ex); System.exit(10); } else { // 如果自己处理了异常,则不会弹出错误对话框,则需要手动退出app try { Thread.sleep(3000); } catch (InterruptedException e) { } android.os.Process.killProcess(android.os.Process.myPid()); System.exit(10); } } /** * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 开发者可以根据自己的情况来自定义异常处理逻辑 * * @return true代表处理该异常,不再向上抛异常, * false代表不处理该异常(可以将该log信息存储起来)然后交给上层(这里就到了系统的异常处理)去处理, * 简单来说就是true不会弹出那个错误提示框,false就会弹出 */ private boolean handleException(final Throwable ex) { if (ex == null) { return false; } final String msg = ex.getLocalizedMessage(); final StackTraceElement[] stack = ex.getStackTrace(); // final String message = ex.getMessage(); // 使用Toast来显示异常信息 new Thread() { @Override public void run() { Looper.prepare(); // 可以只创建一个文件,以后全部往里面append然后发送,这样就会有重复的信息,个人不推荐 try { // if(BuildConfig.DEBUG){ // InitData updataUtil = new InitData(); // updataUtil.createSDFolder(InitData.getSDPath() + "/log"); // String fileName = InitData.getSDPath() + "/log/" + "F10" + DataUtil.getNOWDateMSS(); // File file = new File(fileName); // FileOutputStream fos = new FileOutputStream(file, true); // fos.write(msg != null ? msg.getBytes() : "".getBytes()); // for (int i = 0; i < stack.length; i++) { // fos.write(stack[i].toString().getBytes()); // } // fos.flush(); // fos.close(); // }else{ StringBuilder str_ex = new StringBuilder(); if(msg != null && msg.length() > 0){ str_ex.append(msg); } for (int i = 0; i < stack.length; i++) { str_ex.append(stack[i].toString()); } postReport(str_ex.toString()); // FileUtils.writeFileWithBugLog(mContext, str_ex.toString()); // } } catch (Exception e) { e.getStackTrace(); } Looper.loop(); } }.start(); return false; } // TODO 使用HTTP Post 发送错误报告到服务器 这里不再赘述 private void postReport(String message) { String model = android.os.Build.MODEL; String release = android.os.Build.VERSION.RELEASE; String versionName=getVersion(); hchs.gainCrashHandler(model,release,versionName,message); /* RequestParams rq = new RequestParams(); rq.put("phone_type", model); rq.put("phone_ver", "Android版本" + release+(versionName.equals("")?"":("@ver"+versionName))); rq.put("msg", message); mApplication.post(HttpServiceURL.EXCEPTION_LOG, rq, new JsonHttpResponseHandler(){ @Override public void onSuccess(int statusCode,Header[] headers, JSONObject response) { try { if (response != null) { if(!response.isNull("boolen")){ String result = response.getString("boolen"); if (result.equals("1")) { if(mContext != null) Toast.makeText(mContext, "程序出错,反馈日志已经发送到服务端.",Toast.LENGTH_LONG).show(); } } } } catch (Exception e) { } } }); */ } /** * 获取版本号 * @return 当前应用的版本号 */ public String getVersion() { try { PackageManager manager = mApplication.getPackageManager(); PackageInfo info = manager.getPackageInfo(mApplication.getPackageName(), 0); String version = info.versionName; return version; } catch (Exception e) { e.printStackTrace(); return ""; } } @Override public void gainCrashHandlersuccess(BaseJson response) { // TODO Auto-generated method stub try { if (response != null) { if(response.getBoolen()!=null){ String result = response.getBoolen(); if (result.equals("1")) { if(mContext != null) Toast.makeText(mContext, "程序出错,反馈日志已经发送到服务端.",Toast.LENGTH_LONG).show(); } } } } catch (Exception e) { } } @Override public void gainCrashHandlerfail() { // TODO Auto-generated method stub } /** * 为我们的应用程序设置自定义Crash处理 */ public void setCustomCrashHanler(Context context){ mContext = context; Thread.setDefaultUncaughtExceptionHandler(this); } /** * 显示提示信息,需要在线程中显示Toast * @param context * @param msg */ private void showToast(final Context context, final String msg){ new Thread(new Runnable() { @Override public void run() { Looper.prepare(); Toast.makeText(context, msg, Toast.LENGTH_LONG).show(); Looper.loop(); } }).start(); } /** * 收集设备参数信息 * * @param ctx */ public void collectDeviceInfo(Context ctx) { try { PackageManager pm = ctx.getPackageManager(); PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES); if (pi != null) { String versionName = pi.versionName == null ? "null" : pi.versionName; String versionCode = pi.versionCode + ""; infos.put("versionName", versionName); infos.put("versionCode", versionCode); } } catch (NameNotFoundException e) { // Log.e(TAG, "an error occured when collect package info", e); } Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true); infos.put(field.getName(), field.get(null).toString()); // Log.d(TAG, field.getName() + " : " + field.get(null)); } catch (Exception e) { // Log.e(TAG, "an error occured when collect crash info", e); } } } private String saveCrashInfo2File(Throwable ex) { StringBuffer sb = new StringBuffer(); for (Map.Entry<String, String> entry : infos.entrySet()) { String key = entry.getKey(); String value = entry.getValue(); sb.append(key + "=" + value + "\n"); } Writer writer = new StringWriter(); PrintWriter printWriter = new PrintWriter(writer); ex.printStackTrace(printWriter); Throwable cause = ex.getCause(); while (cause != null) { cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); String result = writer.toString(); sb.append(result); try { long timestamp = System.currentTimeMillis(); String time = formatter.format(new Date()); String fileName = "crash-" + time + "-" + timestamp + ".log"; if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { String path = "/sdcard/crash/"; File dir = new File(path); if (!dir.exists()) { dir.mkdirs(); } FileOutputStream fos = new FileOutputStream(path + fileName); fos.write(sb.toString().getBytes()); fos.close(); } return fileName; } catch (Exception e) { } return null; } }
相关文章推荐
- Android基础--程序第一次启动从欢迎页跳到引导页,下次再启动直接从欢迎页跳转到主页
- 解决Android方法数超出限定的问题
- AndroidStudio用GIT做版本管理时,.gitnore文件的配置相关
- Android 24小时制与12小时制格式设置
- 优秀的开源项目
- Fragment的生命周期
- Android AsyncTask异步任务
- 使用mob进行Android短信验证流程
- Android 6.0 - 动态权限管理的解决方案
- Android中popupwindow的用法
- (4.2.32.4)android热修复之Andfix方式:Andfix的实践应用
- Android Studio导出jar包
- 携程工作感言(一)(6.30)
- (4.2.32.2)android热修复之ClassLoader方式:Android 热补丁动态修复框架小结
- Android:onNewIntent()触发机制及注意事项
- Android 监听Home key
- 【Android Studio插件】Android Studio插件整理
- Android Tab -- 使用ViewPager、PagerAdapter来实现
- Android Studio异常:Fatal signal 11 (SIGSEGV) at 0x000000b4 (code=1)
- Android拓展系列(12)--使用Gradle发布aar项目到JCenter仓库