android基础知识---重写系统Crash处理类保存上传和完美退出程序的方法

当今市场上android的手机型号和版本太多要做到完全适配几乎是完全不可能的,那么怎么才能获取其他的玩家的出错的信息呢!这里我们就要重新定义系统的Crash处理类了。

首先我们我们新建一个CustomCrashHandler类 实现UncaughtExceptionHandler接口,重写回调方法void uncaughtException(Thread thread, Throwable ex)

package com.example.admin.crashchuli;

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 java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.TimeZone;

/**
 * 自定义系统的Crash捕捉类,用Toast替换系统的对话框
 * 将软件版本信息,设备信息,出错信息保存在sd卡中,方便上传到服务器中
 * @author ls
 *
 */
public class CustomCrashHandler implements Thread.UncaughtExceptionHandler {
    private static final String TAG = "Activity";
    private Context mContext;
    private static final String SDCARD_ROOT = Environment.getExternalStorageDirectory().toString();
    private static CustomCrashHandler mInstance = new CustomCrashHandler();

    private CustomCrashHandler(){}
    /**
     * 单例模式,保证只有一个CustomCrashHandler实例存在
     * @return
     */
    public static CustomCrashHandler getInstance(){
        return mInstance;
    }

    /**
     * 异常发生时,系统回调的函数,我们在这里处理一些操作
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        //将一些信息保存到SDcard中
        savaInfoToSD(mContext, ex);

        //提示用户程序即将退出
        showToast(mContext, "很抱歉,程序遭遇异常,即将退出!");
        try {
            thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
//      android.os.Process.killProcess(android.os.Process.myPid());
//        System.exit(1);

        //完美退出程序方法
        ExitAppUtils.getInstance().exit();

    }

    /**
     * 为我们的应用程序设置自定义Crash处理
     */
    public void setCustomCrashHanler(Context context){
        mContext = context;
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 显示提示信息,需要在线程中显示Toast
     * @param context
     * @param msg
     */
    private void showToast(final Context context, final String msg){
        new Thread(new Runnable() {

            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(context, msg, Toast.LENGTH_LONG).show();
                Looper.loop();
            }
        }).start();
    }

    /**
     * 获取一些简单的信息,软件版本,手机版本,型号等信息存放在HashMap中
     * @param context
     * @return
     */
    private HashMap<String, String> obtainSimpleInfo(Context context){
        HashMap<String, String> map = new HashMap<String, String>();
        PackageManager mPackageManager = context.getPackageManager();
        PackageInfo mPackageInfo = null;
        try {
            mPackageInfo = mPackageManager.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

        map.put("versionName", mPackageInfo.versionName);
        map.put("versionCode", "" + mPackageInfo.versionCode);

        map.put("MODEL", "" + Build.MODEL);
        map.put("SDK_INT", "" + Build.VERSION.SDK_INT);
        map.put("PRODUCT", "" +  Build.PRODUCT);

        return map;
    }

    /**
     * 获取系统未捕捉的错误信息
     * @param throwable
     * @return
     */
    private String obtainExceptionInfo(Throwable throwable) {
        StringWriter mStringWriter = new StringWriter();
        PrintWriter mPrintWriter = new PrintWriter(mStringWriter);
        throwable.printStackTrace(mPrintWriter);
        mPrintWriter.close();

        Log.e(TAG, mStringWriter.toString());
        return mStringWriter.toString();
    }

    /**
     * 保存获取的 软件信息,设备信息和出错信息保存在SDcard中
     * @param context
     * @param ex
     * @return
     */
    private String savaInfoToSD(Context context, Throwable ex){
        String fileName = null;
        StringBuffer sb = new StringBuffer();

        for (Map.Entry<String, String> entry : obtainSimpleInfo(context).entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key).append(" = ").append(value).append("\n");
        }

        sb.append(obtainExceptionInfo(ex));

        if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
            File dir = new File(SDCARD_ROOT + File.separator + "crash" + File.separator);
            if(! dir.exists()){
                dir.mkdir();
            }

            try{
                fileName = dir.toString() + File.separator + paserTime(System.currentTimeMillis()) + ".log";
                FileOutputStream fos = new FileOutputStream(fileName);
                fos.write(sb.toString().getBytes());
                fos.flush();
                fos.close();
            }catch(Exception e){
                e.printStackTrace();
            }

        }

        return fileName;

    }

    /**
     * 将毫秒数转换成yyyy-MM-dd-HH-mm-ss的格式
     * @param milliseconds
     * @return
     */
    private String paserTime(long milliseconds) {
        System.setProperty("user.timezone", "Asia/Shanghai");
        TimeZone tz = TimeZone.getTimeZone("Asia/Shanghai");
        TimeZone.setDefault(tz);
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
        String times = format.format(new Date(milliseconds));

        return times;
    }
}

当然不忘记加权限

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

然后是就是在重写的Application类中的onCreate()方法中为它设置Crash处理类。

/**
 * Created by ls on 2017/4/30.
 */

public class MyApplication  extends Application{
    @Override
    public void onCreate() {
        super.onCreate();
        CustomCrashHandler mCustomCrashHandler = CustomCrashHandler.getInstance();
        mCustomCrashHandler.setCustomCrashHanler(getApplicationContext());
    }
}

记得在清单文件中设置application这个就不用说了吧

然后随便写个空指针异常


public class MainActivity extends BaseActivity {
    TextView mTextView;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextView.setText("crash");
    }
}

然后我们就可看到在我们制定的文件夹下面有我们要的log的txt文件,剩下我就不说了就是简单的上传到服务器、

对了说下ExitAppUtils就是为了设置完美退出因为异常的时候会闪几次或者有点手机直接退出不了这肯定不行,所以就用ExitAppUtils来控制,当然不能所有activity都写所以要写在BaseActivity然后其他的继承就好了

package com.example.admin.crashchuli;

import android.app.Activity;
import android.os.Bundle;

/**
 * Created by admin on 2017/4/30.
 */

public class BaseActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ExitAppUtils.getInstance().addActivity(this);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        ExitAppUtils.getInstance().delActivity(this);
    }
}
时间: 2024-10-12 10:54:12

android基础知识---重写系统Crash处理类保存上传和完美退出程序的方法的相关文章

Android_ 重写系统Crash处理类,保存Crash信息到SD卡 和 完美退出程序的方法

转载时注明地址:http://blog.csdn.net/xiaanming/article/details/9344703 我们开发Android应用的时候,当出现Crash的时候,系统弹出一个警告框,如下图一,有些手机会黑屏几秒钟然后还伴随着振动,作为我们开发人员,是很讨厌这样子的Crash,因为这意味着我们又要改bug,每个程序员都希望自己开发出来的东西bug少点,稳定点,但是没有bug的程序几乎是不可能的,作为用户,如果出现这样子的警告框,他的心情也会很不爽,也许还会破口大骂,如果用图二

Android基础知识(6)—数据持久化之数据存储

阅读前,请浏览此处上方目录. Android基础知识(6)-数据持久化之数据存储 本章内容为个人笔记,参考书籍有:<疯狂的android>第3版.<第一行代码> 首先,我们要知道什么是数据持久化. 数据持久化就是指那些内存中的瞬时数据保存到存储设备中,保证即使手机在关机的情况下,这些数据不会丢失.保存在内存中的数据是处于瞬时状态,保存在存储设备中的数据是处于持久状态.持久化技术则是提供了一种机制可以让数据在瞬时状态和持久状态之间进行转换. Android系统主要提供了三种方式用于简

android基础知识

1. 前言 1.1. 什么是3G.4G Ÿ 第三代移动通信技术(3rd - Generation),速率一般在几百Kbps,较之前的2G和2.5G在数据传输速度上有很大提升. Ÿ 第四代移动通信技术(4th - Generation),速度可达到100Mbps以上,几乎可以满足人们的所有传输数据的需求. Ÿ 目前主流的3G技术标准有三种: WCDMA:全球80%以上的3G网络都是采用此种制式.中国联通运营.186 CDMA2000:目前日韩及北美使用较多.中国电信运营. 189 TD-SCDMA

android基础知识13:AndroidManifest.xml文件解析

1.重要性 AndroidManifest.xml是Android应用程序中最重要的文件之一.它是Android程序的全局配置文件,是每个 android程序中必须的文件.它位于我们开发的应用程序的根目录下,描述了package中的全局数据,包括package中暴露的组件 (activities, services, 等等),以及他们各自的实现类,各种能被处理的数据和启动位置等重要信息. 因此,该文件提供了Android系统所需要的关于该应用程序的必要信息,即在该应用程序的任何代码运行之前系统所

Android基础知识【项目实训】【4】

[该项目实训是Android基础知识的一个综合练习,特别提示:项目中会用到一些图片素材,都是随意整理的,稍后会上传一个资源,包含该事项项目的基本功能,也含有图片素材] [项目题目]:校园订餐App设计 综合案例 [目标] 主界面的功能确实比较复杂,因此上一篇知识说的周边内容.现在开始说这个界面的代码和布局文件. 1.先看一下项目的组织结构吧,要不然不好说他们的关系: (1)db包中的都是跟 数据库相关的 (2)eatall中放的都是activity或者fragment (3)entity中放的实

Android基础知识【项目实训】【1】

[该项目实训是Android基础知识的一个综合练习] [项目题目]:校园订餐App设计 综合案例 [设计目标] 1.必要功能 ?快餐店浏览,与订餐 ?今天订餐活动查询与订餐,特价饭菜预定 ?分类订餐查询,预定 ?常定饭菜  预定 ?健康餐推荐 ?定时预定,提前预定 ?订单查看, ?餐馆与饭菜打分,评价 ?用户注册与登录 2.扩展选择功能 ?快速拨打电话 ?饮食跟踪,热量估算 ?系统设置 [项目说明] 该项目为实际应用项目的单机 简化版本,只需要完成Android平台App的设计与开发工作. Ap

Android基础知识【项目实训】【2】

[该项目实训是Android基础知识的一个综合练习,特别提示:项目中会用到一些图片素材,都是随意整理的,稍后会上传一个资源,包含该事项项目的基本功能,也含有图片素材] [项目题目]:校园订餐App设计 综合案例 [目标] 因为项目只涉及基础知识,因此项目中所用数据并不联网,都读取单机数据库.(即将该项目中所用数据,如菜品信息.店铺信息等存入数据库)用户在第一次打开该项目时,会在用户手机上创建这些数据库,并插入测试数据. 1.先制作一个欢迎界面,欢迎的同时,准备数据库 欢迎界面Activity对应

Android基础知识【项目实训】【3】

[该项目实训是Android基础知识的一个综合练习,特别提示:项目中会用到一些图片素材,都是随意整理的,稍后会上传一个资源,包含该事项项目的基本功能,也含有图片素材] [项目题目]:校园订餐App设计 综合案例 [目标] 欢迎界面过后,应该显示app的主界面了,根据[UI设计指导]中的规划,主界面采用上下两级标签导航.这部分是app开发中比较麻烦的一块. 1.先来看一下,最终的效果吧,这样做起来比较有底: 默认显示的主界面,下部是主导航,上面是二级导航,默认打开的是"促销打折"这一版面

java基础知识回顾之java Thread类学习(四)--java多线程安全问题(锁)

上一节售票系统中我们发现,打印出了错票,0,-1,出现了多线程安全问题.我们分析为什么会发生多线程安全问题? 看下面线程的主要代码: @Override public void run() { // TODO Auto-generated method stub while(true){ if(ticket > 0){//当线程0被调起的时候,当执行到这条判断语句的时候,线程1被调起抢了CPU资源,线程0进入冻结状态. try { Thread.sleep(100);//中断当前活跃的线程,或者