首先创建全局的Application ,此Application全局通用。
package com.demo.utils;
import com.demo.exception.CrashHandler;
import android.app.Application;
/**
* 全局的context,任意位置调用
* @author Administrator
*
*/
public class GlobalApplication extends Application
{
private static GlobalApplication instance;
public static GlobalApplication getInstance()
{
return instance;
}
@Override
public void onCreate()
{
super.onCreate();
instance = this;
CrashHandler crashHandler = CrashHandler.getInstance();//这是收集异常信息的单例类,具体代码请看下文
crashHandler.init(getApplicationContext());//初始化
}
}
注意:上面的代码需要在注册清单文件中注册。部分清单如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.demo.test"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" />
<!-- 网络 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 访问sdcard -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 获取mac需要的网络状态 -->
<uses-permission android:name="andorid.permission.CHANGE_CONFIGURATION" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<application
android:name="com.demo.utils.GlobalApplication"
android:allowBackup="true"
android:icon="@drawable/app_icon"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.demo.activity.MyActivity"
android:launchMode="singleTask"
android:screenOrientation="landscape"
android:theme="@android:style/Theme.NoTitleBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</activity>
</application>
</manifest>
crashHandler
package com.demo.exception;
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 android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Looper;
import com.demo.utils.ConstantUtils;
import com.demo.utils.PathUtils;
import com.demo.utils.log.MyLog;
/**
* UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
*
* @author user
* 实现
UncaughtExceptionHandler接口
*/
public class CrashHandler implements UncaughtExceptionHandler
{
public static final String TAG = "bug";
//系统默认的UncaughtException处理类
private Thread.UncaughtExceptionHandler mDefaultHandler;
//CrashHandler实例
private static CrashHandler INSTANCE = new CrashHandler();
//程序的Context对象
private Context mContext;
//用来存储设备信息和异常信息
private Map<String, String> infos = new HashMap<String, String>();
//用于格式化日期,作为日志文件名的一部分
private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
/** 保证只有一个CrashHandler实例 */
private CrashHandler()
{
}
/** 获取CrashHandler实例 ,单例模式 */
public static CrashHandler getInstance()
{
return INSTANCE;
}
/**
* 初始化
*
* @param context
*/
public void init(Context context)
{
mContext = context;
//获取系统默认的UncaughtException处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
//设置该CrashHandler为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* 当UncaughtException发生时会转入该函数来处理
*/
@Override
public void uncaughtException(Thread thread, Throwable ex)
{
if (!handleException(ex) && mDefaultHandler != null)
{
//如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex);
}
else
{
try
{
Thread.sleep(3000);
}
catch (InterruptedException e)
{
MyLog.e(TAG, "error : ", e);
}
mDefaultHandler.uncaughtException(thread, ex);
//退出程序
// android.os.Process.killProcess(android.os.Process.myPid());
// System.exit(1);
}
}
/**
* 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
*
* @param ex
* @return true:如果处理了该异常信息;否则返回false.
*/
private boolean handleException(Throwable ex)
{
if (ex == null)
{
return false;
}
//使用Toast来显示异常信息
new Thread()
{
@Override
public void run()
{
Looper.prepare();
// Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
//收集设备参数信息
collectDeviceInfo(mContext);
//保存日志文件
MyLog.i(TAG, "ex:" + ex.toString() + "--" + ex.getLocalizedMessage());
saveCrashInfo2File(ex);
return true;
}
/**
* 收集设备参数信息
* @param ctx
*/
public void collectDeviceInfo(Context ctx)
{
try
{
PackageManager pm = ctx.getPackageManager();
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null)
{
String versionName = pi.versionName == null ? "null" : pi.versionName;
String versionCode = pi.versionCode + "";
infos.put("versionName", versionName);
infos.put("versionCode", versionCode);
}
}
catch (NameNotFoundException e)
{
MyLog.e(TAG, "an error occured when collect package info", e);
}
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields)
{
try
{
field.setAccessible(true);
}
catch (Exception e)
{
MyLog.e(TAG, "an error occured when collect crash info", e);
}
}
}
/**
* 保存错误信息到文件中
*
* @param ex
* @return 返回文件名称,便于将文件传送到服务器
*/
private String saveCrashInfo2File(Throwable ex)
{
StringBuffer sb = new StringBuffer();
// 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)
{
MyLog.i(TAG, "cause:" + cause.toString() + "--");
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
MyLog.i(TAG, "result:" + result);
sb.append(result);
try
{
long timestamp = System.currentTimeMillis();
String time = formatter.format(new Date());
String fileName = "crash-" + time + "-" + timestamp + ".log";
if (ConstantUtils.isOnline)
{
fileName = "crash-online.log";
}
String path = PathUtils.BUGPATH;
FileOutputStream fos = new FileOutputStream(path + fileName);
fos.write(sb.toString().getBytes());
fos.close();
return fileName;
}
catch (Exception e)
{
MyLog.e(TAG, "an error occured while writing file...", e);
}
return null;
}
}
上面信息涉及到下面的几个类ConstantUtils,PathUtils和MyLog
constantUtils
public class ConstantUtils
{
/***是否上线版本***/
public final static boolean isOnline = false;
/***
* 时间格式
*/
public static String timeFormat = "yyyy-MM-dd HH:mm:ss";
}
MyLog
package com.demo.utils.log;
import java.text.SimpleDateFormat;
import java.util.Date;
import android.util.Log;
import com.demo.utils.ConstantUtils;
import com.demo.utils.PathUtils;
public class MyLog
{
private static String filename = "log.txt";
/***
* 打印日志
* @param tag
* @param msg
*/
public static void d(String tag, String msg)
{
if (!ConstantUtils.isOnline)
{
Log.d(tag, msg);
}
}
/***
* 打印日志
* @param tag
* @param msg
*/
public static void i(String tag, String msg)
{
if (!ConstantUtils.isOnline)
{
Log.i(tag, msg);
}
}
/***
* 打印日志
* @param tag
* @param msg
*/
public static void e(String tag, String msg)
{
if (!ConstantUtils.isOnline)
{
Log.e(tag, msg);
}
}
/***
* 打印日志
* @param tag
* @param msg
*/
public static void e(String tag, String msg, Throwable tr)
{
if (!ConstantUtils.isOnline)
{
Log.e(tag, msg, tr);
}
}
/***
* 打印日志
* @param tag
* @param msg
*/
public static void w(String tag, String msg)
{
if (!ConstantUtils.isOnline)
{
Log.w(tag, msg);
}
}
/***
* 写入文本到日志文件中
* @param value
*/
public static void write(String value)
{
if (!ConstantUtils.isOnline)
{
String newValue = getDataFormat(System.currentTimeMillis()) + " " + value;
LogUtils.getInstances().write(PathUtils.LOGPATH, filename, newValue);
}
}
/***
* 获取当前时间
* @param timeInMillis
* @return
*/
private static String getDataFormat(long timeInMillis)
{
SimpleDateFormat dataFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
return dataFormat.format(new Date(timeInMillis));
}
}
pathutils
package com.demo.utils;
import java.io.File;
import android.os.Environment;
public class PathUtils
{
public final static String sdcardPath = Environment.getExternalStorageDirectory() + "";
public final static String PATH = sdcardPath + "/exception/" + AppUtils.getAppPackageName();
public final static String BUGPATH = PATH + "/bug/";
public final static String LOGPATH = PATH + "/log/";
public PathUtils()
{
File PATH = new File(PathUtils.PATH);
if (!PATH.exists())
{
PATH.mkdirs();
}
File LOGPATH = new File(PathUtils.LOGPATH);
if (!LOGPATH.exists())
{
LOGPATH.mkdirs();
}
File BUGPATH = new File(PathUtils.BUGPATH);
if (!BUGPATH.exists())
{
BUGPATH.mkdirs();
}
}
}