android开发异常信息收集程序代码
- - CSDN博客移动开发推荐文章首先创建全局的Application ,此Application全局通用. CrashHandler crashHandler = CrashHandler.getInstance();//这是收集异常信息的单例类,具体代码请看下文. crashHandler.init(getApplicationContext());//初始化.
首先创建全局的Application ,此Application全局通用。
package com.demo.utils; import com.demo.exception.CrashHandler; import android.app.Application; /** * 全局的context,任意位置调用 * @author Administrator * */ public class GlobalApplication extends Application { private static GlobalApplication instance; public static GlobalApplication getInstance() { return instance; } @Override public void onCreate() { super.onCreate(); instance = this; CrashHandler crashHandler = CrashHandler.getInstance();//这是收集异常信息的单例类,具体代码请看下文 crashHandler.init(getApplicationContext());//初始化 } }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.demo.test" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="18" /> <!-- 网络 --> <uses-permission android:name="android.permission.INTERNET" /> <!-- 访问sdcard --> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> <!-- 获取mac需要的网络状态 --> <uses-permission android:name="andorid.permission.CHANGE_CONFIGURATION" /> <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <application android:name="com.demo.utils.GlobalApplication" android:allowBackup="true" android:icon="@drawable/app_icon" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name="com.demo.activity.MyActivity" android:launchMode="singleTask" android:screenOrientation="landscape" android:theme="@android:style/Theme.NoTitleBar" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </activity> </application> </manifest>
crashHandler
package com.demo.exception; 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 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.Looper; import com.demo.utils.ConstantUtils; import com.demo.utils.PathUtils; import com.demo.utils.log.MyLog; /** * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告. * * @author user * 实现 UncaughtExceptionHandler接口 */ public class CrashHandler implements UncaughtExceptionHandler { public static final String TAG = "bug"; //系统默认的UncaughtException处理类 private Thread.UncaughtExceptionHandler mDefaultHandler; //CrashHandler实例 private static CrashHandler INSTANCE = new CrashHandler(); //程序的Context对象 private Context mContext; //用来存储设备信息和异常信息 private Map<String, String> infos = new HashMap<String, String>(); //用于格式化日期,作为日志文件名的一部分 private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); /** 保证只有一个CrashHandler实例 */ private CrashHandler() { } /** 获取CrashHandler实例 ,单例模式 */ public static CrashHandler getInstance() { return INSTANCE; } /** * 初始化 * * @param context */ public void init(Context context) { mContext = context; //获取系统默认的UncaughtException处理器 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); //设置该CrashHandler为程序的默认处理器 Thread.setDefaultUncaughtExceptionHandler(this); } /** * 当UncaughtException发生时会转入该函数来处理 */ @Override public void uncaughtException(Thread thread, Throwable ex) { if (!handleException(ex) && mDefaultHandler != null) { //如果用户没有处理则让系统默认的异常处理器来处理 mDefaultHandler.uncaughtException(thread, ex); } else { try { Thread.sleep(3000); } catch (InterruptedException e) { MyLog.e(TAG, "error : ", e); } mDefaultHandler.uncaughtException(thread, ex); //退出程序 // android.os.Process.killProcess(android.os.Process.myPid()); // System.exit(1); } } /** * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. * * @param ex * @return true:如果处理了该异常信息;否则返回false. */ private boolean handleException(Throwable ex) { if (ex == null) { return false; } //使用Toast来显示异常信息 new Thread() { @Override public void run() { Looper.prepare(); // Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show(); Looper.loop(); } }.start(); //收集设备参数信息 collectDeviceInfo(mContext); //保存日志文件 MyLog.i(TAG, "ex:" + ex.toString() + "--" + ex.getLocalizedMessage()); saveCrashInfo2File(ex); return true; } /** * 收集设备参数信息 * @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) { MyLog.e(TAG, "an error occured when collect package info", e); } Field[] fields = Build.class.getDeclaredFields(); for (Field field : fields) { try { field.setAccessible(true); } catch (Exception e) { MyLog.e(TAG, "an error occured when collect crash info", e); } } } /** * 保存错误信息到文件中 * * @param ex * @return 返回文件名称,便于将文件传送到服务器 */ 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) { MyLog.i(TAG, "cause:" + cause.toString() + "--"); cause.printStackTrace(printWriter); cause = cause.getCause(); } printWriter.close(); String result = writer.toString(); MyLog.i(TAG, "result:" + result); sb.append(result); try { long timestamp = System.currentTimeMillis(); String time = formatter.format(new Date()); String fileName = "crash-" + time + "-" + timestamp + ".log"; if (ConstantUtils.isOnline) { fileName = "crash-online.log"; } String path = PathUtils.BUGPATH; FileOutputStream fos = new FileOutputStream(path + fileName); fos.write(sb.toString().getBytes()); fos.close(); return fileName; } catch (Exception e) { MyLog.e(TAG, "an error occured while writing file...", e); } return null; } }上面信息涉及到下面的几个类ConstantUtils,PathUtils和MyLog
constantUtils
public class ConstantUtils { /***是否上线版本***/ public final static boolean isOnline = false; /*** * 时间格式 */ public static String timeFormat = "yyyy-MM-dd HH:mm:ss"; }
package com.demo.utils.log; import java.text.SimpleDateFormat; import java.util.Date; import android.util.Log; import com.demo.utils.ConstantUtils; import com.demo.utils.PathUtils; public class MyLog { private static String filename = "log.txt"; /*** * 打印日志 * @param tag * @param msg */ public static void d(String tag, String msg) { if (!ConstantUtils.isOnline) { Log.d(tag, msg); } } /*** * 打印日志 * @param tag * @param msg */ public static void i(String tag, String msg) { if (!ConstantUtils.isOnline) { Log.i(tag, msg); } } /*** * 打印日志 * @param tag * @param msg */ public static void e(String tag, String msg) { if (!ConstantUtils.isOnline) { Log.e(tag, msg); } } /*** * 打印日志 * @param tag * @param msg */ public static void e(String tag, String msg, Throwable tr) { if (!ConstantUtils.isOnline) { Log.e(tag, msg, tr); } } /*** * 打印日志 * @param tag * @param msg */ public static void w(String tag, String msg) { if (!ConstantUtils.isOnline) { Log.w(tag, msg); } } /*** * 写入文本到日志文件中 * @param value */ public static void write(String value) { if (!ConstantUtils.isOnline) { String newValue = getDataFormat(System.currentTimeMillis()) + " " + value; LogUtils.getInstances().write(PathUtils.LOGPATH, filename, newValue); } } /*** * 获取当前时间 * @param timeInMillis * @return */ private static String getDataFormat(long timeInMillis) { SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return dataFormat.format(new Date(timeInMillis)); } }
pathutils
package com.demo.utils; import java.io.File; import android.os.Environment; public class PathUtils { public final static String sdcardPath = Environment.getExternalStorageDirectory() + ""; public final static String PATH = sdcardPath + "/exception/" + AppUtils.getAppPackageName(); public final static String BUGPATH = PATH + "/bug/"; public final static String LOGPATH = PATH + "/log/"; public PathUtils() { File PATH = new File(PathUtils.PATH); if (!PATH.exists()) { PATH.mkdirs(); } File LOGPATH = new File(PathUtils.LOGPATH); if (!LOGPATH.exists()) { LOGPATH.mkdirs(); } File BUGPATH = new File(PathUtils.BUGPATH); if (!BUGPATH.exists()) { BUGPATH.mkdirs(); } } }