android - 奔溃日志收集,UncaughtExceptionHandler实现
16lz
2021-01-24
Thread.UncaughtExceptionHandler
UncaughtExceptionHandler(未捕获异常处理器)是Thread类的静态内部接口,用来处理用户没有try…caught的异常。也就是系统运行出错throw出来的异常。
UncaughtExceptionHandler里面只有一个方法:
void uncaughtException(Thread thread, Throwable ex);
实现原理
用户调用Thread的静态方法 setDefaultUncaughtExceptionHandler,将自己实现的未捕获异常处理类 设置到Thread私有的静态成员属性defaultUncaughtHandler,也就是简单的set过程。
private static UncaughtExceptionHandler defaultUncaughtHandler;public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) { Thread.defaultUncaughtHandler = handler; }
如果有用户未捕获的异常信息抛出,那么Thread就会调用其成员属性defaultUncaughtHandler的方法uncaughtException 并传入发生异常的thread和ex异常事件。
So what
那这货有什么用呢?
作为一个用户体验友好的app,应该在未知异常发生时给予用户一定的提示,或者主页面跳转。而不是直接直接奔溃闪退。再者,当我们把app上架到应用市场后,应具备奔溃日志收集功能,这样就可以在查看各个不同设备发生异常的情况,并在下一版本中改善这些情况。
好,那看代码就能知道怎个过程的始末了。
code
/** * Created by cchao on 2016/9/2. * E-mail: cchao1024@163.com * Description:奔溃日志收集 */public class CrashCatchHandler implements Thread.UncaughtExceptionHandler { public static final String TAG = "CrashCatchHandler"; //log保存路径 public static String LOG_PATH; private Context mContext; //CrashCatchHandler单例 private static CrashCatchHandler INSTANCE = new CrashCatchHandler ( ); //存储设备信息和异常信息 private Map< String, String > mInfoMap; private SimpleDateFormat mDateFormat; private CrashCatchHandler ( ) { mInfoMap = new LinkedHashMap<> ( ); mDateFormat = new SimpleDateFormat ( "yyyy-MM-dd-HH-mm-ss" ); } /** * 获取CrashCatchHandler实例 ,单例模式 */ public static CrashCatchHandler getInstance ( ) { return INSTANCE; } /** * 初始化 * * @param context */ public void init ( Context context ) { mContext = context; //设置该CrashCatchHandler为程序的默认未捕获异常处理器 Thread.setDefaultUncaughtExceptionHandler ( this ); //如果外存卡可以读写,放在外部存储器,否则放在内部存储器上 if ( Environment.getExternalStorageState ( ).equals ( Environment.MEDIA_MOUNTED ) ) { LOG_PATH = mContext.getExternalFilesDir ( "CARCH_LOG" ).getPath ( ); } else { LOG_PATH = mContext.getFilesDir ( ).getPath ( ); } } /** * 当UncaughtException发生时会转入该函数来处理 */ @Override public void uncaughtException ( Thread thread, Throwable ex ) { Toast.makeText ( mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG ).show ( ); //收集设备参数信息 collectDeviceInfo ( mContext ); //保存日志文件至本地 postService ( saveCrashLog ( ex ) ); } /** * 发送错误日志至服务端 * * @param fileName log路径 */ private void postService ( String fileName ) { if ( fileName != null ) { //发送奔溃日志 至服务器 // TODO: 2016/9/2 } } /** * 收集奔溃设备参数信息 */ public void collectDeviceInfo ( Context context ) { try { PackageManager pm = context.getPackageManager ( ); PackageInfo packageInfo = pm.getPackageInfo ( context.getPackageName ( ), PackageManager.GET_ACTIVITIES ); if ( packageInfo != null ) { String appName = packageInfo.applicationInfo.packageName; String versionName = packageInfo.versionName + ""; String versionCode = packageInfo.versionCode + ""; mInfoMap.put ( "包名", appName ); mInfoMap.put ( "版本名", versionName ); mInfoMap.put ( "版本号", versionCode ); } } catch ( PackageManager.NameNotFoundException e ) { Log.e ( TAG, "收集设备信息出错", e ); } Field[] fields = Build.class.getDeclaredFields ( ); for ( Field field : fields ) { try { field.setAccessible ( true ); mInfoMap.put ( field.getName ( ), field.get ( null ).toString ( ) ); } catch ( Exception e ) { Log.e ( TAG, "收集奔溃日志出错", e ); } } } /** * 保存错误信息到文件中 * * @param ex * @return 返回文件名称 */ private String saveCrashLog ( Throwable ex ) { StringBuilder stringBuilder = new StringBuilder ( ); for ( Map.Entry< String, String > entry : mInfoMap.entrySet ( ) ) { String key = entry.getKey ( ); String value = entry.getValue ( ); stringBuilder.append ( key + " = " + value + "\n" ); } //异常写入stringBuilder Writer writer = new StringWriter ( ); PrintWriter printWriter = new PrintWriter ( writer ); ex.printStackTrace ( printWriter ); //异常原因写入stringBuilder Throwable cause = ex.getCause ( ); if ( cause != null ) { cause.printStackTrace ( printWriter ); } printWriter.close ( ); String result = writer.toString ( ); stringBuilder.append ( result ); try { String time = mDateFormat.format ( new Date ( System.currentTimeMillis ( ) ) ); String fileName = time + ".txt"; File dir = new File ( LOG_PATH ); if ( ! dir.exists ( ) ) { dir.mkdirs ( ); } FileOutputStream fos = new FileOutputStream ( dir.getAbsolutePath ( ) + File.separator + fileName ); fos.write ( stringBuilder.toString ( ).getBytes ( ) ); fos.close ( ); return fileName; } catch ( Exception e ) { Log.e ( TAG, "写入文件异常", e ); } return null; }}
异常保存的本地路径是:Android/data/包名/files/CARCH_LOG/时间+.txt
在Application中去调用我们自定义的UncaughtExceptionHandler。
public class BaseApplication extends Application { @Override public void onCreate ( ) { super.onCreate ( ); CrashCatchHandler.getInstance().init(getApplicationContext ()); }}
更多相关文章
- Android(安卓)Dialog 应用
- Android跟踪NDK崩溃信息
- Android(安卓)FragmentManage FragmentTransaction介绍
- 【Android】获取手机通讯录中的联系人信息
- Android(安卓)显示网络信息 ConnectivityManager用法
- Android(安卓)用户偏好设置,SharedPreference
- Android真机调试不打印日志解决
- Android(安卓)GPS
- Android(安卓)UI日志