从框架到完整项目搭建,实战项目《约个球》(2)-框架搭建之使用CrashHandler来获取应用的Crash信息

项目github地址:https://github.com/CameloeAnthony/DatingBall

在我们的实际开发中,我们都避免不了遇到我们程序crash直接崩掉的情况,这对用户来说是很不友好的 ,当然也是我们开发者不想看到的。所以我们希望当我们的程序发生异常crash的时候,我们能够得治用户的crash信息,我们也可以在程序crash的时候弹出一个对话框告诉用户程序crash了。然后再退出,这样比闪退会好一些。

在安卓中我们可以利用CrashHandler来监视我们应用的crash信息。这里也将CrashHander引入到我们的框架中去。

package com.nsu.library.utils.log;

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

import java.io.*;
import java.util.Calendar;
import java.util.Locale;

/**
 * Create By Anthony on 2016/1/16
 * 当前类注释:异常处理类,将我们的异常信息保存到本地SD卡上面或者上传到服务器
 */
public class UncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
	private static final boolean DEBUG =true;
	private static final String TAG ="UncaughtExceptionHandler";
	private Context mContext;
	private Thread.UncaughtExceptionHandler mDefaultHandler;

	public UncaughtExceptionHandler(Context context, Thread.UncaughtExceptionHandler defaultHandler){
		this.mDefaultHandler = defaultHandler;
		this.mContext = context;
	}

	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		Log.e("Crash", "Application crash", ex);
		writeFile(thread, ex);//将异常信息保存到SD卡上面
		//TODO 在这里写方法将异常信息上传到服务器
		mDefaultHandler.uncaughtException(thread, ex);
	}

	private void writeFile(final Thread thread, final Throwable ex){
		//如果SD卡不存在则无法写入
		if(!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
			if(DEBUG){
				Log.w(TAG,"SDCard unmounted, skip write exception to file");
				return ;
			}
		}
		try {
			OutputStream os = getLogStream();
			os.write(getExceptionInformation(thread, ex).getBytes("utf-8"));
			os.flush();
			os.close();

			android.os.Process.killProcess(android.os.Process.myPid());
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}

	private OutputStream getLogStream() throws IOException {

		//crash_log_pkgname.log
		String fileName = String.format("crash_%s.log", mContext.getPackageName());
		File file  = new File(Environment.getExternalStorageDirectory(), fileName);

		if(!file.exists()){
			file.createNewFile();
		}

		OutputStream os = new FileOutputStream(file, true);

		return os;
	}



	private String getExceptionInformation(Thread thread, Throwable ex){
		long current = System.currentTimeMillis();
		StringBuilder sb = new StringBuilder().append('\n');
		sb.append("THREAD: ").append(thread).append('\n');
		sb.append("BOARD: ").append(Build.BOARD).append('\n');
		sb.append("BOOTLOADER: ").append(Build.BOOTLOADER).append('\n');
		sb.append("BRAND: ").append(Build.BRAND).append('\n');
		sb.append("CPU_ABI: ").append(Build.CPU_ABI).append('\n');
		sb.append("CPU_ABI2: ").append(Build.CPU_ABI2).append('\n');
		sb.append("DEVICE: ").append(Build.DEVICE).append('\n');
		sb.append("DISPLAY: ").append(Build.DISPLAY).append('\n');
		sb.append("FINGERPRINT: ").append(Build.FINGERPRINT).append('\n');
		sb.append("HARDWARE: ").append(Build.HARDWARE).append('\n');
		sb.append("HOST: ").append(Build.HOST).append('\n');
		sb.append("ID: ").append(Build.ID).append('\n');
		sb.append("MANUFACTURER: ").append(Build.MANUFACTURER).append('\n');
		sb.append("MODEL: ").append(Build.MODEL).append('\n');
		sb.append("PRODUCT: ").append(Build.PRODUCT).append('\n');
		sb.append("SERIAL: ").append(Build.SERIAL).append('\n');
		sb.append("TAGS: ").append(Build.TAGS).append('\n');
		sb.append("TIME: ").append(Build.TIME).append(' ').append(toDateString(Build.TIME)).append('\n');
		sb.append("TYPE: ").append(Build.TYPE).append('\n');
		sb.append("USER: ").append(Build.USER).append('\n');
		sb.append("VERSION.CODENAME: ").append(Build.VERSION.CODENAME).append('\n');
		sb.append("VERSION.INCREMENTAL: ").append(Build.VERSION.INCREMENTAL).append('\n');
		sb.append("VERSION.RELEASE: ").append(Build.VERSION.RELEASE).append('\n');
		sb.append("VERSION.SDK_INT: ").append(Build.VERSION.SDK_INT).append('\n');
		sb.append("LANG: ").append(mContext.getResources().getConfiguration().locale.getLanguage()).append('\n');
		sb.append("APP.VERSION.NAME: ").append(getVersionName()).append('\n');
		sb.append("APP.VERSION.CODE: ").append(getVersionCode()).append('\n');
		sb.append("CURRENT: ").append(current).append(' ').append(toDateString(current)).append('\n');

		sb.append(getErrorInformation(ex));

		return sb.toString();
	}

	private String getVersionName(){
		PackageManager packageManager = mContext.getPackageManager();
		PackageInfo packInfo = null;
		try {
			packInfo = packageManager.getPackageInfo(mContext.getPackageName(),0);
		} catch (PackageManager.NameNotFoundException e) {
			e.printStackTrace();
		}
		String version = packInfo.versionName;

		return version;
	}

	private int getVersionCode(){
		PackageManager packageManager = mContext.getPackageManager();
		PackageInfo packInfo = null;
		try {
			packInfo = packageManager.getPackageInfo(mContext.getPackageName(),0);
		} catch (PackageManager.NameNotFoundException e) {
			e.printStackTrace();
		}
		int version = packInfo.versionCode;

		return version;
	}


	private String getErrorInformation(Throwable t){
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		PrintWriter writter = new PrintWriter(baos);
		t.printStackTrace(writter);
		writter.flush();
		String result = new String(baos.toByteArray());
		writter.close();

		return result;
	}

	private String toDateString(long timeMilli){
		Calendar calc = Calendar.getInstance();
		calc.setTimeInMillis(timeMilli);
		return String.format(Locale.CHINESE, "%04d.%02d.%02d %02d:%02d:%02d:%03d",
				calc.get(Calendar.YEAR), calc.get(Calendar.MONTH) + 1, calc.get(Calendar.DAY_OF_MONTH),
				calc.get(Calendar.HOUR_OF_DAY), calc.get(Calendar.MINUTE), calc.get(Calendar.SECOND), calc.get(Calendar.MILLISECOND));
	}

}
在这里我们只需要在我们的Application的父类:AbsApplication中调用:

Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(this,
      Thread.getDefaultUncaughtExceptionHandler()));


package com.nsu.library.app;

import android.app.Application;
import com.nsu.library.utils.log.UncaughtExceptionHandler;


/**
 * Create By Anthony on 2016/1/15
 * 当前类注释:Application的子类,本项目中的Application将继承本类。
 * 当前功能:单例模式,异常捕获,由子类实现的获取url
 */
public abstract class AbsApplication extends Application {
	private static AbsApplication sInstance;

	public static AbsApplication app(){
		return sInstance;
	}

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

		sInstance = this;

		Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler(this,
				Thread.getDefaultUncaughtExceptionHandler()));


	}


	/***************** About URL ***********************/
	public static enum SourceType{
		JSON, XML,SOAP
	}
	abstract public String getApplicationConfigUrl();
	abstract public SourceType getSourceType();
	abstract public String getFirstClassUrl();

}

以后在我们的程序出现异常崩溃的时候我们都可以将我们的错误信息写入到我们的SD卡上。后期实现还可以将错误信息上报服务器,有利于软件的二次维护。

这里在以前的项目开发中也用到过另外一种情况,我们直接告诉用户我们的程序异常退出了,需不需要重启:

import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
import com.trs.xizang.voice.activity.VtibetSplashActivity;
import com.trs.xizang.voice.view.VAlertDialog;

/**
 * Created by Anthony on 2015/11/18.
 */
public class CrashHandler implements Thread.UncaughtExceptionHandler {

    private static CrashHandler INSTANCE = new CrashHandler();
    private Context mContext;
    private Thread.UncaughtExceptionHandler mDefaultHandler;

    private CrashHandler() {
    }

    public static CrashHandler getInstance() {
        return INSTANCE;
    }

    public void init(Context ctx) {
        mContext = ctx;
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    @Override
    public void uncaughtException(Thread thread, Throwable ex) {
        ex.printStackTrace();
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                
                final AlertDialog dialog = new AlertDialog(mContext);
                dialog.setMessage("程序异常退出,是否需要重新启动应用?"));
                dialog.setPositiveListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dialog.dismiss();
                        Intent intent = new Intent(mContext.getApplicationContext(), VtibetSplashActivity.class);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        mContext.startActivity(intent);
                        System.exit(0);
                    }
                });
                dialog.setNegativeListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dialog.dismiss();
                        System.exit(0);
                    }
                });
                dialog.show();
                Looper.loop();
            }
        }.start();

    }


}
上面这个处理的CrashHandler里面直接调用了Thread.setDefaultUncaughtExceptionHandler(this),然而在我们开始介绍的那个CrashHanler里面是在Application里面调用的。 所以使用上面这个CrashHandler的方式是在Application的onCreate里面调用:

CrashHandler.getInstance().init(this);
okay!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值