zoukankan      html  css  js  c++  java
  • Android 工具类 异常处理类CrashHandler

    1.整体分析

    1.1.源代码如下,可以直接Copy。  

    public class CrashHandler implements Thread.UncaughtExceptionHandler {
        private static final String TAG = "异常处理";
        private static final boolean DEBUG = true;
    
        private static final String PATH = Environment.getExternalStorageDirectory().getPath() +
                "/test/log/";
        private static final String FILE_NAME = "crash";
    
        //log文件的后缀名
        private static final String FILE_NAME_SUFFIX = ".txt";
    
        private static CrashHandler sInstance = new CrashHandler();
    
        //系统默认的异常处理(默认情况下,系统会终止当前的异常程序)
        private Thread.UncaughtExceptionHandler mDefaultCrashHandler;
    
        private Context mContext;
    
        //构造方法私有,防止外部构造多个实例,即采用单例模式
        private CrashHandler() {
        }
    
        public static CrashHandler getInstance() {
            return sInstance;
        }
    
        //这里主要完成初始化工作
        public void init(Context context) {
            //获取系统默认的异常处理器
            mDefaultCrashHandler = Thread.getDefaultUncaughtExceptionHandler();
            //将当前实例设为系统默认的异常处理器
            Thread.setDefaultUncaughtExceptionHandler(this);
            //获取Context,方便内部使用
            mContext = context.getApplicationContext();
    
        }
    
        /**
         * 这个是最关键的函数,当程序中有未被捕获的异常,系统将会自动调用#uncaughtException方法
         * thread为出现未捕获异常的线程,ex为未捕获的异常,有了这个ex,我们就可以得到异常信息。
         */
        @Override
        public void uncaughtException(Thread thread, Throwable ex) {
            try {
                //导出异常信息到SD卡中
                dumpExceptionToSDCard(ex);
                //这里可以通过网络上传异常信息到服务器,便于开发人员分析日志从而解决bug
                uploadExceptionToServer();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            //打印出当前调用栈信息
            ex.printStackTrace();
    
            //如果系统提供了默认的异常处理器,则交给系统去结束我们的程序,否则就由我们自己结束自己
            if (mDefaultCrashHandler != null) {
                mDefaultCrashHandler.uncaughtException(thread, ex);
            } else {
                Process.killProcess(Process.myPid());
            }
    
        }
    
        private void dumpExceptionToSDCard(Throwable ex) throws IOException {
            //如果SD卡不存在或无法使用,则无法把异常信息写入SD卡
            if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
                if (DEBUG) {
                    Log.w(TAG, "sdcard unmounted,skip dump exception");
                    return;
                }
            }
    
            File dir = new File(PATH);
            if (!dir.exists()) {
                dir.mkdirs();
            }
            long current = System.currentTimeMillis();
            String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(current));
            //以当前时间创建log文件
            File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);
            if (!file.exists()) {
                Log.i(TAG, "文件名称为:"+file.getName());
            }else{
                Log.i(TAG, "文件名称为:"+file.getName());
            }
    
            try {
                if(file.createNewFile()){
                    Log.i(TAG, "文件创建成功:名称为:"+file.getName());
                }
                if(file.exists()){
                    Log.i(TAG, "文件exists");
                }
                PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
                //导出发生异常的时间
                pw.println(time);
    
                //导出手机信息
                dumpPhoneInfo(pw);
    
                pw.println();
                //导出异常的调用栈信息
                ex.printStackTrace(pw);
    
                pw.close();
            } catch (Exception e) {
                Log.e(TAG, "dump crash info failed");
            }
        }
    
        private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
            //应用的版本名称和版本号
            PackageManager pm = mContext.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager
                    .GET_ACTIVITIES);
            pw.print("App Version: ");
            pw.print(pi.versionName);
            pw.print('_');
            pw.println(pi.versionCode);
    
            //android版本号
            pw.print("OS Version: ");
            pw.print(Build.VERSION.RELEASE);
            pw.print("_");
            pw.println(Build.VERSION.SDK_INT);
    
            //手机制造商
            pw.print("Vendor: ");
            pw.println(Build.MANUFACTURER);
    
            //手机型号
            pw.print("Model: ");
            pw.println(Build.MODEL);
    
            //cpu架构
            pw.print("CPU ABI: ");
            pw.println(Build.CPU_ABI);
        }
    
        private void uploadExceptionToServer() {
            //TODO Upload Exception Message To Your Web Server
        }
    
    }
    View Code

    1.2.实现的方法如下

    • 继承了Thread.UncaughtExceptionHandler,所以实现override方法uncaughtException
    • 将异常信息写入SD卡,实现dumpExceptionToSDCard
    • 打印应用的版本名称和版本号和手机制造商、手机型号及cpu架构
    • 上传到服务器,这个函数要看情况需不需要 

    1.3.需要动态申请写文件的权限。

      参考文章:http://blog.csdn.net/wuqingyou_w/article/details/60138807

    private static final int REQUEST_EXTERNAL_STORAGE = 1;
        private static String[] PERMISSIONS_STORAGE = {
                "android.permission.READ_EXTERNAL_STORAGE",
                "android.permission.WRITE_EXTERNAL_STORAGE" };
    
    
        public static void verifyStoragePermissions(Activity activity) {
    
            try {
                //检测是否有写的权限
                int permission = ActivityCompat.checkSelfPermission(activity,
                        "android.permission.WRITE_EXTERNAL_STORAGE");
                if (permission != PackageManager.PERMISSION_GRANTED) {
                    // 没有写的权限,去申请写的权限,会弹出对话框
                    ActivityCompat.requestPermissions(activity, PERMISSIONS_STORAGE,REQUEST_EXTERNAL_STORAGE);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

      将这个方法放在onCreate或者其他必然执行的地方即可。


    2.局部分析

    2.1.首先是成员变量

      

      TAG用来调试时输出的。

      DEBUG是为了判断当前项目处于开发模式还是调试模式。

      PATH是日志文件存储路径,前面不能变,后面随意。

      注意点:我的手机一开始我找不到这个文件。

      因为我在Logcat上输出了这个Environment.getExternalStorageDirectory().getPath为

        /storage/emulated/0(不同手机可能不同)

      然后我在手机上真的找到了这个路径,但是这个目录下始终没有日志文件

      最后我才知道这个Environment.getExternalStorageDirectory().getPath就是手机的主目录

      在主目录下就有新建的文件夹以及目录了。

      FILE_NAME是日志文件的前缀

      FILE_NAME_SUFFIX是日志文件的后缀

      sInstance是新建的CrashHandler的一个实例

      mDefaultCrashHandler是系统默认的异常处理,系统会终止当前的异常程序

      mContext是上下文

    2.2.构造方法

      

    2.3.获取单例

      

    2.4.完成初始化工作

      

      还是要获取系统默认的异常处理器

      然后将当前实例设为系统默认的异常处理器,这样每次都会先执行这里了

      获取一下上下文,方便内部使用

    2.5.这里是实现的接口方法uncaughtException

      

      可以看到,这里先将信息导出到SD卡中,然后上传异常信息到服务器(这里需要看情况写了)

      然后交给系统默认的异常处理器去处理后事。没有默认的,就自己结束自己吧。

    2.6.如何写到SD卡中呢?

      

      

      首先获取SD卡默认路径

      先利用file.mkdirs()方法来创建一系列的路径,mkdir()只能创建当前目录的文件夹,mkdirs()可以无限了。

      然后利用系统当前时间可以创建唯一的名称

      然后利用createNewFile()方法,可以创建一个文件了

      之后利用PrintWriter来向文件中写入数据了。

      PrintWriter pw=new PrintWriter(new BufferedWriter(new FileWriter(file)))这种方法来创建流

      然后可以用pw.println()向文件中写入数据了。

      最后要将pw关闭。

    2.7.如何向文件中写入手机信息?

      

      这里利用上下文获取包管理器,然后有包管理器获取包信息,然后就可以获取版本号及名称。

      手机制造商和手机型号和cup架构需要Build类获取。

    2.8.上传到服务器需要自己看情况写

      


    3.使用方法及效果

    3.1.首先在项目的Application中初始化

      在onCreate函数中添加==>

      CrashHandler.getInstance().init(this);

    3.2.然后只要发生异常,都会将异常信息发送到日志文件

          

            


    既然选择了,便不顾风雨兼程。Just follow yourself.
  • 相关阅读:
    音频、摄像机操作
    调用系统相机及摄像机
    图片的放大缩小
    haxm intelx86加速模拟器的安装
    mac eclipse 下安装subclipse
    文件多线程下载实现
    windows与linux之间传输文件
    ZeroMQ接口函数之 :zmq_setsockopt –设置ZMQ socket的属性
    使用C语言在windows下一口气打开一批网页
    Net-SNMP是线程安全的吗
  • 原文地址:https://www.cnblogs.com/Jason-Jan/p/7904551.html
Copyright © 2011-2022 走看看