在写Android程序时,经常碰到在模拟器和调试器中无法捕捉的exception。有时自己运行好好的程序到了其他机器上就出现了问题。虽然Google Play有错误堆栈上传功能,但是没有办法把整个运行的过程记录下来。为此我写了一个LogDog类来试图解决这个问题。





import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.lang.Thread.UncaughtExceptionHandler; import java.sql.Date; import java.text.SimpleDateFormat; import java.util.HashSet;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.Environment; import android.os.Looper; //import android.util.Log; import android.util.Log;public class LogDog implements UncaughtExceptionHandler {public static String TAG = "LogDog";private static HashSet _tags = new HashSet();private static boolean _debug = false;static {// add tags to be captured_tags.add(PDFMaster.TAG);_tags.add(RecentlyUsedActivity.TAG);_tags.add(DB.TAG);}public static LogDog instance() {return _instance;}public static void i(String tag, String message) {if(_debug) {Log.i(tag, message);return;}PrintWriter writer = _instance.getWriter();if(writer != null && _tags.contains(tag)) {_instance._writer.println("i:" + tag + ":" + message);}}public static void e(String tag, String message) {if(_debug) {Log.i(tag, message);return;}PrintWriter writer = _instance.getWriter();if(writer != null && _tags.contains(tag)) {_instance._writer.println("e" + tag + ":" + message);}}public static void w(String tag, String message) {if(_debug) {Log.i(tag, message);return;}PrintWriter writer = _instance.getWriter();if(writer != null && _tags.contains(tag)) {_instance._writer.println("w" + tag + ":" + message);}}public void init(Context context) {_context = context;_fileName = _context.getString(R.string.app_name) + ".log";_defaultHandler = Thread.getDefaultUncaughtExceptionHandler();Thread.setDefaultUncaughtExceptionHandler(this);}public void close() {if(_writer != null) {_writer.close();_writer = null;}}// privateprivate static LogDog _instance = new LogDog();private static final String PATH =Environment.getExternalStorageDirectory().getPath() + "/log";private Context _context;private String _fileName;private Thread.UncaughtExceptionHandler _defaultHandler;private PrintWriter _writer = null;private LogDog() {}private PrintWriter getWriter() {if(_writer != null)return _writer;// check if we have SD cardif (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {//Log.w(TAG, "sdcard unmounted,skip dump exception");return null;}File dir = new File(PATH);if (!dir.exists()) {dir.mkdirs();}File file = new File(PATH + "/" + _fileName);try {_writer = new PrintWriter(new BufferedWriter(new FileWriter(file)));} catch (IOException e) {//Log.e(TAG, "Failed to open " + PATH + "/" + FILE_NAME);_writer = null;}return _writer;}@Overridepublic void uncaughtException(Thread thread, Throwable ex) {// first display a toast messagenew Thread() {@Overridepublic void run() {Looper.prepare();PDFMarkerApp.instance().showToast("Sorry for the trouble, dumping uncaught exeption to SD card");}}.start();try {dump(ex);} catch (Exception e) {//Log.e(TAG, "Failed to dump because " + e.getMessage());e.printStackTrace();}if (_defaultHandler != null) { _defaultHandler.uncaughtException(thread, ex); } else {// sleep so the toast can have time to displaytry {Thread.sleep(3000);} catch (InterruptedException e) {}android.os.Process.killProcess(android.os.Process.myPid()); }}private void dump(Throwable ex) throws IOException, NameNotFoundException {PrintWriter writer = _instance.getWriter();if(writer == null)return;long current = System.currentTimeMillis();String time = new SimpleDateFormat("yyyyMMdd.HHmmss").format(new Date(current));_writer.println(time);PackageManager pm = _context.getPackageManager();PackageInfo pi = pm.getPackageInfo(_context.getPackageName(), PackageManager.GET_ACTIVITIES);_writer.print("App Version: ");_writer.print(pi.versionName);_writer.print('_');_writer.println(pi.versionCode); _writer.print("OS Version: ");_writer.print(Build.VERSION.RELEASE);_writer.print("_");_writer.println(Build.VERSION.SDK_INT);_writer.print("Vendor: ");_writer.println(Build.MANUFACTURER);_writer.print("Model: ");_writer.println(Build.MODEL);_writer.print("CPU ABI: ");_writer.println(Build.CPU_ABI);_writer.println();ex.printStackTrace(_writer);close();File file = new File(PATH + "/" + _fileName);File file2 = new File(PATH + "/" + _fileName + "." + time + ".txt");file.renameTo(file2);}}

在使用时所有的日志就全记录在一个文件中,直到最后的异常。每次异常都会单独被记录在一个文件中,连带所有需要知道的信息。要是正常运行,这个文件下次就被覆盖,不会造成空间问题。要是调试,只要把_debug改一下,所有日志就在LogCat中显示出来,非常方便。