Android 程序异常崩溃的捕捉 前后端

本文介绍如何在Android应用中实现全局异常捕捉,包括自定义异常捕获类、初始化异常处理器、收集设备信息及异常信息,并通过HTTP POST方式将错误报告发送到服务器进行日志备份。

这是一篇笔记,也有参考别人的,也供别人参考,代码有详细注释

参考文章:

[转]Android自定义捕获Application全局异常 
http://www.cnblogs.com/freeliver54/archive/2011/10/25/2223729.html

Android 对程序异常崩溃的捕捉
http://blog.youkuaiyun.com/i_lovefish/article/details/17719081

效果图:


输出异常信息:


android端:

一、自定义异常捕获类

package com.example.uncaughtexceptiondemo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import com.example.uncaughtexceptiondemo.application.AppManager;
import com.example.uncaughtexceptiondemo.net.HttpClientUtil;

/**
 * 捕获全局异常,因为有的异常我们捕获不到
 * 
 * @author river
 * 
 */
public class UncaughtException implements UncaughtExceptionHandler {
	private final static String TAG = "UncaughtException";
	/**
	 * 保存的异常信息的路径
	 */
	private static String path = Environment.getExternalStorageDirectory() + "/rz/crash/";

	private static UncaughtException mUncaughtException;
	private Context context;
	private StringBuffer sb;
	/**
	 * 取得当前时间
	 */
	private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
	/**
	 * 声明当前年时间
	 */
	private String time;

	/**
	 * 用来存储设备信息和异常信息,后面再把这些信息取出来用StringBuffer拼接后,存入文件中
	 */
	private Map<String, String> infos = new HashMap<String, String>();

	public Context getContext() {
		return context;
	}

	/**
	 * 初始化context 添加到需要检测的类下面
	 * 
	 * @param context
	 */
	public void setContext(Context context) {
		this.context = context;
	}

	private UncaughtException() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * 获取UncaughtException实例 ,单例模式 同步方法,保证只有一个CrashHandler实例 ,以免单例多线程环境下出现异常
	 * 
	 * @return
	 */
	public synchronized static UncaughtException getInstance() {
		if (mUncaughtException == null) {
			mUncaughtException = new UncaughtException();
		}
		return mUncaughtException;
	}

	/**
	 * 
	 * 获取系统默认的UncaughtException处理器, 设置该mUncaughtException为程序的默认处理器
	 * 初始化,把当前对象设置成UncaughtExceptionHandler处理异常
	 */
	public void init() {
		Thread.setDefaultUncaughtExceptionHandler(mUncaughtException);
	}
	private boolean isShow = false;
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		if (!isShow) {
			// 获得当前的Activity对象
			context = AppManager.getAppManager().currentActivity();
			// 搜集设备信息
			collectDeviceInfo(context);
			// 保存信息到文件中
			saveCrashInfo2File(ex);

			// 发送错误报告到服务器 ,也可以放到dialog中,用户同意再发送
			// sendCrashReportsToServer();
			// 通知用户程序出现异常
			showDialog();
		} else {
		}
	}

	private void sendCrashReportsToServer() {
		// TODO Auto-generated method stub
		// 发送Bug信息到服务器
		HttpClientUtil util = new HttpClientUtil();
		Map<String, String> map = new HashMap<String, String>();
		map.put("ask", sb.toString());

		String post = util.sendUnDESPost("www.baidu.com", map);
	}

	/**
	 * 弹出异常对话框,通知用户
	 */
	private void showDialog() {
		new Thread() {
			@Override
			public void run() {
				// 子线程弹出dialog 必须有looper.prepare(); Looper.loop();
				Looper.prepare();

				View view = View.inflate(context, R.layout.exception_dialog, null);
				Button button = (Button) view.findViewById(R.id.dialog_button_cancel);

				final AlertDialog dlg = new AlertDialog.Builder(context).setCancelable(false).create();

				dlg.setView(view, 0, 0, 0, 0);
				button.setOnClickListener(new OnClickListener() {

					@Override
					public void onClick(View v) {
						// 用户同意再发送
						// sendCrashReportsToServer();
						isShow = true;
						// Intent startMain = new Intent(Intent.ACTION_MAIN);
						// startMain.addCategory(Intent.CATEGORY_HOME);
						// startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
						// context.startActivity(startMain);
						System.exit(0);
					}
				});

				dlg.show();
				Looper.loop();
			}
		}.start();
	}
	/**
	 * 收集设备参数信息
	 * 
	 * @param ctx
	 */

	public void collectDeviceInfo(Context context) {
		try {
			PackageManager pm = context.getPackageManager();
			PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
			if (pi != null) {
				String versionName = pi.versionName == null ? "null" : pi.versionName;
				String versionCode = pi.versionCode + "";
				// 把版本名字放入infos中
				infos.put("versionName", versionName);
				// 把版本号放入infos中
				infos.put("versionCode", versionCode);
			}
		} catch (NameNotFoundException e) {
			Log.e(TAG, "an error occured when collect package info", e);
		}
		// 使用反射来收集设备信息.在Build类中包含各种设备信息,
		// 例如: 系统版本号,设备生产商 等帮助调试程序的有用信息
		// 具体信息请参考后面的log输出
		// 得到Build的所有属性,通过Build的类获取手机硬件信息
		Field[] fields = Build.class.getDeclaredFields();
		for (Field field : fields) {
			try {
				field.setAccessible(true);

				if ("TIME".equals(field.getName())) {
					time = format.format(new Date());
					infos.put(field.getName(), time);
					Log.v("kan", "名字  = " + field.getName() + ",time = " + time);
					// 输出:11-26 16:43:55.400: V/kan(1810): 名字 = TIME,time =
					// 2014-11-26-16-43-55
				} else {
					infos.put(field.getName(), field.get(null).toString());

					Log.v("kan", "名字  = " + field.getName() + ",值 = " + field.get(null).toString());
					// 输出:
					// 11-26 16:31:02.615: V/kan(862): 名字 = BOARD,值 = smdk4x12
					// 11-26 16:31:02.615: V/kan(862): 名字 = BOOTLOADER,值 =
					// C101ZNUANA2
					// 11-26 16:31:02.615: V/kan(862): 名字 = BRAND,值 = samsung
					// 11-26 16:31:02.620: V/kan(862): 名字 = CPU_ABI,值 =
					// armeabi-v7a
					// 11-26 16:31:02.620: V/kan(862): 名字 = CPU_ABI2,值 = armeabi
					// 11-26 16:31:02.620: V/kan(862): 名字 = DEVICE,值 =
					// mproject3g
					// 11-26 16:31:02.620: V/kan(862): 名字 = DISPLAY,值 =
					// JDQ39.C101ZNUANA2
					// 11-26 16:31:02.620: V/kan(862): 名字 = FINGERPRINT,值 =
					// samsung/mproject3gzn/mproject3g:4.2.2/JDQ39/C101ZNUANA2:user/release-keys
					// 11-26 16:31:02.620: V/kan(862): 名字 = HARDWARE,值 =
					// smdk4x12
					// 11-26 16:31:02.620: V/kan(862): 名字 = HOST,值 = SEP-123
					// 11-26 16:31:02.620: V/kan(862): 名字 = ID,值 = JDQ39
					// 11-26 16:31:02.620: V/kan(862): 名字 = IS_DEBUGGABLE,值 =
					// false
					// 11-26 16:31:02.620: V/kan(862): 名字 = IS_SECURE,值 = false
					// 11-26 16:31:02.620: V/kan(862): 名字 = IS_SYSTEM_SECURE,值 =
					// false
					// 11-26 16:31:02.620: V/kan(862): 名字 =
					// IS_TRANSLATION_ASSISTANT_ENABLED,值 = false
					// 11-26 16:31:02.620: V/kan(862): 名字 = MANUFACTURER,值 =
					// samsung
					// 11-26 16:31:02.620: V/kan(862): 名字 = MODEL,值 = SM-C101
					// 11-26 16:31:02.620: V/kan(862): 名字 = PRODUCT,值 =
					// mproject3gzn
					// 11-26 16:31:02.620: V/kan(862): 名字 = RADIO,值 = unknown
					// 11-26 16:31:02.620: V/kan(862): 名字 = SERIAL,值 =
					// 4d004dd9b0d87003
					// 11-26 16:31:02.620: V/kan(862): 名字 = TAGS,值 =
					// release-keys
					// 11-26 16:31:02.625: V/kan(862): 名字 = TYPE,值 = user
					// 11-26 16:31:02.625: V/kan(862): 名字 = UNKNOWN,值 = unknown
					// 11-26 16:31:02.625: V/kan(862): 名字 = USER,值 = se.infra

				}
				/*
				 * Set<String> set = infos.keySet(); for(String key:set){
				 * Log.v("kan","key : "+key+", 值 : "+infos.get(key)); }
				 */
				Log.d(TAG, field.getName() + " : " + field.get(null));
			} catch (Exception e) {
				Log.e(TAG, "an error occured when collect crash info", e);
			}
		}
	}
	/**
	 * 保存错误信息到文件中
	 * 
	 * @param ex
	 * @return 返回文件名称,便于将文件传送到服务器
	 */
	private String saveCrashInfo2File(Throwable ex) {

		String SDState = Environment.getExternalStorageState();
		if (SDState.equals(Environment.MEDIA_MOUNTED)) {
			sb = new StringBuffer();
			sb.append("本手机信息:\n");
			// 取出infos中的信息放入字符sb中
			for (Map.Entry<String, String> entry : infos.entrySet()) {
				String key = entry.getKey();
				String value = entry.getValue();
				sb.append(key + "=" + value + "\n");
			}

			Writer writer = new StringWriter();
			PrintWriter printWriter = new PrintWriter(writer);
			ex.printStackTrace(printWriter);
			Throwable cause = ex.getCause();
			while (cause != null) {
				cause.printStackTrace(printWriter);
				cause = cause.getCause();
			}
			printWriter.close();
			String result = writer.toString();
			// 把异常信息加入字符串sb中
			sb.append("异常信息 :\n" + result);
			try {
				long timestamp = System.currentTimeMillis();
				String time = format.format(new Date());
				String fileName = "crash-" + time + "-" + timestamp + ".txt";
				if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
					File dir = new File(path);
					if (!dir.exists()) {
						dir.mkdirs();
					}
					FileOutputStream fos = new FileOutputStream(path + fileName);
					fos.write(sb.toString().getBytes());
					fos.close();
				}
				return fileName;
			} catch (Exception e) {
				Log.e(TAG, "an error occured while writing file...", e);
			}
		}
		return null;

	}
}

二、针对异常的捕捉要进行全局监控整个项目,所以要将其在Application中注册(也就是初始化):

package com.example.uncaughtexceptiondemo.application;

import java.util.LinkedList;
import java.util.List;

import android.app.Activity;
import android.app.Application;

import com.example.uncaughtexceptiondemo.UncaughtException;


/**全局application类
 * @author nseer
 *
 */
public class GlobalParams extends Application {

	@Override
	public void onCreate() {

		super.onCreate();

		// Initialize UncaughtException 
		//针对异常的捕捉要进行全局监控整个项目,所以要将其在Application中注册(也就是初始化):
		UncaughtException crashHandler = UncaughtException.getInstance();
		crashHandler.init();
	}





}

三、定义一个activity管理类,负责将监控的activity加入,一般加入顶级父类

package com.example.uncaughtexceptiondemo.application;

import android.app.Activity;
import android.content.Context;

import java.util.Stack;

/**
 * 应用程序Activity管理类:用于Activity管理和应用程序退出
 */
public class AppManager {
	
	private static Stack<Activity> activityStack;
	private static AppManager instance;
	
	private AppManager(){}
	/**
	 * 单一实例
	 */
	public static AppManager getAppManager(){
		if(instance==null){
			instance=new AppManager();
		}
		return instance;
	}
	/**
	 * 添加Activity到堆栈
	 */
	public void addActivity(Activity activity){
		if(activityStack==null){
			activityStack=new Stack<Activity>();
		}
		activityStack.add(activity);
	}
	/**
	 * 获取当前Activity(堆栈中最后一个压入的)
	 */
	public Activity currentActivity(){
		Activity activity=activityStack.lastElement();
		return activity;
	}
	/**
	 * 结束当前Activity(堆栈中最后一个压入的)
	 */
	public void finishActivity(){
		Activity activity=activityStack.lastElement();
		finishActivity(activity);
	}
	/**
	 * 结束指定的Activity
	 */
	public void finishActivity(Activity activity){
		if(activity!=null){
			activityStack.remove(activity);
			activity.finish();
			activity=null;
		}
	}
	/**
	 * 结束指定类名的Activity
	 */
	public void finishActivity(Class<?> cls){
		for (Activity activity : activityStack) {
			if(activity.getClass().equals(cls) ){
				finishActivity(activity);
			}
		}
	}
	/**
	 * 结束所有Activity
	 */
	public void finishAllActivity(){
		for (int i = 0, size = activityStack.size(); i < size; i++){
            if (null != activityStack.get(i)){
            	activityStack.get(i).finish();
            }
	    }
		activityStack.clear();
	}
	/**
	 * 退出应用程序
	 */
	public void AppExit(Context context) {
		try {
			finishAllActivity();
//			ActivityManager activityMgr= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
//			activityMgr.killBackgroundProcesses(context.getPackageName());
            android.os.Process.killProcess(android.os.Process.myPid());
			System.exit(0);
		} catch (Exception e) {	}
	}
}

四、在配置文件中对Application进行注册:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.uncaughtexceptiondemo"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="17" />
      <!-- Required  一些系统要求的权限,如访问网络等 -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" >
    </uses-permission>
    <uses-permission android:name="android.permission.RECORD_AUDIO" />

    <application
        android:allowBackup="true"
        android:name="com.example.uncaughtexceptiondemo.application.GlobalParams"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.uncaughtexceptiondemo.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

五、模拟一个空指针异常测试:

package com.example.uncaughtexceptiondemo;

import java.util.List;

import com.example.uncaughtexceptiondemo.application.AppManager;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//加入需要监控的activity,一般放在顶级父类中
		//两种方法
		//第一种方法利用AppManager添加到栈中
		AppManager.getAppManager().addActivity(this);
		//第二种方法调用setContext()直接初始化 
		//设置UncaughtException 需要的上下文
//		UncaughtException.getInstance().setContext(this); 
		
	}

	public  void throwException(View view) {

		List list = null;
		Log.e("MainActivity", list.get(0)+"");
		
	}
}

服务器端:

一、servlet接受代码:

后端接受ask值后,保存在本地E盘根目录下,文件名以日期格式yyyy-MM-dd-HH-mm-ss命名:
package android.log ;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.io.* ;
import javax.servlet.* ;
import javax.servlet.http.* ;

public class AndroidLogServlet extends HttpServlet {

    /**  */
    private static final long serialVersionUID = 1L;

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException,IOException {
        req.setCharacterEncoding("UTF-8");
        resp.setCharacterEncoding("UTF-8");
        String ask = req.getParameter("ask");
		 PrintWriter out = resp.getWriter();
        out.write("ask: " + ask);
        System.out.println(ask);
		if(ask!=null)
		saveLog(ask);
   
}
	private  void saveLog(String log) {
		// TODO Auto-generated method stub	
		SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
		String temp = format.format(new Date());
		File f = new File("e:\\"+temp+".txt") ;
		  OutputStream out = null ;
		  try
		  {
		   out = new FileOutputStream(f) ;
		  }
		  catch (FileNotFoundException e)
		  {
		   e.printStackTrace();
		  }
		 
		  byte b[] = log.getBytes() ;
		  try
		  {
		   
		   out.write(b) ;
		  }
		  catch (IOException e1)
		  {
		   e1.printStackTrace();
		  }
		  try
		  {
		  out.flush();
		   out.close() ;
		  }
		  catch (IOException e2)
		  {
		   e2.printStackTrace();
		  }
		 
	}

}
把类编译成class文件:


二:web.xml配置:

<servlet>
		<servlet-name>android</servlet-name>
		<servlet-class>android.log.AndroidLogServlet</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>android</servlet-name>
		<url-pattern>/androidLog</url-pattern>
	</servlet-mapping>
web配置效果:

三、启动tomcat信息:



四、可以通过两种方式访问:浏览器访问和android客户端访问

ask的值会显示在上图tomcat的下面

1.浏览器访问


2.android客户端访问效果见第一张效果图。

五、后台效果图:



注意事项:

请求网络
1.关掉防火墙
2.把请求放到线程当中
3.url写替换为本机IP地址
改写异常类代码:主要是修改网络发送log信息部分
package com.example.uncaughtexceptiondemo;

import java.io.File;
import java.io.FileOutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

import com.example.uncaughtexceptiondemo.application.AppManager;
import com.example.uncaughtexceptiondemo.net.HttpClientUtil;

/**
 * 捕获全局异常,因为有的异常我们捕获不到
 * 
 * @author river
 * 
 */
public class UncaughtException implements UncaughtExceptionHandler {
	private final static String TAG = "UncaughtException";
	/**
	 * 保存的异常信息的路径
	 */
	private static String path = Environment.getExternalStorageDirectory() + "/rz/crash/";

	private static UncaughtException mUncaughtException;
	private Context context;
	private StringBuffer sb;
	/**
	 * 取得当前时间
	 */
	private SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
	/**
	 * 声明当前年时间
	 */
	private String time;

	/**
	 * 用来存储设备信息和异常信息,后面再把这些信息取出来用StringBuffer拼接后,存入文件中
	 */
	private Map<String, String> infos = new HashMap<String, String>();

	public Context getContext() {
		return context;
	}

	/**
	 * 初始化context 添加到需要检测的类下面
	 * 
	 * @param context
	 */
	public void setContext(Context context) {
		this.context = context;
	}

	private UncaughtException() {
		// TODO Auto-generated constructor stub
	}

	/**
	 * 获取UncaughtException实例 ,单例模式 同步方法,保证只有一个CrashHandler实例 ,以免单例多线程环境下出现异常
	 * 
	 * @return
	 */
	public synchronized static UncaughtException getInstance() {
		if (mUncaughtException == null) {
			mUncaughtException = new UncaughtException();
		}
		return mUncaughtException;
	}

	/**
	 * 
	 * 获取系统默认的UncaughtException处理器, 设置该mUncaughtException为程序的默认处理器
	 * 初始化,把当前对象设置成UncaughtExceptionHandler处理异常
	 */
	public void init() {
		Thread.setDefaultUncaughtExceptionHandler(mUncaughtException);
	}
	private boolean isShow = false;
	@Override
	public void uncaughtException(Thread thread, Throwable ex) {
		if (!isShow) {
			// 获得当前的Activity对象
			context = AppManager.getAppManager().currentActivity();
			// 搜集设备信息
			collectDeviceInfo(context);
			// 保存信息到文件中
			saveCrashInfo2File(ex);

			// 发送错误报告到服务器 ,也可以放到dialog中,用户同意再发送
			 sendCrashReportsToServer();
			// 通知用户程序出现异常
			showDialog();
		} else {
		}
	}

	private void sendCrashReportsToServer() {
		// TODO Auto-generated method stub
		new Thread(new Runnable() {
			@Override
			public void run() {
				sendUnDESPost();
//				 sentGet();
			}
		}).start();
	}
	private void sendUnDESPost() {
		// 发送Bug信息到服务器
		HttpClientUtil util = new HttpClientUtil();
		Map<String, String> map = new HashMap<String, String>();
		map.put("ask", sb.toString());
//		Log.v("kan", "输出 :" + sb.toString());

		String postString = util.sendUnDESPost("http://192.168.0.113:8080/myweb/androidLog", map);
		Log.v("kan", "postString = " + postString);
	}

	/**
	 * 弹出异常对话框,通知用户
	 */
	private void showDialog() {
		new Thread() {
			@Override
			public void run() {
				// 子线程弹出dialog 必须有looper.prepare(); Looper.loop();
				Looper.prepare();

				View view = View.inflate(context, R.layout.exception_dialog, null);
				Button button = (Button) view.findViewById(R.id.dialog_button_cancel);

				final AlertDialog dlg = new AlertDialog.Builder(context).setCancelable(false).create();

				dlg.setView(view, 0, 0, 0, 0);
				button.setOnClickListener(new OnClickListener() {

					@Override
					public void onClick(View v) {
						// 用户同意再发送
//						sendCrashReportsToServer();
						isShow = true;
						// Intent startMain = new Intent(Intent.ACTION_MAIN);
						// startMain.addCategory(Intent.CATEGORY_HOME);
						// startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
						// context.startActivity(startMain);
						System.exit(0);
					}
				});

				dlg.show();
				Looper.loop();
			}
		}.start();
	}
	/**
	 * 收集设备参数信息
	 * 
	 * @param ctx
	 */

	public void collectDeviceInfo(Context context) {
		try {
			PackageManager pm = context.getPackageManager();
			PackageInfo pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
			if (pi != null) {
				String versionName = pi.versionName == null ? "null" : pi.versionName;
				String versionCode = pi.versionCode + "";
				// 把版本名字放入infos中
				infos.put("versionName", versionName);
				// 把版本号放入infos中
				infos.put("versionCode", versionCode);
			}
		} catch (NameNotFoundException e) {
			Log.e(TAG, "an error occured when collect package info", e);
		}
		// 使用反射来收集设备信息.在Build类中包含各种设备信息,
		// 例如: 系统版本号,设备生产商 等帮助调试程序的有用信息
		// 具体信息请参考后面的log输出
		// 得到Build的所有属性,通过Build的类获取手机硬件信息
		Field[] fields = Build.class.getDeclaredFields();
		for (Field field : fields) {
			try {
				field.setAccessible(true);

				if ("TIME".equals(field.getName())) {
					time = format.format(new Date());
					infos.put(field.getName(), time);
//					Log.v("kan", "名字  = " + field.getName() + ",time = " + time);
					// 输出:11-26 16:43:55.400: V/kan(1810): 名字 = TIME,time =
					// 2014-11-26-16-43-55
				} else {
					infos.put(field.getName(), field.get(null).toString());

//					Log.v("kan", "名字  = " + field.getName() + ",值 = " + field.get(null).toString());
					// 输出:
					// 11-26 16:31:02.615: V/kan(862): 名字 = BOARD,值 = smdk4x12
					// 11-26 16:31:02.615: V/kan(862): 名字 = BOOTLOADER,值 =
					// C101ZNUANA2
					// 11-26 16:31:02.615: V/kan(862): 名字 = BRAND,值 = samsung
					// 11-26 16:31:02.620: V/kan(862): 名字 = CPU_ABI,值 =
					// armeabi-v7a
					// 11-26 16:31:02.620: V/kan(862): 名字 = CPU_ABI2,值 = armeabi
					// 11-26 16:31:02.620: V/kan(862): 名字 = DEVICE,值 =
					// mproject3g
					// 11-26 16:31:02.620: V/kan(862): 名字 = DISPLAY,值 =
					// JDQ39.C101ZNUANA2
					// 11-26 16:31:02.620: V/kan(862): 名字 = FINGERPRINT,值 =
					// samsung/mproject3gzn/mproject3g:4.2.2/JDQ39/C101ZNUANA2:user/release-keys
					// 11-26 16:31:02.620: V/kan(862): 名字 = HARDWARE,值 =
					// smdk4x12
					// 11-26 16:31:02.620: V/kan(862): 名字 = HOST,值 = SEP-123
					// 11-26 16:31:02.620: V/kan(862): 名字 = ID,值 = JDQ39
					// 11-26 16:31:02.620: V/kan(862): 名字 = IS_DEBUGGABLE,值 =
					// false
					// 11-26 16:31:02.620: V/kan(862): 名字 = IS_SECURE,值 = false
					// 11-26 16:31:02.620: V/kan(862): 名字 = IS_SYSTEM_SECURE,值 =
					// false
					// 11-26 16:31:02.620: V/kan(862): 名字 =
					// IS_TRANSLATION_ASSISTANT_ENABLED,值 = false
					// 11-26 16:31:02.620: V/kan(862): 名字 = MANUFACTURER,值 =
					// samsung
					// 11-26 16:31:02.620: V/kan(862): 名字 = MODEL,值 = SM-C101
					// 11-26 16:31:02.620: V/kan(862): 名字 = PRODUCT,值 =
					// mproject3gzn
					// 11-26 16:31:02.620: V/kan(862): 名字 = RADIO,值 = unknown
					// 11-26 16:31:02.620: V/kan(862): 名字 = SERIAL,值 =
					// 4d004dd9b0d87003
					// 11-26 16:31:02.620: V/kan(862): 名字 = TAGS,值 =
					// release-keys
					// 11-26 16:31:02.625: V/kan(862): 名字 = TYPE,值 = user
					// 11-26 16:31:02.625: V/kan(862): 名字 = UNKNOWN,值 = unknown
					// 11-26 16:31:02.625: V/kan(862): 名字 = USER,值 = se.infra

				}
				/*
				 * Set<String> set = infos.keySet(); for(String key:set){
				 * Log.v("kan","key : "+key+", 值 : "+infos.get(key)); }
				 */
				Log.d(TAG, field.getName() + " : " + field.get(null));
			} catch (Exception e) {
				Log.e(TAG, "an error occured when collect crash info", e);
			}
		}
	}
	/**
	 * 保存错误信息到文件中
	 * 
	 * @param ex
	 * @return 返回文件名称,便于将文件传送到服务器
	 */
	private String saveCrashInfo2File(Throwable ex) {

		String SDState = Environment.getExternalStorageState();
		if (SDState.equals(Environment.MEDIA_MOUNTED)) {
			sb = new StringBuffer();
			sb.append("本手机信息:\n");
			// 取出infos中的信息放入字符sb中
			for (Map.Entry<String, String> entry : infos.entrySet()) {
				String key = entry.getKey();
				String value = entry.getValue();
				sb.append(key + "=" + value + "\n");
			}

			Writer writer = new StringWriter();
			PrintWriter printWriter = new PrintWriter(writer);
			ex.printStackTrace(printWriter);
			Throwable cause = ex.getCause();
			while (cause != null) {
				cause.printStackTrace(printWriter);
				cause = cause.getCause();
			}
			printWriter.close();
			String result = writer.toString();
			// 把异常信息加入字符串sb中
			sb.append("异常信息 :\n" + result);
			try {
				long timestamp = System.currentTimeMillis();
				String time = format.format(new Date());
				// 指定文件格式为txt
				String fileName = "crash-" + time + "-" + timestamp + ".txt";
				if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
					File dir = new File(path);
					if (!dir.exists()) {
						dir.mkdirs();
					}
					FileOutputStream fos = new FileOutputStream(path + fileName);
					fos.write(sb.toString().getBytes());
					fos.close();
				}
				return fileName;
			} catch (Exception e) {
				Log.e(TAG, "an error occured while writing file...", e);
			}
		}
		return null;

	}
}

源码:

Android程序异常崩溃的捕捉 前后端 
http://download.youkuaiyun.com/detail/xiaobijia/8220073

异常抛出log备份 - 下载频道 - youkuaiyun.com
http://download.youkuaiyun.com/detail/xiaobijia/8365227






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值