Android编程实现捕获程序异常退出时的错误log信息功能详解

本文实例讲述了Android编程实现捕获程序异常退出时的错误log信息功能。分享给大家供大家参考,具体如下:

很多时候我们程序无缘无故的就挂掉了,让我们一头雾水,如果刚好我们在调试,那我们可以通过错误log来查看是什么原因引起的程序崩溃。但是当我们把程序发别人使用时,就没那么好运了,那我们要怎么样才能捕获到那个错误异常呢?还好Android给我们提供了UncaughtExceptionHandler 这个类,我们可以通过实现这个类的接口,来全局捕获那个让程序崩掉的错误log信息。可以将错误的log保存在本地,也可以发送给服务器后台。下面来看下UncaughtExceptionHandler 的实现类CrashHandler吧。

CrashHandler.Java

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.Locale;
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;
public class CrashHandler implements UncaughtExceptionHandler {
 private static final String TAG = CrashHandler.class.getSimpleName();
 private static final String SINGLE_RETURN = "\n";
 private static final String SINGLE_LINE = "--------------------------------";
 private static CrashHandler mCrashHandler;
 private Context mContext;
 private UncaughtExceptionHandler mDefaultHandler;
 private StringBuffer mErrorLogBuffer = new StringBuffer();
 /**
  * 获取CrashHandler实例,单例模式。
  *
  * @return 返回CrashHandler实例
  */
 public static CrashHandler getInstance() {
  if (mCrashHandler == null) {
   synchronized (CrashHandler.class) {
    if (mCrashHandler == null) {
     mCrashHandler = new CrashHandler();
    }
   }
  }
  return mCrashHandler;
 }
 public void init(Context context) {
  mContext = context;
  // 获取系统默认的uncaughtException处理类实例
  mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
  // 设置成我们处理uncaughtException的类
  Thread.setDefaultUncaughtExceptionHandler(this);
 }
 @Override
 public void uncaughtException(Thread thread, Throwable ex) {
  Log.d(TAG, "uncaughtException:" + ex);
  if (!handleException(ex) && mDefaultHandler != null) {
   // 如果用户没有处理异常就由系统默认的异常处理器来处理
   mDefaultHandler.uncaughtException(thread, ex);
  } else {
   try {
    Thread.sleep(3000);
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   android.os.Process.killProcess(android.os.Process.myPid());
  }
 }
 //处理异常事件
 private boolean handleException(Throwable ex) {
  if (ex == null) {
   return false;
  }
  new Thread(new Runnable() {
   @Override
   public void run() {
    Looper.prepare();
    Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_SHORT)
      .show();
    Looper.loop();
   }
  }).start();
  // 收集设备参数信息
  collectDeviceInfo(mContext);
  // 收集错误日志
  collectCrashInfo(ex);
  // 保存错误日志
  saveErrorLog();
  //TODO: 这里可以加一个网络的请求,发送错误log给后台
//  sendErrorLog();
  return true;
 }
 //保存日志到/mnt/sdcard/AppLog/目录下,文件名已时间yyyy-MM-dd_hh-mm-ss.log的形式保存
 private void saveErrorLog() {
  if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
   SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh-mm-ss", Locale.getDefault());
   String format = sdf.format(new Date());
   format += ".log";
   String path = Environment.getExternalStorageDirectory().getPath()+"/AppLog/";
   File file = new File(path);
   if (!file.exists()){
    file.mkdirs();
   }
   FileOutputStream fos = null;
   try {
    fos = new FileOutputStream(path+format);
    fos.write(mErrorLogBuffer.toString().getBytes());
    fos.flush();
   } catch (FileNotFoundException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace();
   } finally {
    if (fos != null) {
     try {
      fos.close();
      fos = null;
     } catch (IOException e) {
      e.printStackTrace();
     }
    }
   }
  }
 }
 //收集错误信息
 private void collectCrashInfo(Throwable ex) {
  Writer info = new StringWriter();
  PrintWriter printWriter = new PrintWriter(info);
  ex.printStackTrace(printWriter);
  Throwable cause = ex.getCause();
  while (cause != null) {
   cause.printStackTrace(printWriter);
   cause = cause.getCause();
  }
  String result = info.toString();
  printWriter.close();
  //将错误信息加入mErrorLogBuffer中
  append("", result);
  mErrorLogBuffer.append(SINGLE_LINE + SINGLE_RETURN);
  Log.d(TAG, "saveCrashInfo2File:" + mErrorLogBuffer.toString());
 }
 //收集应用和设备信息
 private void collectDeviceInfo(Context context) {
  //每次使用前,清掉mErrorLogBuffer里的内容
  mErrorLogBuffer.setLength(0);
  mErrorLogBuffer.append(SINGLE_RETURN + SINGLE_LINE + SINGLE_RETURN);
  //获取应用的信息
  PackageManager pm = context.getPackageManager();
  try {
   PackageInfo pi = pm.getPackageInfo(context.getPackageName(),
     PackageManager.GET_ACTIVITIES);
   if (pi != null) {
    append("versionCode", pi.versionCode);
    append("versionName", pi.versionName);
    append("packageName", pi.packageName);
   }
  } catch (NameNotFoundException e) {
   e.printStackTrace();
  }
  mErrorLogBuffer.append(SINGLE_LINE + SINGLE_RETURN);
  //获取设备的信息
  Field[] fields = Build.class.getDeclaredFields();
  getDeviceInfoByReflection(fields);
  fields = Build.VERSION.class.getDeclaredFields();
  getDeviceInfoByReflection(fields);
  mErrorLogBuffer.append(SINGLE_LINE + SINGLE_RETURN);
 }
 //获取设备的信息通过反射方式
 private void getDeviceInfoByReflection(Field[] fields) {
  for (Field field : fields) {
   try {
    field.setAccessible(true);
    append(field.getName(), field.get(null));
   } catch (IllegalArgumentException e) {
    e.printStackTrace();
   } catch (IllegalAccessException e) {
    e.printStackTrace();
   }
  }
 }
 //mErrorLogBuffer添加友好的log信息
 private void append(String key, Object value) {
  mErrorLogBuffer.append("" + key + ":" + value + SINGLE_RETURN);
 }
}

在application中的使用非常简单,只要init就好了,之后我们就只要等异常出现吧。

CrashApplication.java

import android.app.Application;
public class CrashApplication extends Application{
 @Override
 public void onCreate() {
  super.onCreate();
  CrashHandler.getInstance().init(this);
 }
}

不要忘记在AndroidManifest.xml声明我们的CrashApplication 。

AndroidManifest.xml

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
  android:allowBackup="true"
  android:name=".CrashApplication"
  android:icon="@drawable/ic_launcher"
  android:label="@string/app_name"
  android:theme="@style/AppTheme" >
  <activity
   android:name="com.example.crashtestdemo.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>

更多关于Android相关内容感兴趣的读者可查看本站专题:《Android开发入门与进阶教程》、《Android调试技巧与常见问题解决方法汇总》、《Android编程之activity操作技巧总结》、《Android操作json格式数据技巧总结》、《Android数据库操作技巧总结》、《Android文件操作技巧汇总》、《Android资源操作技巧汇总》及《Android控件用法总结》

希望本文所述对大家Android程序设计有所帮助。

(0)

相关推荐

  • Android编程实现Toast只显示最后一条的方法

    本文实例讲述了Android编程实现Toast只显示最后一条的方法.分享给大家供大家参考,具体如下: 在做Android开发中,时不时的可能会用到Toast,但用Toast的时候,连续使用会存在一个问题,就是一条条显示Toast.而不是直接显示最后一条.因此,根据此需求,现在写了ToastUtil这个类,该类中有三个方法供选择. ToastUtil.Java import android.content.Context; import android.graphics.PixelFormat;

  • Android实现屏幕各尺寸的获取的示例

    在开发中我们会遇到各种需要获得屏幕参数的场景,当中也有不少坑,所以现在就记录一下这些参数的获取方式.以免再入坑. 物理屏幕宽高 一.底部没有虚拟按键 这里获取到的宽高,就是你眼睛能看到的,屏幕亮着的地方的宽高. /** * 获取屏幕的宽 * * @param context * @return */ public static int getScreenWidth(Context context) { WindowManager wm = (WindowManager) context.getS

  • Android编程实现播放视频时切换全屏并隐藏状态栏的方法

    本文实例讲述了Android编程实现播放视频时切换全屏并隐藏状态栏的方法.分享给大家供大家参考,具体如下: 1. Demo示例: @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (this.getResources().getConfiguration().ori

  • Android开发之全屏与非全屏的切换设置方法小结

    本文实例讲述了Android开发之全屏与非全屏的切换设置方法.分享给大家供大家参考,具体如下: 静态方法 1. 代码方式 在Activity类OnCreate方法中设置,代码如下 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().se

  • Android MTU 值修改的实例详解

    Android MTU 值修改的实例详解 通信术语 最大传输单元(Maximum Transmission Unit,MTU)是指一种通信协议的某一层上面所能通过的最大数据包大小(以字节为单位).最大传输单元这个参数通常与通信接口有关(网络接口卡.串口等). 1.首先使用 adb 命令进入系统,然后 ifconfig 查看可用网络 C:\>adb shell $ su su # ifconfig ifconfig lo Link encap:Local Loopback inet addr:12

  • Android编程实现捕获程序异常退出时的错误log信息功能详解

    本文实例讲述了Android编程实现捕获程序异常退出时的错误log信息功能.分享给大家供大家参考,具体如下: 很多时候我们程序无缘无故的就挂掉了,让我们一头雾水,如果刚好我们在调试,那我们可以通过错误log来查看是什么原因引起的程序崩溃.但是当我们把程序发别人使用时,就没那么好运了,那我们要怎么样才能捕获到那个错误异常呢?还好Android给我们提供了UncaughtExceptionHandler 这个类,我们可以通过实现这个类的接口,来全局捕获那个让程序崩掉的错误log信息.可以将错误的lo

  • Android编程实现全局获取Context及使用Intent传递对象的方法详解

    本文实例讲述了Android编程实现全局获取Context及使用Intent传递对象的方法.分享给大家供大家参考,具体如下: 一.全局获取 Context Android 开发中很多地方需要用到 Context,比如弹出 Toast.启动活动.发送广播.操作数据库-- 由于很多操作都是在活动中进行的,而活动本身就是一个 Context 对象,所以获取 Context 并不是那么困难. 但是,当应用程序的架构逐渐开始复杂起来的时候,很多的逻辑代码都将脱离 Activity 类,由此在某些情况下,获

  • Android编程实现项目中异常捕获及对应Log日志文件保存功能

    本文实例讲述了Android编程实现项目中异常捕获及对应Log日志文件保存功能.分享给大家供大家参考,具体如下: 做程序开发,肯定离不开与BUG打交道,更加离不开程序异常的出现.在开发的时候,我们可以通断点调试,日志打印,异常捕获工具等方式发现或处理程序中的Exception.那客户在使用我们的应用时,程序了问题,我们怎么可以知道呢?当然,我们可以加上友盟统计等第三方工具.另外还能怎么做呢?那就是把异常信息通过文档地形式保存下来,如果用户在使用的时候程序出了异常,可以让用户把对应的日志信息发给我

  • C#程序异常关闭时的捕获

    本文主要以一个简单的小例子,描述C# Winform程序异常关闭时,如何进行捕获,并记录日志. 概述 有时在界面的事件中,明明有try... catch 进行捕获异常,但是还是会有异常关闭的情况,所以在程序中如何最终的记录一些无法捕获的异常,会大大方便问题的定位分析及程序优化. 涉及知识点 以下两个异常事件,主要应用不同的场景. Application.ThreadException 在发生应用程序UI主线程中未捕获线程异常时发生,触发的事件. AppDomain.CurrentDomain.U

  • Android编程实现应用程序开机自启动的方法

    本文实例讲述了Android编程实现应用程序开机自启动的方法.分享给大家供大家参考,具体如下: Android在开机时自动启动一个应用程序 在启动时自动启动一个应用程序: 1.AndroidManifest.xml <receiver android:enabled="true" android:name=".BootUpReceiver" android:permission="android.permission.RECEIVE_BOOT_COM

  • Android编程实现在一个程序中启动另一个程序的方法

    本文实例讲述了Android编程实现在一个程序中启动另一个程序的方法.分享给大家供大家参考,具体如下: Android 开发有时需要在一个应用中启动另一个应用,比如Launcher加载所有的已安装的程序的列表,当点击图标时可以启动另一个应用. 一般我们知道了另一个应用的包名和MainActivity的名字之后便可以直接通过如下代码来启动: Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEG

  • Android编程设定activity进入和退出效果的方法

    本文实例讲述了Android编程设定activity进入和退出效果的方法.分享给大家供大家参考,具体如下: 看了android的源代码和资源文件,终于明白如何去修改设置Dialog和Activity的进入和退出效果了.设置Dialog首先通过getWindow()方法获取它的窗口,然后通过getAttributes()方法获得window的WindowManager.LayoutParams lp, lp有个公共属性windowAnimations, 只要把要实现的animation的id赋值给

  • Android编程简单实现ImageView点击时背景图修改的方法

    本文实例讲述了Android编程简单实现ImageView点击时背景图修改的方法.分享给大家供大家参考,具体如下: 在使用ImageView时,当被点击时,希望背景图修改一下,这样显示被点击效果明显一些.在这里,一个很简单的方法,最起码是个很清晰的方法.在res/drawable文件夹下创建一个xml文件.比如my.xml,内容如下: <?xml version="1.0" encoding="utf-8"?> <selector xmlns:an

  • Android编程判断应用程序是否已安装的方法

    本文实例讲述了Android编程判断应用程序是否已安装的方法.分享给大家供大家参考,具体如下: 这里以twitter程序为例,假设twitter程序包名为"com.twitter.android".这时候可用以下代码来判断是否被安装. PackageInfo packageInfo; try { packageInfo = this.getPackageManager().getPackageInfo( "com.twitter.android", 0); } ca

  • android编程实现为程序创建快捷方式的方法

    本文实例讲述了android编程实现为程序创建快捷方式的方法.分享给大家供大家参考,具体如下: /** * 为程序创建桌面快捷方式 */ private void addShortcut(){ Intent shortcut = new Intent("com.android.launcher.action.INSTALL_SHORTCUT"); //快捷方式的名称 shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, getString(R.st

随机推荐