日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > Android >内容正文

Android

Android中处理崩溃异常 (转)

發(fā)布時(shí)間:2025/3/13 Android 46 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Android中处理崩溃异常 (转) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

大家都知道,現(xiàn)在安裝Android系統(tǒng)的手機(jī)版本和設(shè)備千差萬(wàn)別,在模擬器上運(yùn)行良好的程序安裝到某款手機(jī)上說(shuō)不定就出現(xiàn)崩潰的現(xiàn)象,開發(fā)者個(gè)人不可能購(gòu)買所有設(shè)備逐個(gè)調(diào)試,所以在程序發(fā)布出去之后,如果出現(xiàn)了崩潰現(xiàn)象,開發(fā)者應(yīng)該及時(shí)獲取在該設(shè)備上導(dǎo)致崩潰的信息,這對(duì)于下一個(gè)版本的bug修復(fù)幫助極大,所以今天就來(lái)介紹一下如何在程序崩潰的情況下收集相關(guān)的設(shè)備參數(shù)信息和具體的異常信息,并發(fā)送這些信息到服務(wù)器供開發(fā)者分析和調(diào)試程序。

我們先建立一個(gè)crash項(xiàng)目,項(xiàng)目結(jié)構(gòu)如圖:

在MainActivity.java代碼中,代碼是這樣寫的:

package com.scott.crash;import android.app.Activity; import android.os.Bundle;public class MainActivity extends Activity {private String s;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);System.out.println(s.equals("any string"));} }

我們?cè)谶@里故意制造了一個(gè)潛在的運(yùn)行期異常,當(dāng)我們運(yùn)行程序時(shí)就會(huì)出現(xiàn)以下界面:

遇到軟件沒有捕獲的異常之后,系統(tǒng)會(huì)彈出這個(gè)默認(rèn)的強(qiáng)制關(guān)閉對(duì)話框。

我們當(dāng)然不希望用戶看到這種現(xiàn)象,簡(jiǎn)直是對(duì)用戶心靈上的打擊,而且對(duì)我們的bug的修復(fù)也是毫無(wú)幫助的。我們需要的是軟件有一個(gè)全局的異常捕獲器,當(dāng)出現(xiàn)一個(gè)我們沒有發(fā)現(xiàn)的異常時(shí),捕獲這個(gè)異常,并且將異常信息記錄下來(lái),上傳到服務(wù)器公開發(fā)這分析出現(xiàn)異常的具體原因。

接下來(lái)我們就來(lái)實(shí)現(xiàn)這一機(jī)制,不過首先我們還是來(lái)了解以下兩個(gè)類:android.app.Application和java.lang.Thread.UncaughtExceptionHandler。

Application:用來(lái)管理應(yīng)用程序的全局狀態(tài)。在應(yīng)用程序啟動(dòng)時(shí)Application會(huì)首先創(chuàng)建,然后才會(huì)根據(jù)情況(Intent)來(lái)啟動(dòng)相應(yīng)的Activity和Service。本示例中將在自定義加強(qiáng)版的Application中注冊(cè)未捕獲異常處理器。

Thread.UncaughtExceptionHandler:線程未捕獲異常處理器,用來(lái)處理未捕獲異常。如果程序出現(xiàn)了未捕獲異常,默認(rèn)會(huì)彈出系統(tǒng)中強(qiáng)制關(guān)閉對(duì)話框。我們需要實(shí)現(xiàn)此接口,并注冊(cè)為程序中默認(rèn)未捕獲異常處理。這樣當(dāng)未捕獲異常發(fā)生時(shí),就可以做一些個(gè)性化的異常處理操作。

大家剛才在項(xiàng)目的結(jié)構(gòu)圖中看到的CrashHandler.java實(shí)現(xiàn)了Thread.UncaughtExceptionHandler,使我們用來(lái)處理未捕獲異常的主要成員,代碼如下:

package com.scott.crash;import java.io.File; 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.Environment; import android.os.Looper; import android.util.Log; import android.widget.Toast;/*** UncaughtException處理類,當(dāng)程序發(fā)生Uncaught異常的時(shí)候,有該類來(lái)接管程序,并記錄發(fā)送錯(cuò)誤報(bào)告.* * @author user* */ public class CrashHandler implements UncaughtExceptionHandler {public static final String TAG = "CrashHandler";//系統(tǒng)默認(rèn)的UncaughtException處理類 private Thread.UncaughtExceptionHandler mDefaultHandler;//CrashHandler實(shí)例private static CrashHandler INSTANCE = new CrashHandler();//程序的Context對(duì)象private Context mContext;//用來(lái)存儲(chǔ)設(shè)備信息和異常信息private Map<String, String> infos = new HashMap<String, String>();//用于格式化日期,作為日志文件名的一部分private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");/** 保證只有一個(gè)CrashHandler實(shí)例 */private CrashHandler() {}/** 獲取CrashHandler實(shí)例 ,單例模式 */public static CrashHandler getInstance() {return INSTANCE;}/*** 初始化* * @param context*/public void init(Context context) {mContext = context;//獲取系統(tǒng)默認(rèn)的UncaughtException處理器mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();//設(shè)置該CrashHandler為程序的默認(rèn)處理器Thread.setDefaultUncaughtExceptionHandler(this);}/*** 當(dāng)UncaughtException發(fā)生時(shí)會(huì)轉(zhuǎn)入該函數(shù)來(lái)處理*/@Overridepublic void uncaughtException(Thread thread, Throwable ex) {if (!handleException(ex) && mDefaultHandler != null) {//如果用戶沒有處理則讓系統(tǒng)默認(rèn)的異常處理器來(lái)處理 mDefaultHandler.uncaughtException(thread, ex);} else {try {Thread.sleep(3000);} catch (InterruptedException e) {Log.e(TAG, "error : ", e);}//退出程序 android.os.Process.killProcess(android.os.Process.myPid());System.exit(1);}}/*** 自定義錯(cuò)誤處理,收集錯(cuò)誤信息 發(fā)送錯(cuò)誤報(bào)告等操作均在此完成.* * @param ex* @return true:如果處理了該異常信息;否則返回false.*/private boolean handleException(Throwable ex) {if (ex == null) {return false;}//使用Toast來(lái)顯示異常信息new Thread() {@Overridepublic void run() {Looper.prepare();Toast.makeText(mContext, "很抱歉,程序出現(xiàn)異常,即將退出.", Toast.LENGTH_LONG).show();Looper.loop();}}.start();//收集設(shè)備參數(shù)信息 collectDeviceInfo(mContext);//保存日志文件 saveCrashInfo2File(ex);return true;}/*** 收集設(shè)備參數(shù)信息* @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) {Log.e(TAG, "an error occured when collect package info", e);}Field[] fields = Build.class.getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);infos.put(field.getName(), field.get(null).toString());Log.d(TAG, field.getName() + " : " + field.get(null));} catch (Exception e) {Log.e(TAG, "an error occured when collect crash info", e);}}}/*** 保存錯(cuò)誤信息到文件中* * @param ex* @return 返回文件名稱,便于將文件傳送到服務(wù)器*/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) {cause.printStackTrace(printWriter);cause = cause.getCause();}printWriter.close();String result = writer.toString();sb.append(result);try {long timestamp = System.currentTimeMillis();String time = formatter.format(new Date());String fileName = "crash-" + time + "-" + timestamp + ".log";if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {String path = "/sdcard/crash/";File dir = new File(path);if (!dir.exists()) {dir.mkdirs();}FileOutputStream fos = new FileOutputStream(path + fileName);fos.write(sb.toString().getBytes());fos.close();}return fileName;} catch (Exception e) {Log.e(TAG, "an error occured while writing file...", e);}return null;} }

在收集異常信息時(shí),朋友們也可以使用Properties,因?yàn)镻roperties有一個(gè)很便捷的方法properties.store(OutputStream out, String comments),用來(lái)將Properties實(shí)例中的鍵值對(duì)外輸?shù)捷敵隽髦?#xff0c;但是在使用的過程中發(fā)現(xiàn)生成的文件中異常信息打印在同一行,看起來(lái)極為費(fèi)勁,所以換成Map來(lái)存放這些信息,然后生成文件時(shí)稍加了些操作。

完成這個(gè)CrashHandler后,我們需要在一個(gè)Application環(huán)境中讓其運(yùn)行,為此,我們繼承android.app.Application,添加自己的代碼,CrashApplication.java代碼如下:

package com.scott.crash;import android.app.Application;public class CrashApplication extends Application {@Overridepublic void onCreate() {super.onCreate();CrashHandler crashHandler = CrashHandler.getInstance();crashHandler.init(getApplicationContext());} }

最后,為了讓我們的CrashApplication取代android.app.Application的地位,在我們的代碼中生效,我們需要修改AndroidManifest.xml:

<application android:name=".CrashApplication" ...> </application>

因?yàn)槲覀兩厦娴腃rashHandler中,遇到異常后要保存設(shè)備參數(shù)和具體異常信息到SDCARD,所以我們需要在AndroidManifest.xml中加入讀寫SDCARD權(quán)限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

搞定了上邊的步驟之后,我們來(lái)運(yùn)行一下這個(gè)項(xiàng)目:

看以看到,并不會(huì)有強(qiáng)制關(guān)閉的對(duì)話框出現(xiàn)了,取而代之的是我們比較有好的提示信息。

然后看一下SDCARD生成的文件:


用文本編輯器打開日志文件,看一段日志信息:

CPU_ABI=armeabi CPU_ABI2=unknown ID=FRF91 MANUFACTURER=unknown BRAND=generic TYPE=eng ...... Caused by: java.lang.NullPointerExceptionat com.scott.crash.MainActivity.onCreate(MainActivity.java:13)at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)... 11 more

這些信息對(duì)于開發(fā)者來(lái)說(shuō)幫助極大,所以我們需要將此日志文件上傳到服務(wù)器,有關(guān)文件上傳的技術(shù),請(qǐng)參照Android中使用HTTP服務(wù)相關(guān)介紹。

不過在使用HTTP服務(wù)之前,需要確定網(wǎng)絡(luò)暢通,我們可以使用下面的方式判斷網(wǎng)絡(luò)是否可用:

/*** 網(wǎng)絡(luò)是否可用* * @param context* @return*/public static boolean isNetworkAvailable(Context context) {ConnectivityManager mgr = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);NetworkInfo[] info = mgr.getAllNetworkInfo();if (info != null) {for (int i = 0; i < info.length; i++) {if (info[i].getState() == NetworkInfo.State.CONNECTED) {return true;}}}return false;}


轉(zhuǎn)自:http://blog.csdn.net/liuhe688/article/details/6584143

轉(zhuǎn)載于:https://www.cnblogs.com/welcoming/p/4168691.html

總結(jié)

以上是生活随笔為你收集整理的Android中处理崩溃异常 (转)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。