• Android 应用程序崩溃日志捕捉


    程序崩溃是应用迭代中不可避免的问题,即使有着5年或者10年经验的程序猿也无法完全保证自己的代码没有任何的bug导致崩溃,现在有一些第三方平台可以帮助我们搜集应用程序的崩溃,比如友盟,详情如下图

    虽然能够看到崩溃的日志以及机型等,但还是不是很方便,如果需要精确定位的话需要用户提供崩溃的时间点、机型等信息,所以最好的办法就是我们把崩溃的信息保存在用户的sd卡上,必要的时候发送到后台或者让用户手动提供一下文件,下面就来看如何实现这个功能。

    Android 系统提供了处理这类问题的方法,Thread 类中提供了一个方法 setDefaultUncaughtExceptionHandler,设置了这个默认的异常处理器之后当程序发生异常之后就会回调uncaugthException()这个方法,然后可以在这个回调里面捕获异常信息,保存到文件。

    话不多说,直接上代码:

    
    
    package com.hxc.supreme.utils;

    import android.content.Context;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.os.Build;
    import android.os.Environment;
    import android.os.Looper;
    import android.util.Log;
    import android.widget.Toast;


    import com.hxc.supreme.MainApplication;

    import java.io.BufferedWriter;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.FileWriter;
    import java.io.IOException;
    import java.io.PrintWriter;
    import java.io.StringWriter;
    import java.io.Writer;
    import java.lang.reflect.Field;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    import java.util.HashMap;
    import java.util.Map;

    import static com.hxc.supreme.BuildConfig.DEBUG;

    /**
    * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
    * 在Application 中调用
    * CrashHandlerUtil.getInstance().init(this);
    */
    public class XCrashHandlerUtils implements Thread.UncaughtExceptionHandler {
    private static final String TAG = "XCrashHandlerUtils";
    //系统默认的UncaughtException处理类
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    //CrashHandler实例
    private static XCrashHandlerUtils INSTANCE = new XCrashHandlerUtils();
    //程序的Context对象
    private Context mContext;
    //用来存储设备信息和异常信息
    private String crashTip = "似乎遇到了一点小麻烦,程序即将重新启动";
    /**
    * 文件名
    */
    public static final String FILE_NAME = "crash";
    /**
    * 异常日志 存储位置为根目录下的 Crash文件夹
    */
    private static final String PATH = Environment.getExternalStorageDirectory().getPath() +
    "/Supreme_crash/";
    /**
    * 文件名后缀
    */
    private static final String FILE_NAME_SUFFIX = ".txt";

    public String getCrashTip() {
    return crashTip;
    }

    public void setCrashTip(String crashTip) {
    this.crashTip = crashTip;
    }

    /**
    * 保证只有一个CrashHandler实例
    */
    private XCrashHandlerUtils() {
    }

    /**
    * 获取CrashHandler实例 ,单例模式
    *
    * @return 单例
    */
    public static XCrashHandlerUtils getInstance() {
    return INSTANCE;
    }

    /**
    * 初始化
    *
    * @param context 上下文
    */
    public void init(Context context) {
    mContext = context;
    //获取系统默认的UncaughtException处理器
    mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
    //设置该CrashHandler为程序的默认处理器
    Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
    * 这个是最关键的函数,当系统中有未被捕获的异常,系统将会自动调用 uncaughtException 方法
    *
    * @param thread 为出现未捕获异常的线程
    * @param ex 为未捕获的异常 ,可以通过e 拿到异常信息
    */
    @Override
    public void uncaughtException(Thread thread, final Throwable ex) {
    //导入异常信息到SD卡中
    try {
    dumpExceptionToSDCard(ex);
    } catch (IOException e) {
    e.printStackTrace();
    }
    //这里可以上传异常信息到服务器,便于开发人员分析日志从而解决Bug
    // uploadExceptionToServer();
    ex.printStackTrace();
    //如果系统提供了默认的异常处理器,则交给系统去结束程序,否则就由自己结束自己
    if (mDefaultHandler != null) {
    mDefaultHandler.uncaughtException(thread, ex);
    } else {
    android.os.Process.killProcess(android.os.Process.myPid());
    System.exit(1);
    }

    }


    /**
    * 将异常信息写入SD卡
    *
    * @param e
    */
    private void dumpExceptionToSDCard(Throwable e) 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));
    //在定义的Crash文件夹下创建文件
    File file = new File(PATH + FILE_NAME + time + FILE_NAME_SUFFIX);

    try {
    PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
    //写入时间
    pw.println(time);
    //写入手机信息
    dumpPhoneInfo(pw);
    pw.println();//换行
    e.printStackTrace(pw);
    pw.close();//关闭输入流
    } catch (Exception e1) {
    Log.e(TAG, "dump crash info failed");
    }

    }

    /**
    * 获取手机各项信息
    *
    * @param pw
    */
    private void dumpPhoneInfo(PrintWriter pw) throws PackageManager.NameNotFoundException {
    //得到包管理器
    PackageManager pm = mContext.getPackageManager();
    //得到包对象
    PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
    //写入APP版本号
    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: ");
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    pw.println(Build.SUPPORTED_ABIS);
    } else {
    pw.println(Build.CPU_ABI);
    }
    }

    }
    
    

    然后再Mainapplication的onCreate中调用一下XCrashHandlerUtils.init()方法,接下来写一个bug看一下效果,代码很简单

     1 package com.hxc.supreme.activity;
     2 
     3 import android.content.ComponentName;
     4 import android.content.Intent;
     5 import android.content.ServiceConnection;
     6 import android.graphics.Color;
     7 import android.os.Bundle;
     8 import android.os.IBinder;
     9 import android.support.annotation.Nullable;
    10 import android.support.v7.app.AppCompatActivity;
    11 import android.view.Gravity;
    12 import android.view.LayoutInflater;
    13 import android.view.View;
    14 import android.view.ViewGroup;
    15 import android.widget.Button;
    16 import android.widget.LinearLayout;
    17 import android.widget.TextView;
    18 
    19 import com.hxc.supreme.R;
    20 import com.hxc.supreme.service.MainService;
    21 
    22 
    23 /**
    24  * created by huxc  on 2017/9/28.
    25  * func:  ViewsTestActivity
    26  * email: hxc242313@qq.com
    27  */
    28 
    29 public class ViewsTestActivity extends AppCompatActivity implements View.OnClickListener {
    30     private LinearLayout mainLayout;
    31 
    32     @Override
    33     protected void onCreate(@Nullable Bundle savedInstanceState) {
    34         super.onCreate(savedInstanceState);
    35         setContentView(R.layout.activity_views_test);
    36 //        mainLayout = findViewById(R.id.layout_main);
    37 
    38         View view = LayoutInflater.from(this).inflate(R.layout.button_view, null);
    39         TextView textView = new TextView(this);
    40         textView.setText("add View Dynamic");
    41         textView.setGravity(Gravity.CENTER);
    42         textView.setAllCaps(false);
    43         textView.setTextColor(Color.RED);
    44         mainLayout.addView(textView,new LinearLayout.LayoutParams(300, 200));
    45     }
    46 
    47     @Override
    48     protected void onResume() {
    49         super.onResume();
    50     }
    51 
    52     @Override
    53     public void onClick(View view) {
    54         switch (view.getId()) {
    55         }
    56     }
    57 
    58 }

    第36行把findviewById屏蔽了,然后运行了一下程序,看一下logcat中的崩溃信息:

    crash文件的保存路径是Supreme_crash,看一下文件中的内容:

    和控制台输出的一毛一样,而且还打印出了手机的型号,app的版本等相关信息,大功告成!

  • 相关阅读:
    mysql 行号
    java 异常链
    springsecurity密码加密
    java 四舍五入
    ArrayList的使用及原理
    java 匿名内部类
    java 克隆
    logback的配置
    信号量 Semaphore
    障碍器 CyclicBarrier
  • 原文地址:https://www.cnblogs.com/hxchaoshuai/p/9989681.html
Copyright © 2020-2023  润新知