Android編程實(shí)現(xiàn)項(xiàng)目中異常捕獲及對應(yīng)Log日志文件保存功能
本文實(shí)例講述了Android編程實(shí)現(xiàn)項(xiàng)目中異常捕獲及對應(yīng)Log日志文件保存功能。分享給大家供大家參考,具體如下:
做程序開發(fā),肯定離不開與BUG打交道,更加離不開程序異常的出現(xiàn)。在開發(fā)的時(shí)候,我們可以通斷點(diǎn)調(diào)試,日志打印,異常捕獲工具等方式發(fā)現(xiàn)或處理程序中的Exception。那客戶在使用我們的應(yīng)用時(shí),程序了問題,我們怎么可以知道呢?當(dāng)然,我們可以加上友盟統(tǒng)計(jì)等第三方工具。另外還能怎么做呢?那就是把異常信息通過文檔地形式保存下來,如果用戶在使用的時(shí)候程序出了異常,可以讓用戶把對應(yīng)的日志信息發(fā)給我們或客服人員,更好的是在程序中做好處理,把日志發(fā)到指定服務(wù)器(程序中記得添加網(wǎng)絡(luò)權(quán)限哦)中,我們也可以拿到日志,我們就能發(fā)現(xiàn)問題,處理問題啦。
異常捕獲的關(guān)鍵代碼:
/**
* UncaughtExceptionHandler:線程未捕獲異??刂破魇怯脕硖幚砦床东@異常的。 實(shí)現(xiàn)該接口并注冊為程序中的默認(rèn)未捕獲異常處理
* 這樣當(dāng)未捕獲異常發(fā)生時(shí),就可以做些異常處理操作 例如:收集異常信息,發(fā)送錯(cuò)誤報(bào)告 等。
*
* @description:
* @author ldm
* @date 2016-4-18 上午11:31:19
*/
public class MyExceptionHandler implements UncaughtExceptionHandler {
// 上下文
private Context mContext;
// 是否打開上傳
public boolean openUpload = true;
// Log文件路徑
private static final String LOG_FILE_DIR = "log";
// log文件的后綴名
private static final String FILE_NAME = ".log";
private static MyExceptionHandler instance = null;
// 系統(tǒng)默認(rèn)的異常處理(默認(rèn)情況下,系統(tǒng)會(huì)終止當(dāng)前的異常程序)
private UncaughtExceptionHandler mDefaultCrashHandler;
private MyExceptionHandler(Context cxt) {
// 獲取系統(tǒng)默認(rèn)的異常處理器
mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
// 將當(dāng)前實(shí)例設(shè)為系統(tǒng)默認(rèn)的異常處理器
Thread.setDefaultUncaughtExceptionHandler(this);
// 獲取Context,方便內(nèi)部使用
this.mContext = cxt.getApplicationContext();
}
public synchronized static MyExceptionHandler create(Context cxt) {
if (instance == null) {
instance = new MyExceptionHandler(cxt);
}
return instance;
}
/**
* 當(dāng)程序中有未被捕獲的異常,系統(tǒng)將會(huì)自動(dòng)調(diào)用#uncaughtException方法
* thread為出現(xiàn)未捕獲異常的線程,ex為未捕獲的異常,有了這個(gè)ex,我們就可以得到異常信息。
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
try {
// 保存導(dǎo)出異常日志信息到SD卡中
saveToSDCard(ex);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 如果系統(tǒng)提供了默認(rèn)的異常處理器,則交給系統(tǒng)去結(jié)束我們的程序,否則就由我們自己結(jié)束自己
Toast.makeText(mContext,
"很抱歉,程序出錯(cuò),即將退出:\r\n" + ex.getLocalizedMessage(),
Toast.LENGTH_LONG).show();
if (mDefaultCrashHandler != null) {
mDefaultCrashHandler.uncaughtException(thread, ex);
} else {
ex.printStackTrace();
}
}
}
/**
* 保存文件到SD卡
*
* @description:
* @author ldm
* @date 2016-4-18 上午11:37:17
*/
private void saveToSDCard(Throwable ex) throws Exception {
File file = FileUtil.getAppointFile(mContext.getPackageName()
+ File.separator + LOG_FILE_DIR,
getDataTime("yyyy-MM-dd-HH-mm-ss") + FILE_NAME);
PrintWriter pw = new PrintWriter(new BufferedWriter(
new FileWriter(file)));
// 導(dǎo)出發(fā)生異常的時(shí)間
pw.println(getDataTime("yyyy-MM-dd-HH-mm-ss"));
// 導(dǎo)出手機(jī)信息
savePhoneInfo(pw);
pw.println();
// 導(dǎo)出異常的調(diào)用棧信息
ex.printStackTrace(pw);
pw.close();
}
/**
* 保存手機(jī)硬件信息
*
* @description:
* @author ldm
* @date 2016-4-18 上午11:38:01
*/
private void savePhoneInfo(PrintWriter pw) throws NameNotFoundException {
// 應(yīng)用的版本名稱和版本號(hào)
PackageManager pm = mContext.getPackageManager();
PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(),
PackageManager.GET_ACTIVITIES);
pw.print("App Version: ");
pw.print(pi.versionName);
pw.print('_');
pw.println(pi.versionCode);
pw.println();
// android版本號(hào)
pw.print("OS Version: ");
pw.print(Build.VERSION.RELEASE);
pw.print("_");
pw.println(Build.VERSION.SDK_INT);
pw.println();
// 手機(jī)制造商
pw.print("Manufacturer: ");
pw.println(Build.MANUFACTURER);
pw.println();
// 手機(jī)型號(hào)
pw.print("Model: ");
pw.println(Build.MODEL);
pw.println();
}
/**
* 根據(jù)時(shí)間格式返回時(shí)間
*
* @description:
* @author ldm
* @date 2016-4-18 上午11:39:30
*/
private String getDataTime(String format) {
SimpleDateFormat df = new SimpleDateFormat(format);
return df.format(new Date());
}
}
使用的時(shí)候,我們需要在Application中初始化:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
MyExceptionHandler.create(this);
}
}
在AndroidManifest.xml文件中注冊好Application:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.ldm.exception"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:name="com.ldm.exception.MyApplication"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.ldm.activity.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>
</manifest>
注:更多關(guān)于Android Manifest權(quán)限控制文件的說明可點(diǎn)擊此處查看Android權(quán)限操作說明
文件上傳的方法有寫好,但是沒有具體實(shí)現(xiàn),比如一但有日志文件就上傳或是日志文件達(dá)到一定大小再上傳,這就要根據(jù)實(shí)際情況來定啦。
當(dāng)我們應(yīng)用出現(xiàn)異常時(shí),在手機(jī)文件夾中存在我們應(yīng)用包名的文件夾,里面就有日志文件。
附:完整Demo點(diǎn)擊此處本站下載。
更多關(guān)于Android相關(guān)內(nèi)容感興趣的讀者可查看本站專題:《Android調(diào)試技巧與常見問題解決方法匯總》、《Android開發(fā)入門與進(jìn)階教程》、《Android基本組件用法總結(jié)》、《Android視圖View技巧總結(jié)》、《Android布局layout技巧總結(jié)》及《Android控件用法總結(jié)》
希望本文所述對大家Android程序設(shè)計(jì)有所幫助。
相關(guān)文章
Android?自定義開源庫?EasyView實(shí)現(xiàn)詳解
這篇文章主要為大家介紹了Android自定義開源庫EasyView實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-04-04
詳解Android:向服務(wù)器提供數(shù)據(jù)之get、post方式
本篇文章主要介紹了詳解Android:向服務(wù)器提供數(shù)據(jù)之get、post方式,具有一定的參考價(jià)值,有興趣的可以了解一下。2017-03-03
Android開發(fā)快速實(shí)現(xiàn)底部導(dǎo)航欄示例
這篇文章主要為大家介紹了Android開發(fā)快速實(shí)現(xiàn)底部導(dǎo)航欄的示例代碼,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-04-04
Android對圖片Drawable實(shí)現(xiàn)變色示例代碼
這篇文章主要給大家介紹了關(guān)于Android對圖片Drawable實(shí)現(xiàn)變色的相關(guān)資料,文中通過示例代碼將實(shí)現(xiàn)的方法介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來一起看看吧。2017-08-08
Android App中實(shí)現(xiàn)可以雙擊放大和縮小圖片功能的實(shí)例
這篇文章主要介紹了Android App中實(shí)現(xiàn)可以雙擊放大和縮小圖片功能的實(shí)例,文中的例子不能做到逐級(jí)放大但可以做到邊界控制和以觸摸點(diǎn)為中心進(jìn)行放大,需要的朋友可以參考下2016-03-03

