zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

Android UncaughtExceptionHandler进行全局异常捕获

Android异常 进行 全局 捕获
2023-09-14 08:57:28 时间

        在实际开发过程中,我们的APP由于各种原因,难免会有Crash现象(应用程序XXX已经停止)。这样给用户一种很不友好的感觉,那么我们如何去处理这种情况呢?答案就在实现UncaughtchExceptionHanlder,复写uncaughtException()方法。

        当crash发生的时候,系统会调用UncaughtchExceptionHanlder#uncaughtException()。在uncaughtException()中我们可以选择收集错误信息,然后保存在SD卡中,在合适的时机将错误日志上传至服务器。这样开发人员在后期维护的时候,就可以有针对性的修复BUG。由于默认的异常处理器是Thread类的静态成员,所以它的作用对象是当前进程的所有线程。下面是一个常用的标准的异常处理器三步走。

一)实现自定义CrashHandler

package com.example.crashhandler;

import java.io.File;

import java.io.IOException;

import java.io.PrintWriter;

import java.lang.Thread.UncaughtExceptionHandler;

import java.lang.reflect.Field;

import java.text.SimpleDateFormat;

import java.util.Date;

import java.util.Timer;

import java.util.TimerTask;

import org.apache.http.HttpResponse;

import org.apache.http.HttpStatus;

import org.apache.http.client.ClientProtocolException;

import org.apache.http.client.HttpClient;

import org.apache.http.client.methods.HttpGet;

import org.apache.http.impl.client.DefaultHttpClient;

import org.apache.http.util.EntityUtils;

import android.content.Context;

import android.content.Intent;

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.os.Process;

import android.util.Log;

import android.widget.Toast;

import com.lidroid.xutils.HttpUtils;

import com.lidroid.xutils.http.ResponseInfo;

import com.lidroid.xutils.http.callback.RequestCallBack;

import com.lidroid.xutils.http.client.HttpRequest.HttpMethod;

public class CrashHandler implements UncaughtExceptionHandler {

 private static final String TAG = "CrashHandler";

 private UncaughtExceptionHandler mDefaultHandler;

 private static CrashHandler crashHandler = new CrashHandler();

 private Context mContext;

 /** 错误日志文件 */

 private File logFile = new File(Environment.getExternalStorageDirectory(),"crashLog.trace");

 private CrashHandler() {

 public static CrashHandler getInstance() {

 if (crashHandler == null) {

 synchronized (CrashHandler.class) {

 if (crashHandler == null) {

 crashHandler = new CrashHandler();

 return crashHandler;

 public void init(Context context) {

 mContext = context;

 mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();

 //设置为线程默认的异常处理器

 Thread.setDefaultUncaughtExceptionHandler(this);

 @Override

 public void uncaughtException(Thread thread, Throwable ex) {

 // 打印异常信息

 ex.printStackTrace();

 // 我们没有处理异常 并且默认异常处理不为空 则交给系统处理

 if (!handlelException(ex) mDefaultHandler != null) {

 // 系统处理

 mDefaultHandler.uncaughtException(thread, ex);

 } else {

 try {

 Thread.sleep(3 * 1000);

 } catch (InterruptedException e) {

 e.printStackTrace();

 try {

 // 上传错误日志到服务器

 upLoadErrorFileToServer(logFile);

 } catch (Exception e) {

 e.printStackTrace();

 Intent intent = new Intent(mContext, SplashActivity.class);

 // 新开任务栈

 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

 mContext.startActivity(intent);

 // 杀死我们的进程

 Timer timer = new Timer();

 timer.schedule(new TimerTask() {

 @Override

 public void run() {

 Process.killProcess(Process.myPid());

 }, 2 * 1000);

 private boolean handlelException(Throwable ex) {

 if (ex == null) {

 return false;

 // 使用Toast来显示异常信息

 new Thread() {

 @Override

 public void run() {

 Looper.prepare();

 Toast.makeText(mContext, "程序发生异常,即将重启", Toast.LENGTH_LONG)

 .show();

 Looper.loop();

 }.start();

 PrintWriter pw = null;

 try {

 if (!logFile.exists()) {

 logFile.createNewFile();

 pw = new PrintWriter(logFile);

 // 收集手机及错误信息

 logFile = collectInfoToSDCard(pw, ex);

 pw.close();

 } catch (Exception e) {

 e.printStackTrace();

 return true;

 * 上传错误日志到服务器

 * @param logFile

 * @throws IOException 

 private void upLoadErrorFileToServer(File errorFile) {

 * 收集手机信息

 * @throws NameNotFoundException

 private File collectInfoToSDCard(PrintWriter pw, Throwable ex)

 throws NameNotFoundException {

 PackageManager pm = mContext.getPackageManager();

 PackageInfo pi = pm.getPackageInfo(mContext.getPackageName(),PackageManager.GET_ACTIVITIES);

 // 错误发生时间

 String time = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());

 pw.print("time : ");

 pw.println(time);

 // 版本信息

 pw.print("versionCode : ");

 pw.println(pi.versionCode);

 // 应用版本号

 pw.print("versionName : ");

 pw.println(pi.versionName);

 try {

 /** 暴力反射获取数据 */

 Field[] Fields = Build.class.getDeclaredFields();

 for (Field field : Fields) {

 field.setAccessible(true);

 pw.print(field.getName() + " : ");

 pw.println(field.get(null).toString());

 } catch (Exception e) {

 Log.i(TAG, "an error occured when collect crash info" + e);

 // 打印堆栈信息

 ex.printStackTrace(pw);

 return logFile;

}


2)在MyApplication中实例化CrashHanlder

package com.example.crashhandler;

import android.app.Application;

public class MyApplication extends Application {

 private static MyApplication mInstance;

 public static MyApplication getInstance(){

 return mInstance;

 @Override

 public void onCreate() {

 super.onCreate();

 mInstance = this;

 CrashHandler crashHandler = CrashHandler.getInstance();

 crashHandler.init(this);

3)应用到Manifest.xml中

 ?xml version="1.0" encoding="utf-8"? 

 manifest xmlns:android="http://schemas.android.com/apk/res/android"

 package="com.example.crashhandler"

 android:versionCode="1"

 android:versionName="1.0.0" 

 uses-sdk

 android:minSdkVersion="8"

 android:targetSdkVersion="21" / 

 uses-permission android:name="android.permission.INTERNET"/ 

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

 application

 android:name=".MyApplication"

 android:allowBackup="true"

 android:icon="@drawable/ic_launcher"

 android:label="@string/app_name"

 android:theme="@style/AppTheme" 

 activity

 android:name=".SplashActivity"

 android:label="@string/app_name" 

 intent-filter 

 action android:name="android.intent.action.MAIN" / 

 category android:name="android.intent.category.LAUNCHER" / 

 /intent-filter 

 /activity 

 activity android:name=".MainActivity" 

 /activity 

 /application 

 /manifest 

        至此,我们的自定义异常处理器就算写完了。注释比较多,就不赘述了。有不清楚的读者请下面评论,我会一一回复。下面,测试下我们的异常处理器。代码很简单,SplshActivity---- MainActivity。在MainActivity中点击按钮抛出一个空指针异常。详情看代码

package com.example.crashhandler;

import java.util.Timer;

import java.util.TimerTask;

import android.app.Activity;

import android.content.Intent;

import android.os.Bundle;

public class SplashActivity extends Activity {

 @Override

 protected void onCreate(Bundle savedInstanceState) {

 super.onCreate(savedInstanceState);

 setContentView(R.layout.activity_splash);

 MyApplication.getInstance();

 // 两秒后进入MainActivity

 Timer timer = new Timer();

 timer.schedule(new TimerTask() {

 @Override

 public void run() {

 Intent intent = new Intent(SplashActivity.this, MainActivity.class);

 startActivity(intent);

 }, 2*1000);


protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); crash = (Button) findViewById(R.id.crash); crash.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { throw new NullPointerException();

点击按钮之后,触发我们的全局未捕获异常处理器。手机错误信息保存至SD卡,随后新开任务栈重启SplashActivity。点我下载Demo源代码

待完善:

对SD卡是否存在进行判断 对错误日志文件轮询,新开service上传至服务器


Android在Application层级维护和管理全局所有Activity的方法ActivityLifecycleCallbacks 经常看到有些项目中经常性的把所有activity继承自一个base的Activity,然后在每一次启动新activity时候添加当前activity到一个全局List那样的列表中,已达到全局管理和维护activity的目的,这种做法大概是四五年前的技术解决方案。
Android项目实战(二十三):仿QQ设置App全局字体大小 原文:Android项目实战(二十三):仿QQ设置App全局字体大小 一、项目需求: 因为产品对象用于中老年人,所以产品设计添加了APP全局字体调整大小功能。 这里仿做QQ设置字体大小的功能。 QQ实现的效果是,滚动下面的seekbar,当只有seekbar到达某一个刻度的时候,这时候上部分的效果展示部分会改变文字大小, 但是在拖动过程中字体不会改变。