`
1320438999
  • 浏览: 122562 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
文章分类
社区版块
存档分类
最新评论

Android之将logcat信息保存本地

 
阅读更多

大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了崩溃现象,开发者应该及时获取在该设备上导致崩溃的信息,这对于下一个版本的bug修复帮助极大,所以今天就来介绍一下如何在程序崩溃的情况下收集相关的设备参数信息和具体的异常信息,并发送这些信息到服务器供开发者分析和调试程序。

源码下载地址http://download.csdn.net/detail/weidi1989/4588310

我们先建立一个crash项目,项目结构如图:

在MainActivity.java代码中,代码是这样写的:

package com.scott.crash;

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

public class MainActivity extends Activity {

	private String s;
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        System.out.println(s.equals("any string"));
    }
}

我们在这里故意制造了一个潜在的运行期异常,当我们运行程序时就会出现以下界面:

遇到软件没有捕获的异常之后,系统会弹出这个默认的强制关闭对话框。

我们当然不希望用户看到这种现象,简直是对用户心灵上的打击,而且对我们的bug的修复也是毫无帮助的。我们需要的是软件有一个全局的异常捕获器,当出现一个我们没有发现的异常时,捕获这个异常,并且将异常信息记录下来,上传到服务器公开发这分析出现异常的具体原因。

接下来我们就来实现这一机制,不过首先我们还是来了解以下两个类:android.app.Application和java.lang.Thread.UncaughtExceptionHandler。

Application:用来管理应用程序的全局状态。在应用程序启动时Application会首先创建,然后才会根据情况(Intent)来启动相应的Activity和Service。本示例中将在自定义加强版的Application中注册未捕获异常处理器。

Thread.UncaughtExceptionHandler:线程未捕获异常处理器,用来处理未捕获异常。如果程序出现了未捕获异常,默认会弹出系统中强制关闭对话框。我们需要实现此接口,并注册为程序中默认未捕获异常处理。这样当未捕获异常发生时,就可以做一些个性化的异常处理操作。

大家刚才在项目的结构图中看到的CrashHandler.java实现了Thread.UncaughtExceptionHandler,使我们用来处理未捕获异常的主要成员,代码如下:

package com.way.crash;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
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.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.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

/**
 * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告.
 * 
 * @author way
 * 
 */
public class CrashHandler implements UncaughtExceptionHandler {
	private static final String TAG = "CrashHandler";
	private Thread.UncaughtExceptionHandler mDefaultHandler;// 系统默认的UncaughtException处理类
	private static CrashHandler INSTANCE = new CrashHandler();// CrashHandler实例
	private Context mContext;// 程序的Context对象
	private Map<String, String> info = new HashMap<String, String>();// 用来存储设备信息和异常信息
	private SimpleDateFormat format = 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;
		mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 获取系统默认的UncaughtException处理器
		Thread.setDefaultUncaughtExceptionHandler(this);// 设置该CrashHandler为程序的默认处理器
	}

	/**
	 * 当UncaughtException发生时会转入该重写的方法来处理
	 */
	public void uncaughtException(Thread thread, Throwable ex) {
		if (!handleException(ex) && mDefaultHandler != null) {
			// 如果自定义的没有处理则让系统默认的异常处理器来处理
			mDefaultHandler.uncaughtException(thread, ex);
		} else {
			try {
				Thread.sleep(3000);// 如果处理了,让程序继续运行3秒再退出,保证文件保存并上传到服务器
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			// 退出程序
			android.os.Process.killProcess(android.os.Process.myPid());
			System.exit(1);
		}
	}

	/**
	 * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
	 * 
	 * @param ex
	 *            异常信息
	 * @return true 如果处理了该异常信息;否则返回false.
	 */
	public boolean handleException(Throwable ex) {
		if (ex == null)
			return false;
		new Thread() {
			public void run() {
				Looper.prepare();
				Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出", 0).show();
				Looper.loop();
			}
		}.start();
		// 收集设备参数信息
		collectDeviceInfo(mContext);
		// 保存日志文件
		saveCrashInfo2File(ex);
		return true;
	}

	/**
	 * 收集设备参数信息
	 * 
	 * @param context
	 */
	public void collectDeviceInfo(Context context) {
		try {
			PackageManager pm = context.getPackageManager();// 获得包管理器
			PackageInfo pi = pm.getPackageInfo(context.getPackageName(),
					PackageManager.GET_ACTIVITIES);// 得到该应用的信息,即主Activity
			if (pi != null) {
				String versionName = pi.versionName == null ? "null"
						: pi.versionName;
				String versionCode = pi.versionCode + "";
				info.put("versionName", versionName);
				info.put("versionCode", versionCode);
			}
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}

		Field[] fields = Build.class.getDeclaredFields();// 反射机制
		for (Field field : fields) {
			try {
				field.setAccessible(true);
				info.put(field.getName(), field.get("").toString());
				Log.d(TAG, field.getName() + ":" + field.get(""));
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		}
	}

	private String saveCrashInfo2File(Throwable ex) {
		StringBuffer sb = new StringBuffer();
		for (Map.Entry<String, String> entry : info.entrySet()) {
			String key = entry.getKey();
			String value = entry.getValue();
			sb.append(key + "=" + value + "\r\n");
		}
		Writer writer = new StringWriter();
		PrintWriter pw = new PrintWriter(writer);
		ex.printStackTrace(pw);
		Throwable cause = ex.getCause();
		// 循环着把所有的异常信息写入writer中
		while (cause != null) {
			cause.printStackTrace(pw);
			cause = cause.getCause();
		}
		pw.close();// 记得关闭
		String result = writer.toString();
		sb.append(result);
		// 保存文件
		long timetamp = System.currentTimeMillis();
		String time = format.format(new Date());
		String fileName = "crash-" + time + "-" + timetamp + ".log";
		if (Environment.getExternalStorageState().equals(
				Environment.MEDIA_MOUNTED)) {
			try {
				File dir = new File(Environment.getExternalStorageDirectory()
						+ "/crash/");
				Log.i("CrashHandler", dir.toString());
				if (!dir.exists())
					dir.mkdir();
				FileOutputStream fos = new FileOutputStream(dir + "/"
						+ fileName);
				fos.write(sb.toString().getBytes());
				fos.close();
				return fileName;
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return null;
	}
}

最后,为了让我们的CrashApplication取代android.app.Application的地位,在我们的代码中生效,我们需要修改AndroidManifest.xml:

<application android:name=".CrashApplication" ...>
</application>

因为我们上面的CrashHandler中,遇到异常后要保存设备参数和具体异常信息到SDCARD,所以我们需要在AndroidManifest.xml中加入读写SDCARD权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

搞定了上边的步骤之后,我们来运行一下这个项目:

看以看到,并不会有强制关闭的对话框出现了,取而代之的是我们比较有好的提示信息。

然后看一下SDCARD生成的文件:

用文本编辑器打开日志文件,看一段日志信息:

CPU_ABI=armeabi
CPU_ABI2=unknown
ID=FRF91
MANUFACTURER=unknown
BRAND=generic
TYPE=eng
......
Caused by: java.lang.NullPointerException
	at com.scott.crash.MainActivity.onCreate(MainActivity.java:13)
	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047)
	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627)
	... 11 more

这些信息对于开发者来说帮助极大,所以我们需要将此日志文件上传到服务器。







分享到:
评论

相关推荐

    Android studio保存logcat日志到本地的操作

    主要介绍了Android studio保存logcat日志到本地的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧

    Android保存App异常信息到本地

    本文实例为大家分享了Android保存App异常信息到本地的具体代码,供大家参考,具体内容如下 首先添加权限 &lt;uses android:name=android.permission.WRITE_EXTERNAL_STORAGE/&gt; 代码 // 调用该方法造成异常 private ...

    android保存崩溃时的错误信息log至本地【源码+jar包】使用说明在eoe

    使用说明详见 http://www.eoeandroid.com/thread-265366-1-1.html

    Android将应用调试log信息保存在SD卡的方法

    把自己应用的调试信息写入到SD卡中。 package com.sdmc.hotel.util; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java...

    新版Android开发教程.rar

    这一联盟将会支持 Google 发布的 Android 手机操作系统或者应用软件,共同开发名为 Android 的 开 放源代码的移动系统。开放手机联盟包括手机制造商、手机芯片厂商和移动运营商几类。目前,联盟成员 数 量已经达到了...

    Android native code crash分析工具stack

    出现crash以后,用logcat -s DEBUG把crash的log打印保存下来,例如crash_log.txt; 然后执行 ndk-stack -sym 符号所在的目录 -dump crash_log.txt; 例如ndk-stack -sym out/target/product/mt5520_...

    安卓端 adb shell debug toolbox.apk文件,通过安卓apk进行adb命令操作

    4.支持本地shell adb。 5.支持联想输入。 6.支持自动保存输出。 7.支持与朋友分享输出。 8.支持命令历史。 9.支持快速复制命令。 10.支持多窗口。 11.支持彩色文字。 12.支持后台运行。 13.支持推荐命令。 14.支持...

    Logger:适用于Android的有用记录器

    error 级别和应用崩溃日志保存至本地文件。Logcat 后台打印 Http 日志,屏蔽了文件流打印乱码。使用方法1、引入依赖implementation 'com.wuzy:logger:1.0.0'2、在 Application 中初始化:L.init(tag, isLoggable, ...

    Elasticsearch、Logstash、Kibana安装资源包

    本地用android studio查看日志logcat直接查看,很是方便,但是如果把软件安装在非开发环境怎么查看日志呢? 保存成txt查看?如果非要实时查看做进一步的数据分析呢? 找了很久的解决方案,终于找到了elk ELK 是一...

Global site tag (gtag.js) - Google Analytics