android開發異常資訊收集程式程式碼
阿新 • • 發佈:2019-01-02
首先建立全域性的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
上面資訊涉及到下面的幾個類ConstantUtils,PathUtils和MyLogpackage 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
public class ConstantUtils
{
/***是否上線版本***/
public final static boolean isOnline = false;
/***
* 時間格式
*/
public static String timeFormat = "yyyy-MM-dd HH:mm:ss";
}
MyLog
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();
}
}
}