Android使用UncaughtExceptionHandler获取crash信息

Android应用崩溃时如何获取crash信息及处理后续操作。

Android提供了处理此类问题的方法,Thread类中有这样一个方法

    /**
     * 设置线程突然终止时调用的默认处理程序
     * @param eh
     */
    public static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh) {
        defaultUncaughtExceptionHandler = eh;
    }

通用上面的方法,可以看到要传入一个Thread.UncaughtExceptionHandler对象到此方法中。

    public interface UncaughtExceptionHandler {
        /**
         * Method invoked when the given thread terminates due to the
         * given uncaught exception.
         * <p>Any exception thrown by this method will be ignored by the
         * Java Virtual Machine.
         * @param t the thread
         * @param e the exception
         */
        void uncaughtException(Thread t, Throwable e);
    }

UncaughtExceptionHandler是一个接口,首先要继承这个接口,实现我们的Crash处理

public class MyCrashExceptionHandler implements Thread.UncaughtExceptionHandler {
    private static final String TAG = "MyCrashExceptionHandler";

    private static MyCrashExceptionHandler instance;

    private Thread.UncaughtExceptionHandler mDefaultExceptionHandler;
    private Context mContext;

    private MyCrashExceptionHandler() {

    }

    public static MyCrashExceptionHandler getInstance() {
        if (instance == null) {
            synchronized (MyCrashExceptionHandler.class) {
                if (instance == null) {
                    instance = new MyCrashExceptionHandler();
                }
            }
        }
        return instance;
    }

    public void init(Context context) {
        if (context == null) {
            throw new IllegalArgumentException("Context is null!!!");
        }
        mContext = context.getApplicationContext();
        mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();//获取系统的默认异常处理程序
        Thread.setDefaultUncaughtExceptionHandler(this);//设置系统的默认异常处理
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, "程序发生异常,即将重启", Toast.LENGTH_SHORT).show();
                Looper.loop();
            }
        }).start();

        SystemClock.sleep(2000);

        Intent intent = new Intent(mContext, MainActivity.class);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        mContext.startActivity(intent);
        Process.killProcess(Process.myPid());
    }
}

MainActivity代码

public class MainActivity extends AppCompatActivity {

    ImageView img = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Button btn = new Button(this);
        btn.setText("崩溃");
        btn.setLayoutParams(new ViewGroup.LayoutParams(-1, -2));
        setContentView(btn);

        MyCrashExceptionHandler myCrashExceptionHandler = MyCrashExceptionHandler.getInstance();
        myCrashExceptionHandler.init(this);

        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                img.setImageResource(R.mipmap.ic_launcher);//异常
            }
        });
    }
}

MyCrashExceptionHandler应该在Application类中初始化,这里为了方便演示,直接写在MainActivity类中了
这里写图片描述

点击崩溃按钮,提示程序发生异常,即将重启,然后重新启动MainActivity界面

上述只是演示了发生异常时重启APK,接下来我们来获取Crash信息

  • 修改后的代码如下
public class MyCrashExceptionHandler implements Thread.UncaughtExceptionHandler {
    private static final String TAG = "MyCrashExceptionHandler";

    private static MyCrashExceptionHandler instance;

    private Thread.UncaughtExceptionHandler mDefaultExceptionHandler;
    private Context mContext;

    private final String mCrashFileName = "crash";

    private MyCrashExceptionHandler() {

    }

    public static MyCrashExceptionHandler getInstance() {
        if (instance == null) {
            synchronized (MyCrashExceptionHandler.class) {
                if (instance == null) {
                    instance = new MyCrashExceptionHandler();
                }
            }
        }
        return instance;
    }

    public void init(Context context) {
        if (context == null) {
            throw new IllegalArgumentException("Context is null!!!");
        }
        mContext = context.getApplicationContext();
        mDefaultExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();//获取系统的默认异常处理程序
        Thread.setDefaultUncaughtExceptionHandler(this);//设置系统的默认异常处理
    }

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, "程序发生异常,即将退出", Toast.LENGTH_SHORT).show();
                Looper.loop();
            }
        }).start();

        printCrashInfo(e);
        SystemClock.sleep(2000);
//
//        Intent intent = new Intent(mContext, MainActivity.class);
//        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
//        mContext.startActivity(intent);
        Process.killProcess(Process.myPid());
    }

    public void printCrashInfo(Throwable ex) {
        File path = mContext.getCacheDir();
        long time = System.currentTimeMillis();
        File crashFile = new File(path, mCrashFileName + time + ".trace");
        String date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date(time));
        try {
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(crashFile)));
            pw.println(date);
            PackageManager pm = mContext.getPackageManager();
            PackageInfo packageInfo = pm.getPackageInfo(mContext.getPackageName(), PackageManager.GET_ACTIVITIES);
            pw.println("packageName:" + mContext.getPackageName());
            pw.println("APK Ver:" + packageInfo.versionName + " _ " + packageInfo.versionCode);
            //TODO 这里为了演示就不打印更多的信息了
            pw.println("---------------------------------------------------");
            ex.printStackTrace(pw);
            pw.close();
        } catch (Exception e) {

        }
    }

    /**
     * 联网上传crash文件
     */
    public void uploadCrashFile() {
        //TODO
    }
}

点击崩溃后通过ADM查看包名下的cache目录可以看到crash文件已经保存成功了
这里写图片描述
导出后可以看到详细的崩溃信息
这里写图片描述
还可以根据需要上传crash信息文件,后台做到上线APK崩溃反馈崩溃信息,更好的完善APK。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值