一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Android中處理崩潰閃退錯誤

 WindySky 2016-03-01

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

 

  我們需要的是軟件有一個全局的異常捕獲器,當(dāng)出現(xiàn)一個我們沒有發(fā)現(xiàn)的異常時,捕獲這個異常,并且將異常信息記錄下來,上傳到服務(wù)器公開發(fā)這分析出現(xiàn)異常的具體原因。不過首先我們還是來了解以下兩個類:android.app.Application和java.lang.Thread.UncaughtExceptionHandler。

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

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

AppException.java實現(xiàn)了Thread.UncaughtExceptionHandler,使我們用來處理未捕獲異常的主要成員,代碼如下:

package cn.com.ista.pdachina.app;

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;  
  
/** 
 * AppException處理類,當(dāng)程序發(fā)生Uncaught異常的時候,有該類來接管程序,并記錄發(fā)送錯誤報告. 
 *  
 * @author user 
 *  
 */  
public class AppException implements UncaughtExceptionHandler {  
      
    public static final String TAG = "AppException";  
      
    //系統(tǒng)默認(rèn)的UncaughtException處理類   
    private Thread.UncaughtExceptionHandler mDefaultHandler;  
    //CrashHandler實例  
    private static AppException INSTANCE = new AppException();  
    //程序的Context對象  
    private Context mContext;  
    //用來存儲設(shè)備信息和異常信息  
    private Map<String, String> infos = new HashMap<String, String>();  
  
    //用于格式化日期,作為日志文件名的一部分  
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");  
  
    /** 保證只有一個AppEeception實例 */  
    private AppException() {  
    }  
  
    /** 獲取AppException實例 ,單例模式 */  
    public static AppException 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ā)生時會轉(zhuǎn)入該函數(shù)來處理 
     */  
    @Override  
    public void uncaughtException(Thread thread, Throwable ex) {  
        if (!handleException(ex) && mDefaultHandler != null) {  
            //如果用戶沒有處理則讓系統(tǒng)默認(rèn)的異常處理器來處理  
            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);  
        }  
    }  
  
    /** 
     * 自定義錯誤處理,收集錯誤信息 發(fā)送錯誤報告等操作均在此完成. 
     *  
     * @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, "很抱歉,程序出現(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);  
            }  
        }  
    }  
  
    /** 
     * 保存錯誤信息到文件中 
     *  
     * @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 = "log-" + time + "-" + timestamp + ".log";  
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {  
                String path = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/PdaChina/";  
                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;  
    }  
}  

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

package cn.com.ista.pdachina.app;

import android.app.Application;
import android.content.Context;
/**
 * 全局獲取上下文類:用于保存和調(diào)用全局應(yīng)用配置及訪問網(wǎng)絡(luò)數(shù)據(jù)
 * @author guopeng
 * @version 1.0
 * @created 2015-10-26
 */
public class AppContext extends Application {

    private static Context instance;

    @Override
    public void onCreate() 
    {
        instance = getApplicationContext();
        
        AppException appException = AppException.getInstance();
        appException.init(instance);
    }
    
    public static Context getContext()
    {
        return instance;
    }
    
}

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

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

因為我們上面的AppException中,遇到異常后要保存設(shè)備參數(shù)和具體異常信息到SDCARD,所以我們需要在AndroidManifest.xml中加入讀寫SDCARD權(quán)限:

<!-- SD卡讀寫權(quán)限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 

大功告成

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    精品一区二区三区中文字幕| 人人爽夜夜爽夜夜爽精品视频| 欧美一区二区三区五月婷婷| 成人免费在线视频大香蕉| 日本av在线不卡一区| 欧美日韩中国性生活视频| 99久久精品免费精品国产| 丰满熟女少妇一区二区三区 | 日本一二三区不卡免费| 欧洲偷拍视频中文字幕| 91免费一区二区三区| 国产欧美一区二区久久| 两性色午夜天堂免费视频| 免费高清欧美一区二区视频| 久久国产精品热爱视频| 日韩人妻中文字幕精品| 亚洲在线观看福利视频| 人妻内射在线二区一区| 欧美精品久久一二三区| 亚洲一级二级三级精品| 91亚洲国产—区=区a| 国产av天堂一区二区三区粉嫩| 日本欧美一区二区三区在线播| 免费啪视频免费欧美亚洲| 国产成人亚洲综合色就色| 绝望的校花花间淫事2| 日系韩系还是欧美久久| 日本高清不卡在线一区| 国产免费一区二区三区av大片| 亚洲综合激情另类专区老铁性| 国产免费一区二区三区av大片| 欧美日韩乱一区二区三区| 91精品日本在线视频| 午夜福利92在线观看| 一级欧美一级欧美在线播| 成人国产激情福利久久| 亚洲少妇一区二区三区懂色| 国产乱久久亚洲国产精品| 十八禁日本一区二区三区| 欧美日韩国产另类一区二区| 免费一区二区三区少妇|