您好,登錄后才能下訂單哦!
本文實(shí)例講述了Android編程實(shí)現(xiàn)捕獲程序異常退出時(shí)的錯(cuò)誤log信息功能。分享給大家供大家參考,具體如下:
很多時(shí)候我們程序無(wú)緣無(wú)故的就掛掉了,讓我們一頭霧水,如果剛好我們?cè)谡{(diào)試,那我們可以通過(guò)錯(cuò)誤log來(lái)查看是什么原因引起的程序崩潰。但是當(dāng)我們把程序發(fā)別人使用時(shí),就沒(méi)那么好運(yùn)了,那我們要怎么樣才能捕獲到那個(gè)錯(cuò)誤異常呢?還好Android給我們提供了UncaughtExceptionHandler 這個(gè)類(lèi),我們可以通過(guò)實(shí)現(xiàn)這個(gè)類(lèi)的接口,來(lái)全局捕獲那個(gè)讓程序崩掉的錯(cuò)誤log信息。可以將錯(cuò)誤的log保存在本地,也可以發(fā)送給服務(wù)器后臺(tái)。下面來(lái)看下UncaughtExceptionHandler 的實(shí)現(xiàn)類(lèi)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實(shí)例,單例模式。 * * @return 返回CrashHandler實(shí)例 */ 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)默認(rèn)的uncaughtException處理類(lèi)實(shí)例 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler(); // 設(shè)置成我們處理uncaughtException的類(lèi) Thread.setDefaultUncaughtExceptionHandler(this); } @Override public void uncaughtException(Thread thread, Throwable ex) { Log.d(TAG, "uncaughtException:" + ex); if (!handleException(ex) && mDefaultHandler != null) { // 如果用戶(hù)沒(méi)有處理異常就由系統(tǒng)默認(rèn)的異常處理器來(lái)處理 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); // 收集錯(cuò)誤日志 collectCrashInfo(ex); // 保存錯(cuò)誤日志 saveErrorLog(); //TODO: 這里可以加一個(gè)網(wǎng)絡(luò)的請(qǐng)求,發(fā)送錯(cuò)誤log給后臺(tái) // sendErrorLog(); return true; } //保存日志到/mnt/sdcard/AppLog/目錄下,文件名已時(shí)間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(); } } } } } //收集錯(cuò)誤信息 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(); //將錯(cuò)誤信息加入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è)備的信息通過(guò)反射方式 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中的使用非常簡(jiǎn)單,只要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)容感興趣的讀者可查看本站專(zhuān)題:《Android開(kāi)發(fā)入門(mén)與進(jìn)階教程》、《Android調(diào)試技巧與常見(jiàn)問(wèn)題解決方法匯總》、《Android編程之a(chǎn)ctivity操作技巧總結(jié)》、《Android操作json格式數(shù)據(jù)技巧總結(jié)》、《Android數(shù)據(jù)庫(kù)操作技巧總結(jié)》、《Android文件操作技巧匯總》、《Android資源操作技巧匯總》及《Android控件用法總結(jié)》
希望本文所述對(duì)大家Android程序設(shè)計(jì)有所幫助。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。