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.首先是成员变量

  Android 工具类 异常处理类CrashHandler

  TAG用来调试时输出的。

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

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

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

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

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

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

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

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

  FILE_NAME是日志文件的前缀

  FILE_NAME_SUFFIX是日志文件的后缀

  sInstance是新建的CrashHandler的一个实例

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

  mContext是上下文

 

2.2.构造方法

  Android 工具类 异常处理类CrashHandler

 

2.3.获取单例

  Android 工具类 异常处理类CrashHandler

 

2.4.完成初始化工作

  Android 工具类 异常处理类CrashHandler

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

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

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

 

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

  Android 工具类 异常处理类CrashHandler

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

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

 

2.6.如何写到SD卡中呢?

  Android 工具类 异常处理类CrashHandler

  Android 工具类 异常处理类CrashHandler

  首先获取SD卡默认路径

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

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

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

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

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

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

  最后要将pw关闭。

 

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

  Android 工具类 异常处理类CrashHandler

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

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

 

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

  Android 工具类 异常处理类CrashHandler


3.使用方法及效果

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

  在onCreate函数中添加==>

  CrashHandler.getInstance().init(this);

 

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

  Android 工具类 异常处理类CrashHandler  Android 工具类 异常处理类CrashHandler  

      Android 工具类 异常处理类CrashHandler  Android 工具类 异常处理类CrashHandler


相关文章:

  • 2021-11-25
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2022-12-23
  • 2021-10-27
  • 2021-06-06
猜你喜欢
  • 2022-12-23
  • 2022-12-23
  • 2021-08-08
  • 2021-08-12
  • 2022-12-23
  • 2022-12-23
相关资源
相似解决方案