zoukankan      html  css  js  c++  java
  • Android怎样捕获应用的crash信息

    转载请注明出处:http://blog.csdn.net/fishle123/article/details/50823358

    我们的应用不可避免的会发生crash,假设是在调试阶段,我们能够使用Logcat查看异常信息。可是假设应用公布之后呢?假设在用户那边crash了,假设我们能够捕获这些crash信息,那么对我们定位crash原因并修复问题是非常有帮助的。

    应用crash就可以能是Java层的异常导致的,也可能是native层导致,以下分别来看一下该怎样处理。

    1 Java层的未捕获异常处理

    先来看一下Java层的crash信息收集吧。要想捕获Java层的crash信息并不难。Android已经提供了接口来帮助我们监控系统的未捕获的异常:使用Thread.setDefaultUncaughtExceptionHandler就能够让我们轻松的监控应用的不论什么意外crash。

    首先来看一下Thread.setDefaultUncaughtExceptionHandler这种方法:

    /**
     * Sets the default uncaught exception handler. This handler is invoked in
     * case any Thread dies due to an unhandled exception.
     *
     * @param handler
     *            The handler to set or null.
     */
    public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler handler) {
        Thread.defaultUncaughtHandler = handler;
    }

    从Thread.setDefaultUncaughtExceptionHandler这种方法的凝视就能够看到:当进程内(由于Thread.defaultUncaughtHandler 是一个静态变量,因此对整个进程内的全部线程有效)的不论什么线程发生未捕获异常时。会调用这里设置的handler。那我们看一下UncaughtExceptionHandler 这个类吧:

    /**
     * Implemented by objects that want to handle cases where a thread is being
     * terminated by an uncaught exception. Upon such termination, the handler
     * is notified of the terminating thread and causal exception. If there is
     * no explicit handler set then the thread's group is the default handler.
     */
    public static interface UncaughtExceptionHandler {
        /**
         * The thread is being terminated by an uncaught exception. Further
         * exceptions thrown in this method are prevent the remainder of the
         * method from executing, but are otherwise ignored.
         *
         * @param thread the thread that has an uncaught exception
         * @param ex the exception that was thrown
         */
        void uncaughtException(Thread thread, Throwable ex);
    }

    从源代码能够看出。UncaughtExceptionHandler 事实上是一个接口,它仅仅定义了一个方法uncaughtException(Thread thread, Throwable ex),当线程由于遇到未捕获异常而终止的时候就会调用这种方法。

    假设我们想要捕获应用的crash信息,那么定义一个自己的UncaughtExceptionHandler 就能够,当然我们须要在自己的UncaughtExceptionHandler 里面把crash信息保存起来,必要的时候还能够上传到我们的server,这样就能够非常方便的收集用户的crash信息。

    2 native层的异常处理

    假设我们的应用使用到c/c++,那么也须要收集native层的异常处理。

    大家都知道。Android的底层是基于Linux的,那么native层的未捕获异常就能够通过捕获信号来处理了。Native层假设异常终止会发出SIGKILL信号。我们能够使用sigaaction来注冊一个信号处理函数来处理SIGKILL信号,这样就能够收集到native层的未捕获异常了。

    这里给出一个大概的代码框架:

    void sigkill_handler(int signo){
       //打印堆栈,并写入到文件里
    }
    void install(){
        struct sigaction act, oldact;
        act.sa_handler = sigkill_handler;
        sigaddset(&act.sa_mask, SIGKILL);
    
        sigaction(SIGKILL, &act, &oldact);//注冊信号处理函数
        ......
    }

    3 实现

    结合上面的介绍。以下就来定义一个自己的UncaughtExceptionHandler 。这个样例仅仅处理了Java层的crash收集,并把收集到的crash信息保存到sd卡上。这里给我们自己定义的crash处理器起了一个名字叫做AppCR(Application Crash Response)。

    首先定义ErrorReporter ,它实现了UncaughtExceptionHandler :

    public class ErrorReporter implements UncaughtExceptionHandler {
    
        private final Application mContext;
        private final ReporterExecutor mReporterExecutor;
    
        ErrorReporter(Application context, boolean enabled) {
            mContext = context;
    
            final Thread.UncaughtExceptionHandler defaultExceptionHandler = Thread
                    .getDefaultUncaughtExceptionHandler();
            mReporterExecutor = new ReporterExecutor(context, defaultExceptionHandler);
            mReporterExecutor.setEnabled(enabled);
            Thread.setDefaultUncaughtExceptionHandler(this);
        }
    
        @Override
        public void uncaughtException(final Thread thread,final Throwable ex) {
            // TODO Auto-generated method stub
            LogUtil.i(AppCR.LOG_TAG,"catch uncaughtException");
    
            mReporterExecutor.execute(thread, ex);
        }
    
        public void setEnabled(boolean enabled) {
            LogUtil.i(AppCR.LOG_TAG, "AppCR is" + (enabled ?

    "enabled" : "disabled") + " for " + mContext.getPackageName()); } }

    ReporterExecutor会调用Thread.setDefaultUncaughtExceptionHandler(this);来改动默认的UncaughtExceptionHandler。当发生未捕获的异常时。调用mReporterExecutor.execute(thread, ex);来处理异常。

    ReporterExecutor 中把异常信息以及操作系统的相关信息保存到文件里。

    public class ReporterExecutor {
    
        public static final String TAG = ReporterExecutor.class.getSimpleName();
        private Context mContext;
        private boolean mEnabled = false;
        private final Thread.UncaughtExceptionHandler mDefaultExceptionHandler;
        private File mCrashInfoFile;
    
        public ReporterExecutor(Context context,
                                Thread.UncaughtExceptionHandler defaultedExceptionHandler) {
    
            mContext = context;
            mDefaultExceptionHandler = defaultedExceptionHandler;
    
            if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
                File path = Environment.getExternalStorageDirectory();
                File dir = new File(path, "BleFairy");
                if (!dir.exists()) {
                    dir.mkdirs();
                }
    
                mCrashInfoFile = new File(dir, getCrashFileName());
                if (!mCrashInfoFile.exists()) {
                    try {
                        mCrashInfoFile.createNewFile();
                    } catch (IOException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }
    
        public boolean isEnabled() {
            return mEnabled;
        }
    
        public void setEnabled(boolean enabled) {
            mEnabled = enabled;
        }
    
        public void execute(Thread thread, Throwable ex) {
    
            if (!mEnabled) {
                endApplication(thread, ex);
                return;
            }
    
            // log crash info to file
            Log.w(AppCR.LOG_TAG, "getSysInfo.");
            CrashReportData data = CrashReportData.produce(thread, ex, mContext);
            data.writeToFile(mCrashInfoFile);
            endApplication(thread, ex);
           
        }
    
        private void endApplication(Thread thread, Throwable ex) {
    
            if (mDefaultExceptionHandler != null) {
                Log.w(AppCR.LOG_TAG, "execute default uncaughtException handler.");
                mDefaultExceptionHandler.uncaughtException(thread, ex);
            } else {
                Log.w(AppCR.LOG_TAG, "kill process and exit.");
                android.os.Process.killProcess(android.os.Process.myPid());
                System.exit(10);
            }
        }
    
        private String getCrashFileName() {
            StringBuilder ret = new StringBuilder();
            Calendar calendar = Calendar.getInstance();
    
            ret.append("crash_");
            ret.append(calendar.get(Calendar.YEAR));
            int month = calendar.get(Calendar.MONTH)+1;
            int date = calendar.get(Calendar.DATE);
            if(month < 10 ){
                ret.append("0");
            }
            ret.append(month);
            if(date<10){
                ret.append("0");
            }
            ret.append(date);
            ret.append(".txt");
            return ret.toString();
        }
    }

    CrashReportData 类用于保存异常信息:

    public class CrashReportData {
    
        private final String info;
    
        private CrashReportData(String crashInfo) {
            this.info = crashInfo;
        }
    
        public static CrashReportData produce(Thread thread, Throwable ex, Context context) {
    
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            PrintStream print = new PrintStream(out);
            out.toString();
    
            print.append("crahtime:" + TimeUtil.getCurTimeString()).append("
    ");
            print.append(SysInfo.getSysInfo(context)).append("
    ");
            print.append(thread.getName()).append("(threadID=" + thread.getId() + ")").append("
    ");
            print.append(ex.getMessage()).append("
    ");
            ex.printStackTrace(print);
    
            return new CrashReportData(out.toString());
        }
    
        public void writeToFile(File file) {
            PrintWriter printer = null;
            try {
    
                // append to the end of crash file
                BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(file, true));
                printer = new PrintWriter(out);
                printer.println(info);
                printer.flush();
    
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
    
            } finally {
    
                if (printer != null) {
                    printer.close();
                }
                LogUtil.w(AppCR.LOG_TAG, "write exception info to file over.");
    
            }
        }
    
        @Override
        public String toString() {
            // TODO Auto-generated method stub
            return info;
            // return super.toString();
        }
    
    }

     SysIno类:

    public class SysInfo {
    
        public static String getSysInfo(Context context) {
            StringBuilder info = new StringBuilder();
            info.append("osVersion=Android ").append(Build.VERSION.RELEASE).append("
    ");
            info.append("model=").append(Build.MODEL).append("
    ");
            info.append("brand=").append(Build.BRAND).append("
    ");
    
            LogUtil.i(AppCR.LOG_TAG, "sys info collect over.");
            return info.toString();
        }
    
    }

    使用AppCR来安装我们的crash处理器:

    public class AppCR {
        public static final String LOG_TAG=AppCR.class.getSimpleName();
        private static ErrorReporter mErrorReporter;
    
        public static void init(Application application){
            init(application,true);
        }
    
        public static void init(Application application,boolean enabled){
            mErrorReporter = new ErrorReporter(application, enabled);
        }
    }

    Application中安装上面自己定义的AppCR就能够了:

    public class BeaconApplication extends Application {
    
        private final String TAG = "BeaconFairy.BeaconApplication";
        
    
        @Override
        public void onCreate() {
            super.onCreate();
            AppCR.init(this,true);
        }
    
    }

    须要注意的是:我们须要定义自己的Application,然后改动manifest就能够啦,还要记得加上写SD卡的权限:

    <application
            android:name=".BeaconApplication"
            android:allowBackup="true"
            android:allowTaskReparenting="true"
            android:icon="@drawable/ic_launcher"
            android:label="@string/app_name"
            android:theme="@style/AppTheme" >
             ........
    </application>
    申请写SD卡的权限:

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

    到此为止,我们自己定义的crash信息收集程序AppCR就完毕了。

  • 相关阅读:
    编码问题
    Linux环境给文件重命名
    FIFO简记
    图像去模糊
    matlab直方图均衡,使用向量优化
    研究方向
    FPGA学习笔记之格雷码、边沿检测、门控时钟
    [转]关于凸优化的一些简单概念
    SSD果然劲爆!
    Qunie问题
  • 原文地址:https://www.cnblogs.com/zhchoutai/p/7181334.html
Copyright © 2011-2022 走看看