Android編程實現(xiàn)捕獲程序異常退出時的錯誤log信息功能詳解
本文實例講述了Android編程實現(xiàn)捕獲程序異常退出時的錯誤log信息功能。分享給大家供大家參考,具體如下:
很多時候我們程序無緣無故的就掛掉了,讓我們一頭霧水,如果剛好我們在調(diào)試,那我們可以通過錯誤log來查看是什么原因引起的程序崩潰。但是當(dāng)我們把程序發(fā)別人使用時,就沒那么好運了,那我們要怎么樣才能捕獲到那個錯誤異常呢?還好Android給我們提供了UncaughtExceptionHandler 這個類,我們可以通過實現(xiàn)這個類的接口,來全局捕獲那個讓程序崩掉的錯誤log信息??梢詫㈠e誤的log保存在本地,也可以發(fā)送給服務(wù)器后臺。下面來看下UncaughtExceptionHandler 的實現(xiàn)類CrashHandler吧。
CrashHandler.Java
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
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.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
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.util.Log;
import android.widget.Toast;
public class CrashHandler implements UncaughtExceptionHandler {
private static final String TAG = CrashHandler.class.getSimpleName();
private static final String SINGLE_RETURN = "\n";
private static final String SINGLE_LINE = "--------------------------------";
private static CrashHandler mCrashHandler;
private Context mContext;
private UncaughtExceptionHandler mDefaultHandler;
private StringBuffer mErrorLogBuffer = new StringBuffer();
/**
* 獲取CrashHandler實例,單例模式。
*
* @return 返回CrashHandler實例
*/
public static CrashHandler getInstance() {
if (mCrashHandler == null) {
synchronized (CrashHandler.class) {
if (mCrashHandler == null) {
mCrashHandler = new CrashHandler();
}
}
}
return mCrashHandler;
}
public void init(Context context) {
mContext = context;
// 獲取系統(tǒng)默認的uncaughtException處理類實例
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 設(shè)置成我們處理uncaughtException的類
Thread.setDefaultUncaughtExceptionHandler(this);
}
@Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.d(TAG, "uncaughtException:" + ex);
if (!handleException(ex) && mDefaultHandler != null) {
// 如果用戶沒有處理異常就由系統(tǒng)默認的異常處理器來處理
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
android.os.Process.killProcess(android.os.Process.myPid());
}
}
//處理異常事件
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(mContext, "很抱歉,程序出現(xiàn)異常,即將退出.", Toast.LENGTH_SHORT)
.show();
Looper.loop();
}
}).start();
// 收集設(shè)備參數(shù)信息
collectDeviceInfo(mContext);
// 收集錯誤日志
collectCrashInfo(ex);
// 保存錯誤日志
saveErrorLog();
//TODO: 這里可以加一個網(wǎng)絡(luò)的請求,發(fā)送錯誤log給后臺
// sendErrorLog();
return true;
}
//保存日志到/mnt/sdcard/AppLog/目錄下,文件名已時間yyyy-MM-dd_hh-mm-ss.log的形式保存
private void saveErrorLog() {
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh-mm-ss", Locale.getDefault());
String format = sdf.format(new Date());
format += ".log";
String path = Environment.getExternalStorageDirectory().getPath()+"/AppLog/";
File file = new File(path);
if (!file.exists()){
file.mkdirs();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(path+format);
fos.write(mErrorLogBuffer.toString().getBytes());
fos.flush();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
fos = null;
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
//收集錯誤信息
private void collectCrashInfo(Throwable ex) {
Writer info = new StringWriter();
PrintWriter printWriter = new PrintWriter(info);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
String result = info.toString();
printWriter.close();
//將錯誤信息加入mErrorLogBuffer中
append("", result);
mErrorLogBuffer.append(SINGLE_LINE + SINGLE_RETURN);
Log.d(TAG, "saveCrashInfo2File:" + mErrorLogBuffer.toString());
}
//收集應(yīng)用和設(shè)備信息
private void collectDeviceInfo(Context context) {
//每次使用前,清掉mErrorLogBuffer里的內(nèi)容
mErrorLogBuffer.setLength(0);
mErrorLogBuffer.append(SINGLE_RETURN + SINGLE_LINE + SINGLE_RETURN);
//獲取應(yīng)用的信息
PackageManager pm = context.getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo(context.getPackageName(),
PackageManager.GET_ACTIVITIES);
if (pi != null) {
append("versionCode", pi.versionCode);
append("versionName", pi.versionName);
append("packageName", pi.packageName);
}
} catch (NameNotFoundException e) {
e.printStackTrace();
}
mErrorLogBuffer.append(SINGLE_LINE + SINGLE_RETURN);
//獲取設(shè)備的信息
Field[] fields = Build.class.getDeclaredFields();
getDeviceInfoByReflection(fields);
fields = Build.VERSION.class.getDeclaredFields();
getDeviceInfoByReflection(fields);
mErrorLogBuffer.append(SINGLE_LINE + SINGLE_RETURN);
}
//獲取設(shè)備的信息通過反射方式
private void getDeviceInfoByReflection(Field[] fields) {
for (Field field : fields) {
try {
field.setAccessible(true);
append(field.getName(), field.get(null));
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
//mErrorLogBuffer添加友好的log信息
private void append(String key, Object value) {
mErrorLogBuffer.append("" + key + ":" + value + SINGLE_RETURN);
}
}
在application中的使用非常簡單,只要init就好了,之后我們就只要等異常出現(xiàn)吧。
CrashApplication.java
import android.app.Application;
public class CrashApplication extends Application{
@Override
public void onCreate() {
super.onCreate();
CrashHandler.getInstance().init(this);
}
}
不要忘記在AndroidManifest.xml聲明我們的CrashApplication 。
AndroidManifest.xml
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:name=".CrashApplication"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.crashtestdemo.MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android開發(fā)入門與進階教程》、《Android調(diào)試技巧與常見問題解決方法匯總》、《Android編程之a(chǎn)ctivity操作技巧總結(jié)》、《Android操作json格式數(shù)據(jù)技巧總結(jié)》、《Android數(shù)據(jù)庫操作技巧總結(jié)》、《Android文件操作技巧匯總》、《Android資源操作技巧匯總》及《Android控件用法總結(jié)》
希望本文所述對大家Android程序設(shè)計有所幫助。
- Android獲取apk程序簽名信息代碼示例
- Android實現(xiàn)獲取應(yīng)用程序相關(guān)信息列表的方法
- Android開發(fā)之在程序中時時獲取logcat日志信息的方法(附demo源碼下載)
- 如何判斷軟件程序是否聯(lián)網(wǎng) 聯(lián)網(wǎng)狀態(tài)提示信息Android實現(xiàn)
- Android實現(xiàn)整理PackageManager獲取所有安裝程序信息
- Android ApplicationInfo 應(yīng)用程序信息的詳解
- Android編程獲取APP應(yīng)用程序基本信息輔助類【APP名稱、包名、圖標(biāo),版本號等】
- Android獲取手機型號/系統(tǒng)版本號/App版本號等信息實例講解
- Android開發(fā)獲取系統(tǒng)中已安裝程序信息的方法
相關(guān)文章
Android 8.1隱藏狀態(tài)欄圖標(biāo)的實例代碼
這篇文章主要介紹了Android 8.1隱藏狀態(tài)欄圖標(biāo),本文通過實例代碼給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-04-04
Android使用WebView實現(xiàn)全屏切換播放網(wǎng)頁視頻功能
這篇文章主要介紹了Android使用WebView實現(xiàn)全屏切換播放網(wǎng)頁視頻功能,本文通過實例代碼給大家介紹的非常詳細,需要的朋友可以參考下2019-07-07
Android網(wǎng)絡(luò)判斷知識小結(jié)
本文通過兩段實例代碼分別給大家介紹Android中判斷當(dāng)前網(wǎng)絡(luò)是否可用和Android 關(guān)于判斷應(yīng)用是否有網(wǎng)絡(luò)的相關(guān)知識,對android網(wǎng)絡(luò)判斷相關(guān)知識感興趣的朋友一起學(xué)習(xí)吧2015-12-12
Android貝塞爾曲線實現(xiàn)加入購物車拋物線動畫
這篇文章主要為大家詳細介紹了Android貝塞爾曲線實現(xiàn)加入購物車拋物線動畫,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-06-06
AndroidStudio修改Code Style來格式化自定義標(biāo)簽的xml文件方式
這篇文章主要介紹了AndroidStudio修改Code Style來格式化自定義標(biāo)簽的xml文件方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-03-03

