智慧 + 毅力 = 无所不能

正确性、健壮性、可靠性、效率、易用性、可读性、可复用性、兼容性、可移植性...

导航

Android应用如何反馈Crash报告

Posted on 2015-03-02 17:13  Bill Yuan  阅读(2119)  评论(0编辑  收藏  举报

转自:http://www.cnblogs.com/draem0507/archive/2013/05/25/3099461.html

一、为什么要Crash

crash可以理解成堕落,垮台。按照我们通俗理解就是android App 因为不可预知的因素导致奔溃。

即使我们的程序发布前,经历了很多的测试,但是经过无数用户各种使用情况之后,可能会发生意想不到的crash.

为了及时反馈bug,通常我们都需要一个crash机制,以让开发人员尽快了解到问题所在,在下个版本中及时改进。

二、如何做到Crash

java的Thread中有一个UncaughtExceptionHandler接口,该接口的作用主要是为了  当 Thread 因未捕获的异常而突然终止时,调用处理程序。

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

方法,方法主要作用为设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。

通用demo如下

public class DefaultExceptionHandler implements UncaughtExceptionHandler {  
    private Context act = null;  

    public DefaultExceptionHandler(Context act) {  
       this.act = act;  
    }  

    @Override  
    public void uncaughtException(Thread thread, Throwable ex) {  
       // 收集异常信息 并且发送到服务器  
       sendCrashReport(ex);  

       // 等待半秒  
       try {  
           Thread.sleep(500);  
       } catch (InterruptedException e) {  
           //  
       }  
  
       // 处理异常  
       handleException();  
    }  

    private void sendCrashReport(Throwable ex) {  
       StringBuffer exceptionStr = new StringBuffer();  
       exceptionStr.append(ex.getMessage());  
       StackTraceElement[] elements = ex.getStackTrace();  
       for (int i = 0; i < elements.length; i++) {  
           exceptionStr.append(elements[i].toString());  
       }  

       //TODO   
       //发送收集到的Crash信息到服务器  
    }  

    private void handleException() {  
       //TODO   
       //这里可以对异常进行处理。  
       //比如提示用户程序崩溃了。  
       //比如记录重要的信息,尝试恢复现场。  
       //或者干脆记录重要的信息后,直接杀死程序。  
    }  
}

在主线程中调用

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版本,程序版本,分辨率等等因素