Android應用如何反饋Crash報告


一、為什么要Crash

crash可以理解成墮落,垮台。按照我們通俗理解就是android App 因為不可預知的因素導致奔潰。

即使我們的程序發布前,經歷了很多的測試,但是經過無數用戶各種使用情況之后,可能會發生意想不到的crash.

為了及時反饋bug,通常我們都需要一個crash機制,以讓開發人員盡快了解到問題所在,在下個版本中及時改進。

二、如何做到Crash

java的Thread中有一個UncaughtExceptionHandler接口,該接口的作用主要是為了  當 Thread 因未捕獲的異常而突然終止時,調用處理程序。

接口下面有setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)

方法,方法主要作用為設置當線程由於未捕獲到異常而突然終止,並且沒有為該線程定義其他處理程序時所調用的默認處理程序。

通用demo如下

 1 public class DefaultExceptionHandler implements UncaughtExceptionHandler {  
 2   
 3    
 4   
 5     private Context act = null;  
 6   
 7    
 8   
 9     public DefaultExceptionHandler(Context act) {  
10   
11        this.act = act;  
12   
13     }  
14   
15    
16   
17     @Override  
18   
19     public void uncaughtException(Thread thread, Throwable ex) {  
20   
21    
22   
23        // 收集異常信息 並且發送到服務器  
24   
25        sendCrashReport(ex);  
26   
27    
28   
29        // 等待半秒  
30   
31        try {  
32   
33            Thread.sleep(500);  
34   
35        } catch (InterruptedException e) {  
36   
37            //  
38   
39        }  
40   
41          
42   
43        // 處理異常  
44   
45        handleException();  
46   
47    
48   
49     }  
50   
51    
52   
53     private void sendCrashReport(Throwable ex) {  
54   
55    
56   
57        StringBuffer exceptionStr = new StringBuffer();  
58   
59        exceptionStr.append(ex.getMessage());  
60   
61    
62   
63        StackTraceElement[] elements = ex.getStackTrace();  
64   
65        for (int i = 0; i < elements.length; i++) {  
66   
67            exceptionStr.append(elements[i].toString());  
68   
69        }  
70   
71    
72   
73        //TODO   
74   
75        //發送收集到的Crash信息到服務器  
76   
77     }  
78   
79    
80   
81     private void handleException() {  
82   
83        //TODO   
84   
85        //這里可以對異常進行處理。  
86   
87        //比如提示用戶程序崩潰了。  
88   
89        //比如記錄重要的信息,嘗試恢復現場。  
90   
91        //或者干脆記錄重要的信息后,直接殺死程序。  
92   
93     }  
94   
95    
96   
97 }  
View Code

在主線程中調用

Thread.setDefaultUncaughtExceptionHandler(new DefaultExceptionHandler(  
  
       this.getApplicationContext()));  

之前一直對公司項目的CrashHandler類不是很熟悉,這里結合項目代碼,看下是如何具體實現的

首先,在AndroidManifest.xml中的application節點中配置name 

<application 
        android:name="com.newland.mbop.application.CrashHandlerApp">

 

CrashHandlerApp中初始化CrashHandler(實現UncaughtExceptionHandler的實現類)
    @Override
    public void onCreate() {
        CrashHandler ch = CrashHandler.getInstance();
        ch.init(this);

        super.onCreate();
    }

 最后看下CrashHandler類的具體實現

public class CrashHandler implements UncaughtExceptionHandler {

/** 獲取CrashHandler實例 */
    public static CrashHandler getInstance() {
        if (INSTANCE == null)
            INSTANCE = new CrashHandler();
        return INSTANCE;
    }
    public void init(CrashHandlerApp app) {
        Log.i("BaseActivity","init()");
        this.app = app;
        // 設置該類為線程默認UncatchException的處理器。
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 當UncaughtException發生時會回調該函數來處理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        System.out.println("system wrong....");
        // MBOPApplication app=(MBOPApplication) mainContext;
        // app.setNeed2Exit(true);
        //異常信息收集
        collectCrashExceptionInfo(thread, ex);
        //應用程序信息收集
        collectCrashApplicationInfo(app);
        //保存錯誤報告文件到文件。
        saveCrashInfoToFile(ex);
        //MBOPApplication.setCrash(true);
        //判斷是否為UI線程異常,thread.getId()==1 為UI線程
        if (thread.getId() != 1) {
//            System.out.println("Exception ThreadId" + thread.getId());
            thread.interrupt();
            //TODO 跳轉到IndexActivity
            System.out.println("Thread ID--->" + Thread.currentThread().getId());
//            Intent intent =new Intent(mainContext,IndexActivity.class);
//            actContext.startActivity(intent);
            //彈出對話框提示用戶是否上傳異常日志至服務器
            new Thread(){
                public void run() {}{                                        
                    Looper.prepare();
                    new AlertDialog.Builder(app.getCurrentAct()).setTitle("異常處理").setMessage("您的程序出現異常,是否將異常信息上傳至服務器?")
                    .setPositiveButton("是", new OnClickListener() {
                        
                        public void onClick(DialogInterface dialog, int which) {
                            new Thread(new Runnable() {
                                
                                @Override
                                public void run() {
                                    sendCrashReportsToServer(app,false);
                                    
                                }
                            }).start();
//                            new Thread(){
//                                public void run() {}{    
//                                    try{
//                                        System.out.println("執行上傳線程ID"+this.getId());
//                                        this.sleep(5000);
//                                    }catch(Exception e){
//                                        
//                                    }
//                                    sendCrashReportsToServer(app);
//                                }
//                            }.start();                                    
                        }
                    }).setNegativeButton("否", new OnClickListener() {
                        
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            
                        }
                    }).create().show();
                    Looper.loop();                                 
                }
            }.start();
            
        } else {

//            UserSessionCache usc=UserSessionCache.getInstance();
//            ObjectOutputStream oos=null;
//            try {
//                oos.writeObject(usc);
//            } catch (IOException e) {
//                e.printStackTrace();
//            }
//            SharedPreferences prefenPreferences = mainContext
//            .getSharedPreferences("IsMBOPCrash",Activity.MODE_PRIVATE);
//            SharedPreferences.Editor editor = prefenPreferences.edit();
//            editor.clear();
//            editor.putBoolean("ISCRASH", true);
//            editor.commit();
            
            // 方案一:將所有Activity放入Activity列表中,然后循環從列表中刪除,即可退出程序

            for (int i = app.getActivityList().size()-1; i >=0; i--) {
                Activity act = app.getActivityList().get(i);
                act.finish();
            }
            CoreCommonMethod.setCrash(app, true);
            Intent intent = new Intent(app, WelcomeActivity.class);
            intent.putExtra(WelcomeActivity.EXTRA_DIRECT_TO_INDEX, true);
            intent.putExtra(WelcomeActivity.EXTRA_USERINFO, UserSessionCache.getInstance().getUserInfo());
            intent.putExtra(WelcomeActivity.EXTRA_CURRENT_PORTAL_ID, UserSessionCache.getInstance().getCurrentPortalId());
//            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//            app.startActivity(intent);                     
            android.os.Process.killProcess(android.os.Process.myPid());  
            
            //方案二:直接使用ActivityManager的restartPackage方法關閉應用程序,
            //此方法在android2.1之后被棄用,不起作用
//            ActivityManager am = (ActivityManager) mainContext.getSystemService(Context.ACTIVITY_SERVICE);
//            am.restartPackage(mainContext.getPackageName());

        }
    }



}

一般來說,發生crash的時候,我們需要知道客戶端的SDK版本,程序版本,分辨率等等因素

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM