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

发布时间 - 2026-01-11 03:02:56    点击率:

本文实例讲述了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程序设计有所帮助。


# Android  # 捕获  # 程序  # 异常  # 退出  # 错误  # log  # 信息  # Android获取apk程序签名信息代码示例  # Android实现获取应用程序相关信息列表的方法  # Android开发之在程序中时时获取logcat日志信息的方法(附demo源码下载)  # 如何判断软件程序是否联网 联网状态提示信息Android实现  # Android实现整理PackageManager获取所有安装程序信息  # Android ApplicationInfo 应用程序信息的详解  # Android编程获取APP应用程序基本信息辅助类【APP名称、包名、图标  # 版本号等】  # Android获取手机型号/系统版本号/App版本号等信息实例讲解  # Android开发获取系统中已安装程序信息的方法  # 操作技巧  # 错误信息  # 进阶  # 相关内容  # 让我们  # 给我们  # 感兴趣  # 我们可以  # 就没  # 可以通过  # 给大家  # 掉了  # 当我们  # 无缘无故  # 更多关于  # 解决方法  # 所述  # 很抱歉  # 程序设计  # 就由 


相关栏目: 【 网站优化151355 】 【 网络推广146373 】 【 网络技术251813 】 【 AI营销90571


相关推荐: 南京网站制作费用,南京远驱官方网站?  公司网站制作价格怎么算,公司办个官网需要多少钱?  独立制作一个网站多少钱,建立网站需要花多少钱?  Python数据仓库与ETL构建实战_Airflow调度流程详解  免费视频制作网站,更新又快又好的免费电影网站?  哪家制作企业网站好,开办像阿里巴巴那样的网络公司和网站要怎么做?  Laravel怎么生成二维码图片_Laravel集成Simple-QrCode扩展包与参数设置【实战】  微信小程序 闭包写法详细介绍  Swift中switch语句区间和元组模式匹配  Laravel怎么连接多个数据库_Laravel多数据库连接配置  Windows11怎样设置电源计划_Windows11电源计划调整攻略【指南】  Laravel如何实现数据导出到PDF_Laravel使用snappy生成网页快照PDF【方案】  laravel怎么通过契约(Contracts)编程_laravel契约(Contracts)编程方法  如何在阿里云高效完成企业建站全流程?  Laravel怎么实现前端Toast弹窗提示_Laravel Session闪存数据Flash传递给前端【方法】  佐糖AI抠图怎样调整抠图精度_佐糖AI精度调整与放大细化操作【攻略】  如何在阿里云完成域名注册与建站?  jimdo怎样用html5做选项卡_jimdo选项卡html5实现与切换效果【指南】  canvas 画布在主流浏览器中的尺寸限制详细介绍  如何用低价快速搭建高质量网站?  Laravel Eloquent性能优化技巧_Laravel N+1查询问题解决  Laravel Session怎么存储_Laravel Session驱动配置详解  如何在建站之星绑定自定义域名?  微信推文制作网站有哪些,怎么做微信推文,急?  网站设计制作书签怎么做,怎样将网页添加到书签/主页书签/桌面?  如何将凡科建站内容保存为本地文件?  米侠浏览器网页背景异常怎么办 米侠显示修复  Laravel怎么配置自定义表前缀_Laravel数据库迁移与Eloquent表名映射【步骤】  php结合redis实现高并发下的抢购、秒杀功能的实例  今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】  阿里云网站搭建费用解析:服务器价格与建站成本优化指南  js实现获取鼠标当前的位置  mc皮肤壁纸制作器,苹果平板怎么设置自己想要的壁纸我的世界?  Python文件异常处理策略_健壮性说明【指导】  javascript基于原型链的继承及call和apply函数用法分析  Laravel如何使用Laravel Vite编译前端_Laravel10以上版本前端静态资源管理【教程】  零基础网站服务器架设实战:轻量应用与域名解析配置指南  详解Huffman编码算法之Java实现  如何用AWS免费套餐快速搭建高效网站?  Laravel如何实现登录错误次数限制_Laravel自带LoginThrottles限流配置【方法】  iOS验证手机号的正则表达式  Laravel怎么为数据库表字段添加索引以优化查询  手机软键盘弹出时影响布局的解决方法  Laravel如何实现多语言支持_Laravel本地化与国际化(i18n)配置教程  文字头像制作网站推荐软件,醒图能自动配文字吗?  iOS中将个别页面强制横屏其他页面竖屏  C#如何调用原生C++ COM对象详解  韩国服务器如何优化跨境访问实现高效连接?  Android自定义控件实现温度旋转按钮效果  谷歌浏览器如何更改浏览器主题 Google Chrome主题设置教程