android 快速开发三、 获取应用的crash信息

当用户发生了crash,开发者却无法得知程序为何crash,即便开发人员想去解决这个crash,但是由于无法知道用户当时crash信息,所以往往无能为力。android 提供了获取crash信息的方法。话不多说,直接上代码。

package com.example.wmk.utils;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;

import com.lidroid.xutils.util.LogUtils;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;

/**
 * Created by 老王 on 2016/12/2.
 */
public class CrashHandler implements Thread.UncaughtExceptionHandler {

    /**
     * CrashHandler
     */
    private static CrashHandler CRASHHANDLER = null;

    private Thread.UncaughtExceptionHandler mDefaultUncaughtExceptionHandler;
    private Context mContext;

    private static final String FILE_NAME = "crash";
    private static final String FILE_NAME_TXT = ".txt";

    /**
     * Get Instance
     *
     * @return CrashHandler
     */
    public static CrashHandler getInstance() {
        if (CRASHHANDLER == null) {
            CRASHHANDLER = new CrashHandler();
        }
        return CRASHHANDLER;
    }

    private CrashHandler() {
    }

    public void init(Context context) {
        mDefaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
        mContext = context.getApplicationContext();
    }


    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        //导出异常信息到SD卡中
        dumpExceptionToSDCard(ex);
        //这里可以上传异常信息到服务器,便于开发人员分析日志从而解决bug
        uploadExceptionToServer();

        ex.printStackTrace();

        //如果系统提供了默认的异常处理器,则交给系统去结束程序,否则就由自己结束自己
        if (mDefaultUncaughtExceptionHandler != null) {
            mDefaultUncaughtExceptionHandler.uncaughtException(thread, ex);
        } else {
            ActivityManager.getInstance().appExceptionExit();
        }
    }


    private void dumpExceptionToSDCard(Throwable ex) {

        String time = TimeUtils.formatDate("yyyy-MM-dd HH:mm:ss");
        //创建crash文件
        File file = ProjectUtils.createFile(ProjectUtils.WMK_LOG + FILE_NAME + time + FILE_NAME_TXT);

        try {
            PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter(file)));
            pw.println(time);
            dumpPhoneInfo(pw);
            pw.println();
            ex.printStackTrace(pw);
            pw.close();

        } catch (Exception e) {
            LogUtils.e("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

    }
}

首先,封装了一个日期工具类,用于显示日期,代码如下:

package com.example.wmk.utils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;

/**
 * Created by 老王 on 2016/12/2.
 */
public class TimeUtils {

    /**
     * 格式化当前日期
     *
     * @param rule 格式规则
     * @return 格式化后的日期
     */
    public static String formatDate(String rule) {
        return new SimpleDateFormat(rule, Locale.getDefault()).format(new Date());
    }

}

其次,用到了项目工具类中的创建文件的方法。

package com.example.wmk.utils;

import android.os.Environment;

import java.io.File;

/**
 * Created by 老王 on 2016/12/1.
 */
public class ProjectUtils {

    /**
     * 根目录
     */
    public static File ROOT_DIRECTORY = Environment.getExternalStorageDirectory();

    /**
     * 项目路径
     */
    public static final String PROJECT_PATH = ROOT_DIRECTORY.getAbsolutePath() + "/WMK/";

    /**
     * 数据库
     */
    public static final String WMK_DB = PROJECT_PATH + "DB/";

    /**
     * 图片
     */
    public static final String WMK_IMG = PROJECT_PATH + "IMG/";

    /**
     * Log
     */
    public static final String WMK_LOG = PROJECT_PATH + "LOG/";

    /**
     * sdCard
     *
     * @return If there is a SD card to return true, otherwise false
     */
    public static boolean existsSdCard() {
        return android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
    }

    /**
     * 创建文件
     *
     * @param s
     * @return If you create a file successfully returned to the current file, otherwise
     * Null
     */
    public static File createFile(String s) {
        return new File(s);
    }

    /**
     * 创建文件夹
     *
     * @param s
     * @return If you create a folder successfully returned to the current folder, otherwise
     * Null
     */
    public static File createFolder(String s) {
        File file = new File(s);
        if (!file.exists()) {
            file.mkdirs();
        }
        return file;
    }

    /**
     * 初始化
     *
     * @return If true is returned to the initial success, otherwise false
     */
    public static boolean init() {
        boolean result = false;
        if (existsSdCard()) {
            result = true;
            result &= createFolder(PROJECT_PATH) != null;
            result &= createFolder(WMK_DB) != null;
            result &= createFolder(WMK_IMG) != null;
            result &= createFolder(WMK_LOG) != null;
        }
        return result;
    }
}

最后,在application中,捕获crash信息,代码如下:

package com.example.wmk;

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

import com.example.wmk.utils.ActivityManager;
import com.example.wmk.utils.CrashHandler;
import com.example.wmk.utils.ProjectUtils;
import com.lidroid.xutils.util.LogUtils;

/**
 * Created by 老王 on 2016/12/1.
 */
public class WMKApplication extends Application {

    ActivityManager mActivityManager;

    @Override
    public void onCreate() {
        super.onCreate();

        //init ActivityManager
        mActivityManager = ActivityManager.getInstance();

        // 注册activity监听器
        registerActivityListener();

        if (ProjectUtils.init()) {

            //在这里为应用设置异常处理,然后程序才能获取未处理的异常
            CrashHandler crashHandler = CrashHandler.getInstance();
            crashHandler.init(this);

        } else {
            mActivityManager.appExceptionExit();
        }
    }

    private void registerActivityListener() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {

                @Override
                public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
                    /**
                     * 监听到 Activity创建事件 将该 Activity 加入list
                     */
                    mActivityManager.pushActivity(activity);
                    /**
                     * 栈顶元素名称
                     */
                    LogUtils.d("TopActivityName:" + mActivityManager.getTopActivityName());
                }

                @Override
                public void onActivityStarted(Activity activity) {

                }

                @Override
                public void onActivityResumed(Activity activity) {

                }

                @Override
                public void onActivityPaused(Activity activity) {

                }

                @Override
                public void onActivityStopped(Activity activity) {

                }

                @Override
                public void onActivitySaveInstanceState(Activity activity, Bundle outState) {

                }

                @Override
                public void onActivityDestroyed(Activity activity) {
                    if (null == mActivityManager.getActivitys() && mActivityManager.getActivitys().isEmpty()) {
                        return;
                    }
                    if (mActivityManager.getActivitys().contains(activity)) {
                        /**
                         * 监听到 Activity销毁事件 将该Activity list中移除
                         */
                        mActivityManager.popActivity(activity);
                    }
                }
            });
        }
    }
}

下面在activity中抛出一个异常,布局文件如下:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".activity.MainActivity">

    <Button
        android:id="@+id/btn_crash"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="crash" />
</RelativeLayout>

activity界面代码如下:

package com.example.wmk.activity;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

import com.example.wmk.R;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.view.annotation.ViewInject;

public class MainActivity extends Activity {

    @ViewInject(R.id.btn_crash)
    private Button btnCrash = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ViewUtils.inject(this);

        initOnclickListener();

    }

    private void initOnclickListener() {
        View.OnClickListener onClickListener = new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (v == btnCrash) {
                    throw new RuntimeException("自定义异常");
                }
            }
        };
        btnCrash.setOnClickListener(onClickListener);
    }
}

点击自定义异常按钮,抛出crash信息,在SDCard中,会产生一个crash文件,如图:


最后可以把用户的crash信息上传服务器。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值