我们写程序的时候都希望能写出一个没有任何Bug的程序,期望在任何情况下都不会发生程序崩溃。不过理想是丰满的,现实是骨感的。没有一个程序员能保证自己写的程序绝对不会出现异常崩溃。特别是针对用户数达到几十万几百万的程序,当你用户数达到一定数量级后,就算你的程序出现个别异常崩溃情况也不用惊讶。此时及时收集用户的日志成了解决问题的关键。看了网上大部分是采用日志收集的第三方jar包来完成的,还有一种是自定义一个自己的CrashHandler实现UncaughtExceptionHandler接口来捕获闪退信息然后上传到自己的服务器。这样的实现方式总觉得如果用户误删了闪退的日志文件那么就会导致无法及时上报闪退日志,也就无从分析隐藏的Bug了,于是采用另一种实现思路,即:在程序发生异常时提醒用户发生了什么样的异常,同时把本次捕获的Exception的字段写入到自己定义的log文件中,然后上报异常字段到自己的异常服务器上,从手机端或者后台都可以看到发生的异常堆栈。日志记录系统不借助与任何第三方jar包。说干就干,

项目目录如下:

关键代码如下:

1、定义IExceptionHandler接口

/** *异常处理类 * */ public interface IExceptionHandler { /** * 异常处理类. * @param t * @param db */ public String handlerException(Throwable t, SQLiteDatabase db); /** * 异常处理类. * @param t */ public String handlerException(Throwable t); /** * 异常处理类. * @param t */ public void sendException(Throwable t); /** * 给用户提示 * @param errorMsg */ public void showTipMessgae(String errorMsg); }

2、定义ExceptionHandler实现自IExceptionHandler

/**

* 异常处理类

*/

public class ExceptionHandler implements IExceptionHandler{

private Context context;

private ExceptionSender exceptionSender;

public ExceptionHandler(Context context)

{

this.context = context;

exceptionSender = new ExceptionSender(context);

}

@Override

public String handlerException(Throwable t) {

return handlerException(t, null);

}

@Override

public String handlerException(Throwable exception, SQLiteDatabase db) {

String message = "";

if (exception instanceof ConnectException)

{

message = "网络连接有问题!";

}

else if (exception instanceof SocketTimeoutException)

{

message = "连接超时!";

}

else if (exception instanceof NullPointerException)

{

message = "程序出错,空指针错误!";

}

else if (exception instanceof IllegalArgumentException)

{

message = "程序出错,错误的请求参数!";

}

else if (exception instanceof ArithmeticException)

{

message = "程序出错,数值计算出错!";

}

else if (exception instanceof NetworkOnMainThreadException)

{

message = "程序出错,网络请求放在主线程中运行!";

}

else if (exception instanceof IllegalStateException)

{

message = "程序出错,网络请求要放在主线程中运行!";

}

else if (exception instanceof IndexOutOfBoundsException)

{

message = "数组越界异常!";

}

else if (exception instanceof IOException)

{

message = "程序出错,IO错误!";

}

else

{

message = exception.getMessage();

}

showTipMessgae(message);

exceptionSender.send(exception);

return message;

}

@Override

public void sendException(Throwable t) {

exceptionSender.send(t);

}

@Override

public void showTipMessgae(final String msg)

{

if (context != null)

{

Toast.makeText(context, msg, Toast.LENGTH_SHORT).show();

}

}

}

3、在AndroidManifest.xml里面配置自己的Application:IApplication,并在里面初始化app日志文件目录

public class IApplication extends Application{

/**

* 应用根目录

*/

private File rootFile;

/**

* log目录

*/

private File logFile;

private static final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");

private static IApplication INSTANCE = null;

public static IApplication getInstance()

{

return INSTANCE;

}

private FileLog fileLog;

@Override

public void onCreate() {

super.onCreate();

INSTANCE = this;

initDataRoot();

File log = new File(logFile, df.format(new Date()) + ".log");

PrintWriter pw = null;

try {

pw = new PrintWriter(new FileOutputStream(log, true), true);

} catch (FileNotFoundException e1) {

}

fileLog = new FileLog(pw);

fileLog.setLevel(FileLog.LEVEL_INFO);//设置日志系统的等级

CrashHandler crashHandler = CrashHandler.getInstance();

crashHandler.init(this);

}

/**

* 初始化log目录

*/

private void initDataRoot() {

String state = android.os.Environment.getExternalStorageState();

if (Environment.MEDIA_MOUNTED.equals(state)) {

rootFile =new File(Environment.getExternalStorageDirectory(),"11");

if (!rootFile.exists()) {

rootFile.mkdirs();

}

}else {

rootFile = getDatabasePath("11");

}

logFile = new File(rootFile, "logs");

if (!logFile.exists()) {

logFile.mkdirs();

}

}

public FileLog getFileLog() {

return fileLog;

}

}

4、采用HTTP方式将实时catch的异常发送到异常服务器,并将异常的堆栈信息写入到SDcard中

/** * 异常发送类 */ public class ExceptionSender { private Context context; private String url="你的收集异常信息的服务器的地址"; //版本名称 private String app_name; //版本号 private String app_version; //设备名称 private String device_name; //操作系统 private String os_name; //操作系统版本号 private String os_version; private ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); private FileLog fileLog; public ExceptionSender(Context context) { init(context); this.context = context; this.fileLog = IApplication.getInstance().getFileLog(); } private void init(Context context) { PackageInfo packInfo; try { packInfo = context.getPackageManager().getPackageInfo(context.getPackageName(), 0); if (packInfo != null) { app_name = packInfo.packageName; app_version = packInfo.versionName; } device_name = android.os.Build.MODEL; os_name = "Android"; os_version = android.os.Build.VERSION.RELEASE; } catch (PackageManager.NameNotFoundException e) { } } /** * 发送错误信息到错误收集中心. * * @param ex */ public void send(final Throwable ex) { fileLog.e(ex.getClass().getSimpleName(), ex); cachedThreadPool.execute(new Runnable() { @Override public void run() { try { DefaultHttpClient httpClient = new DefaultHttpClient(); HttpPost httpPost = new HttpPost(url); ByteArrayOutputStream out = new ByteArrayOutputStream(); PrintStream ps = new PrintStream(out); ex.printStackTrace(ps); ps.flush(); String err_msg = new String(out.toByteArray(), "UTF-8"); Map params = new HashMap(); List parameters = new ArrayList(); addParameter(params, parameters, "class_name", ex.getClass().getSimpleName()); addParameter(params, parameters, "app_name", app_name); addParameter(params, parameters, "app_version", app_version); addParameter(params, parameters, "device_name", device_name); addParameter(params, parameters, "os_name", os_name); addParameter(params, parameters, "os_version", os_version); parameters.add(new BasicNameValuePair("err_msg", err_msg)); parameters.add(new BasicNameValuePair("stack_msg", "")); addParameter(params, parameters, "error_time", Long.toString(System.currentTimeMillis())); httpPost.setEntity(new UrlEncodedFormEntity(parameters, HTTP.UTF_8)); httpClient.execute(httpPost); } catch (Throwable e) { } } }); } private void addParameter(Map params, List parameters, String key, String value) { value = value == null ? "" : value; params.put(key, value); parameters.add(new BasicNameValuePair(key, value)); } }

4、使用方式:在程序里面采用try catch捕获可能会出现的异常的代码块(界面给出提示,程序不闪退),另一种是没有进行try catch应用程序直接闪退

public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private IExceptionHandler exceptionHandler; private Button btn1, btn2; private Student student; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); exceptionHandler = new ExceptionHandler(getApplicationContext()); btn1 = (Button)findViewById(R.id.button1); btn1.setOnClickListener(this); btn2 = (Button)findViewById(R.id.button2); btn2.setOnClickListener(this); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.button1: try{//捕获了异常,异常的堆栈信息写入了SDcard中同时也上报到了后台 int m = 1/0; System.out.print(m); }catch (Exception e){ exceptionHandler..handlerException(e); } break; case R.id.button2://没有捕获异常,应用程序直接退了,异常的堆栈信息写入了SDcard中同时也上报到了后台 System.out.print(student.getId()); break; } } }

这样友好的提示也给了用户,异常信息也及时上报了。但是该方式也有自己的缺点:大量的try catch会导致代码的效率不高。

Android记录日志方式,关于Android中处理崩溃异常和记录日志的另一种实现思路相关推荐

  1. Android中处理崩溃异常和记录日志

    转载请注明出处:http://blog.csdn.net/u014608640/article/details/52485340 现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好 ...

  2. Android中处理崩溃异常

    2019独角兽企业重金招聘Python工程师标准>>> 大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象 ...

  3. Android中处理崩溃异常 (转)

    大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了 ...

  4. android 中处理崩溃异常并重启程序

    转:http://blog.csdn.net/cym_lmy/article/details/24704089 有时候由于测试不充分或者程序潜在的问题而导致程序异常崩溃,这个是令人无法接受的,在and ...

  5. android接口回调的两中简单写法

    android接口回调的两中简单写法--B509小贴士 一.第一种内部重写(较简单,常用异步) (1)创建接口 ,并实现方法 (2)在获取数据类中实现传递接口对象为参数的方法 (3)在主类中调用返回数 ...

  6. java的异常与记录日志

    今天在<java编程思想>一书中看到了异常与记录日志,发现学会将异常记录进日志中还是很有必要的,以下是书中的例子: 1 import java.io.PrintWriter; 2 impo ...

  7. android 多个应用,Android中一个应用实现多个图标的几种方式

    前言 最近因为新需求需要,我的应用将有多个ICON入口..最终选择了 activity-alias , 其实实现多图标有好几种方式,下面就给大家总结下,分享出来供大家参考学习: 1. 多Activit ...

  8. 在Android中,如何以编程方式在dp中设置边距?

    本文翻译自:In Android, how do I set margins in dp programmatically? In this , this and this thread I trie ...

  9. Android中通过数组资源文件xml与适配器两种方式给ListView列表视图设置数据源

    场景 实现效果如下 注: 博客: https://blog.csdn.net/badao_liumang_qizhi 关注公众号 霸道的程序猿 获取编程相关电子书.教程推送与免费下载. 实现 将布局改 ...

最新文章

  1. python编程爱心-使用Python画出小人发射爱心的代码
  2. HUD 1043 Eight 八数码问题 A*算法 1667 The Rotation Game IDA*算法
  3. CentOS下挂载iso文件
  4. fiddler如何设置过滤https_Google Analytics如何设置含有过滤器的帐户数据视图
  5. ZOJ - 4114 Flipping Game(dp+组合数学)
  6. 当下流行的分布式文件系统大阅兵
  7. TypeScript中的class声明了什么
  8. c语言数字字体的格式,c语言—— 格式控制符—— 数据类型——相对应的字节数...
  9. 小程序多个echars_小程序界面与逻辑
  10. 简单的php探针,php探针程序的推荐
  11. VC2005 C++入门记
  12. mac下php的坑,MAC下安装laravel时遇到的坑
  13. pytorch报错: invalid argument 0: Sizes of tensors must match except in dimension 0.
  14. angularjscheckbox全选_Vue Checkbox全选和选中的方法
  15. vim编辑器中常用的命令
  16. java 常用汉字_中国常用汉字有多少
  17. 小米6不显示与电脑连接到服务器,小米6怎么连接上电脑连不上怎么办
  18. html5快手视频播放特效,抖音快手热门上人在天上飞的特效视频教程
  19. 在图像中剪切圆形图片
  20. STM32F1xx_StdPeriph_Driver——SPI

热门文章

  1. Python爬虫高阶:微店混淆逆向解密
  2. mysql 查询表的key_MySQL学习笔记-查看表,修改表,删除表
  3. DLL的Export和Import及extern
  4. Axure RP9 自学之路1-软件初识
  5. P3052 [USACO12MAR]摩天大楼里的奶牛Cows in a Skyscraper [模拟退火]
  6. MongoDB复制集搭建主服务器模拟切换
  7. 050医疗项目-模块五:权限设置-第三方系统的接入
  8. 【unity3d study ---- 麦子学院】---------- unity3d常用组件及分析 ---------- Animator动画状态机...
  9. [OpenGL] opengl常见问题汇总
  10. 查看操作系统和处理器的位数