当出现崩溃,软件不会闪退,会出现弹出一个对话框,异常错误信息会自动保存在sd卡crash这个文件夹下。后续需要还可以发送到服务器的。看效果图。

1、实现效果图


2、全局异常捕捉类CrashHandler

package com.crashhandler.util;import java.io.File;import java.io.FileOutputStream;import java.lang.Thread.UncaughtExceptionHandler;import java.text.DateFormat;import java.text.SimpleDateFormat;import java.util.Date;import com.crashhandler.activity.R;import android.annotation.SuppressLint;import android.app.AlertDialog;import android.content.Context;import android.content.DialogInterface;import android.content.Intent;import android.content.pm.PackageInfo;import android.content.pm.PackageManager.NameNotFoundException;import android.net.Uri;import android.os.Environment;import android.os.Looper;import android.util.Log;import android.view.WindowManager;/** * UncaughtException处理类,当程序发生Uncaught异常的时候,由该类来接管程序,并记录发送错误报告. *  */public class CrashHandler implements UncaughtExceptionHandler {// 系统默认的UncaughtException处理类private Thread.UncaughtExceptionHandler mDefaultHandler;// CrashHandler实例private static CrashHandler INSTANCE;// 程序的Context对象private Context mContext;//保证只有一个CrashHandler实例 private CrashHandler() {}//获取CrashHandler实例 ,单例模式public static CrashHandler getInstance() {if (INSTANCE == null)INSTANCE = new CrashHandler();return INSTANCE;}/** * 初始化 *  * @param context */public void init(Context context) {mContext = context;// 获取系统默认的UncaughtException处理器mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();// 设置该CrashHandler为程序的默认处理器Thread.setDefaultUncaughtExceptionHandler(this);}/** * 当UncaughtException发生时会转入该重写的方法来处理 */public void uncaughtException(Thread thread, Throwable ex) {if (!handleException(ex) && mDefaultHandler != null) {// 如果自定义的没有处理则让系统默认的异常处理器来处理mDefaultHandler.uncaughtException(thread, ex);}}/** * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. *  * @param ex *            异常信息 * @return true 如果处理了该异常信息;否则返回false. */public boolean handleException(Throwable ex) {if (ex == null || mContext == null)return false;final String crashReport = getCrashReport(mContext, ex);new Thread() {public void run() {Looper.prepare();File file = save2File(crashReport);sendAppCrashReport(mContext, crashReport, file);Looper.loop();}}.start();return true;}@SuppressLint("SimpleDateFormat")private File save2File(String crashReport) {//用于格式化日期,作为日志文件名的一部分DateFormat dateFormat = new SimpleDateFormat("yyyyMMddHHmmss");String time = dateFormat.format(new Date());   String fileName = "crash-" + time + "-" + System.currentTimeMillis() + ".txt";if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {try {//存储路径,是sd卡的crash文件夹File dir = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "crash");if (!dir.exists())dir.mkdir();File file = new File(dir, fileName);FileOutputStream fos = new FileOutputStream(file);fos.write(crashReport.toString().getBytes());fos.close();return file;} catch (Exception e) {//sd卡存储,记得加上权限,不然这里会抛出异常Log.i("Show","save2File error:" + e.getMessage());}}return null;}private void sendAppCrashReport(final Context context,final String crashReport, final File file) {AlertDialog.Builder builder = new AlertDialog.Builder(context).setIcon(android.R.drawable.ic_dialog_info).setTitle(R.string.app_error).setMessage(R.string.app_error_message).setPositiveButton(R.string.submit_report,new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {try {//这以下的内容,只做参考,因为没有服务器Intent intent = new Intent(Intent.ACTION_SEND);intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);String[] tos = { "way.ping.li@gmail.com" };intent.putExtra(Intent.EXTRA_EMAIL, tos);intent.putExtra(Intent.EXTRA_SUBJECT,"Android客户端 - 错误报告");if (file != null) {intent.putExtra(Intent.EXTRA_STREAM,Uri.fromFile(file));intent.putExtra(Intent.EXTRA_TEXT,"请将此错误报告发送给我,以便我尽快修复此问题,谢谢合作!\n");} else {intent.putExtra(Intent.EXTRA_TEXT,"请将此错误报告发送给我,以便我尽快修复此问题,谢谢合作!\n"+ crashReport);}intent.setType("text/plain");intent.setType("message/rfc882");Intent.createChooser(intent, "Choose Email Client");context.startActivity(intent);} catch (Exception e) {Log.i("Show","error:" + e.getMessage());} finally {dialog.dismiss();// 退出android.os.Process.killProcess(android.os.Process.myPid());System.exit(1);}}}).setNegativeButton(android.R.string.cancel,new DialogInterface.OnClickListener() {public void onClick(DialogInterface dialog, int which) {dialog.dismiss();// 退出android.os.Process.killProcess(android.os.Process.myPid());System.exit(1);}});AlertDialog dialog = builder.create();//需要的窗口句柄方式,没有这句会报错的        dialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);dialog.show();}/** * 获取APP崩溃异常报告 *  * @param ex * @return */private String getCrashReport(Context context, Throwable ex) {PackageInfo pinfo = getPackageInfo(context);StringBuffer exceptionStr = new StringBuffer();exceptionStr.append("Version: " + pinfo.versionName + "("+ pinfo.versionCode + ")\n");exceptionStr.append("Android: " + android.os.Build.VERSION.RELEASE+ "(" + android.os.Build.MODEL + ")\n");exceptionStr.append("Exception: " + ex.getMessage() + "\n");StackTraceElement[] elements = ex.getStackTrace();for (int i = 0; i < elements.length; i++) {exceptionStr.append(elements[i].toString() + "\n");}return exceptionStr.toString();}/** * 获取App安装包信息 *  * @return */private PackageInfo getPackageInfo(Context context) {PackageInfo info = null;try {info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);} catch (NameNotFoundException e) { e.printStackTrace(System.err);}if (info == null)info = new PackageInfo();return info;}}
3、系统级MyApplication继承Application。因为需要整个软件的全局,所以需要在这里初始化CrashHandler

package com.crashhandler.util;import android.app.Application;public class MyApplication extends Application{private static MyApplication mApplication;public synchronized static MyApplication getInstance() {return mApplication;}@Overridepublic void onCreate() {super.onCreate();initData();}private void initData() {//当程序发生Uncaught异常的时候,由该类来接管程序,一定要在这里初始化CrashHandler.getInstance().init(this);}}

4、看怎么使用MainActivity

package com.crashhandler.activity;import android.os.Bundle;import android.app.Activity;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;public class MainActivity extends Activity implements OnClickListener{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button button = (Button)findViewById(R.id.button1);button.setOnClickListener(this);}@Overridepublic void onClick(View v) {switch (v.getId()) {case R.id.button1://自已写了一个异常信息,进行测试Button button = (Button)findViewById(R.id.textView1);break;default:break;}}}
5、记得加入一些权限
                                

    

下载地址:下面评论我再发地址了,因为还没上传。



更多相关文章

  1. Android开发中出现Attempt to invoke virtual method...on a nul
  2. android获取手机流量使用情况
  3. Android(安卓)6.0 运行时权限 处理
  4. Android禁止截屏
  5. Android(安卓)Sudio 如何获取SHA1(开发版和发布版)
  6. Android应用程序获取ROOT权限的方法
  7. Android调用JNI出错 java.lang.UnsatisfiedLinkError: No implem
  8. Android自动检测版本及自动升级
  9. android中的xml处理

随机推荐

  1. Android中利用shape实现圆角
  2. Android(安卓)4 / iMX6系统开发手记
  3. Android(安卓)WebView与JavaScript交互操
  4. android SharedPreferences简单使用
  5. Android面试相关
  6. EventBus3.0使用详解
  7. android的listview美化
  8. Android精通教程V
  9. Android引路蜂地图开发示例:概述
  10. android权限之一——uses-permission