Android程序捕获未处理异常,处理与第三方方法冲突时的异常传递

自己的android程序对异常进行了处理,用的也是网上比较流行的CrashHandler,代码如下,就是出现了未处理的异常程序退出,并收集收集设备信息和错误信息仪器保存到SD卡,这里没有上传到服务器。

public class CrashHandler implements UncaughtExceptionHandler
{

    public static final String TAG = "CrashHandler";

    // CrashHandler 实例
    private static CrashHandler INSTANCE = new CrashHandler();

    // 程序的 Context 对象
    private Context mContext;

    // 系统默认的 UncaughtException 处理类
    private Thread.UncaughtExceptionHandler mDefaultHandler;

    // 用来存储设备信息和异常信息
    private Map<String, String> infos = new HashMap<String, String>();

    // 用于格式化日期,作为日志文件名的一部分
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

    /** 保证只有一个 CrashHandler 实例 */
    private CrashHandler()
    {
    }

    /** 获取 CrashHandler 实例 ,单例模式 */
    public static CrashHandler getInstance()
    {
        return INSTANCE;
    }

    /**
     * 初始化
     *
     * @param context
     */
    public void init(Context context)
    {
        mContext = context;
        // 获取系统默认的 UncaughtException 处理器
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
        // 设置该 CrashHandler 为程序的默认处理器
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当 UncaughtException 发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex)
    {
        if (!handleException(ex) && mDefaultHandler != null)
        {
            // 如果用户没有处理则让系统默认的异常处理器来处理
            LogUtil.i("wepa mDefaultHandler beg");
            mDefaultHandler.uncaughtException(thread, ex);
        } else
        {
            try
            {
                Thread.sleep(2000);
            } catch (InterruptedException e)
            {
                LogUtil.e("error : ", e);
            }
            LogUtil.i("wepa killProcess beg");
            // 退出程序
            // 清除栈
            List<Activity> openedActivity = ((WepaApplication) mContext)
                    .getOpenedActivity();
            for (Activity activity : openedActivity)
            {
                if (activity != null)
                {
                    LogUtil.d("activity getComponentName : "
                            + activity.getComponentName());
                    activity.finish();
                }
            }
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);

            /*
             * // 重新启动程序,注释上面的退出程序 Intent intent = new Intent();
             * intent.setClass(mContext, MainPageActivity.class);
             * intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
             * mContext.startActivity(intent);
             * android.os.Process.killProcess(android.os.Process.myPid());
             */
        }
    }

    /**
     * 自定义错误处理,收集错误信息,发送错误报告等操作均在此完成
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回 false
     */
    private boolean handleException(Throwable ex)
    {
        if (ex == null)
        {
            return false;
        }
        LogUtil.i("wepa handleException beg");
        new Thread()
        {
            @Override
            public void run()
            {
                Looper.prepare();
                Toast.makeText(mContext, "很抱歉,程序遇到异常,即将退出", Toast.LENGTH_SHORT)
                        .show();
                Looper.loop();
            }
        }.start();
        // 收集设备参数信息
        collectDeviceInfo(mContext);
        // 保存日志文件
        saveCrashInfo2File(ex);
        // 使用 Toast 来显示异常信息
        LogUtil.i("wepa saveCrashInfo2File OK");
        return true;
    }

    /**
     * 收集设备参数信息
     *
     * @param ctx
     */
    public void collectDeviceInfo(Context ctx)
    {
        try
        {
            PackageManager pm = ctx.getPackageManager();
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
                    PackageManager.GET_ACTIVITIES);

            if (pi != null)
            {
                String versionName = pi.versionName == null ? "null"
                        : pi.versionName;
                String versionCode = pi.versionCode + "";
                infos.put("versionName", versionName);
                infos.put("versionCode", versionCode);
            }
        } catch (NameNotFoundException e)
        {
            LogUtil.e("an error occured when collect package info", e);
        }

        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields)
        {
            try
            {
                field.setAccessible(true);
                infos.put(field.getName(), field.get(null).toString());
                LogUtil.d(field.getName() + " : " + field.get(null));
            } catch (Exception e)
            {
                LogUtil.e("an error occured when collect crash info", e);
            }
        }
    }

    /**
     * 保存错误信息到文件中 *
     *
     * @param ex
     * @return 返回文件名称,便于将文件传送到服务器
     */
    private String saveCrashInfo2File(Throwable ex)
    {
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet())
        {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");
        }

        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        while (cause != null)
        {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();

        String result = writer.toString();
        sb.append(result);
        try
        {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(new Date());
            String fileName = "crash_" + time + "_" + timestamp + ".log";

            if (Environment.getExternalStorageState().equals(
                    Environment.MEDIA_MOUNTED))
            {
                String path = Environment.getExternalStorageDirectory()
                        + "/crash/";
                File dir = new File(path);
                if (!dir.exists())
                {
                    dir.mkdirs();
                }
                FileOutputStream fos = new FileOutputStream(path + fileName);
                fos.write(sb.toString().getBytes());
                fos.close();
            }

            return fileName;
        } catch (Exception e)
        {
            LogUtil.e("an error occured while writing file...", e);
        }

        return null;
    }
}

使用方法就是在自己的Application类中加入初始化就好了,代码如下:

// 在自己的Application中的OnCreate中加入异常处理的代码
public void onCreate()
    {
        // TODO Auto-generated method stub
        super.onCreate();

        // 异常捕获处理
        initErrorHandler();
               // 其他处理

    }

        /**
     * 处理错误
     */
    private void initErrorHandler()
    {
        CrashHandler handler = CrashHandler.getInstance();
        handler.init(getApplicationContext());
    }

这样就可以保证程序遇到未知错误时推出app。

后来有需求要加个第三方的插件进来,这个插件中也要在自定义的Application类的OnCreate中初始化,而且它也有对异常程序的默认处理,也需要保存错错误文件,用的份上面的CrashHandler是一样的代码,这就出现了问题,因为Thread.setDefaultUncaughtExceptionHandler(this);方法将Thread里的静态变量defaultUncaughtHandler设置的话,以后的Thread默认就是它处理了。

 private static UncaughtExceptionHandler defaultUncaughtHandler;

因此,需要一种传递机制,使得两个异常中定义的handleException方法(里面是异常信息的保存)都能进行,这里因为第三放的插件不能对app的异常造成干扰,只让它保存异常就可以了,所以在handleException方法中返回false,然后将异常向上传递,这使得第三方插件中定义的mDefaultHandler.uncaughtException(thread, ex);得以执行,再根据插件public void init(Context context)函数中的定义顺序

        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();

        // 设置该CrashHandler为程序的默认处理器,人为捕获该异常
        Thread.setDefaultUncaughtExceptionHandler(this);
mDefaultHandler 是上一次设置的值,所以只需要这个值是我们app的异常处理就好了。因此在自定义Application中初始化异常CrashHandler的时候顺序就必须要满足初始自己app的,再初始第三方插件的。这样就可以保证异常先由插件处理,由于返回false,再由app中自己的处理,最后返回true,并终止程序。
Application中初始化如下:

    public void onCreate()
    {
        // app的异常捕获处理
        initErrorHandler();
                // 插件的异常捕获处理
        if (Config.isOpen)
        {
            AnalyticsManager.init(getApplicationContext());
        }

    }

其中插件中也使用的CrashHandlelr,定义差不多,就是返回值不同,然后不终止程序。代码如下

public class CrashHandler implements UncaughtExceptionHandler
{

    public static final String TAG = "CrashHandler";

    // 系统默认的UncaughtException处理类
    private Thread.UncaughtExceptionHandler mDefaultHandler;
    // CrashHandler实例
    private static CrashHandler INSTANCE = new CrashHandler();
    // 程序的Context对象
    private Context mContext;
    // 用来存储设备信息和异常信息
    private Map<String, String> infos = new HashMap<String, String>();

    // 用于格式化日期,作为日志文件名的一部分
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");

    /** 保证只有一个CrashHandler实例 */
    private CrashHandler()
    {
    }

    /** 获取CrashHandler实例 ,单例模式 */
    public static CrashHandler getInstance()
    {
        return INSTANCE;
    }

    /**
     * 初始化
     *
     * @param context
     */
    public void init(Context context)
    {
        mContext = context;
        // 获取系统默认的UncaughtException处理器,此处为主线程异常处理
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();

        // 设置该CrashHandler为程序的默认处理器,人为捕获该异常
        Thread.setDefaultUncaughtExceptionHandler(this);
    }

    /**
     * 当UncaughtException发生时会转入该函数来处理
     */
    @Override
    public void uncaughtException(Thread thread, Throwable ex)
    {
        if (!handleException(ex) && mDefaultHandler != null)
        {
            // 人为捕获该异常之后,让系统默认的异常处理器来处理
            mDefaultHandler.uncaughtException(thread, ex);
        }
        else
        {
            try
            {
                Thread.sleep(3000);
            }
            catch (InterruptedException e)
            {
                Log.e(TAG, "error : ", e);
            }

            // 退出程序
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }
    }

    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private boolean handleException(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();

        // 保存日志文件
        saveCrashInfo2File(ex);
        return false;
    }

    /**
     * 保存错误信息到文件中
     *
     * @param ex
     * @return 返回文件名称,便于将文件传送到服务器
     */
    private void saveCrashInfo2File(Throwable ex)
    {

        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String, String> entry : infos.entrySet())
        {
            String key = entry.getKey();
            String value = entry.getValue();
            sb.append(key + "=" + value + "\n");
        }

        // 将异常写入到PrintWriter
        Writer writer = new StringWriter();
        PrintWriter printWriter = new PrintWriter(writer);
        ex.printStackTrace(printWriter);
        Throwable cause = ex.getCause();
        // 层层错误输出
        while (cause != null)
        {
            cause.printStackTrace(printWriter);
            cause = cause.getCause();
        }
        printWriter.close();
        String result = writer.toString();
        sb.append(result);
        try
        {
            long timestamp = System.currentTimeMillis();
            String time = formatter.format(timestamp);
            BehaviorManager.saveException(mContext, time, sb.toString());

            // test for exception
            String fileName = Environment.getExternalStorageDirectory().toString()
                    + File.separator + "excep/" + "crush_instant_";
            Date date = new Date(System.currentTimeMillis());
            SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss");
            final String fileFullName = fileName + df.format(date) + "_" +System.currentTimeMillis() + ".log";
            Log.i("Wepa", "SDK saveDataToFile OK...");
            System.out.println("SDK saveDataToFile OK...");
            NetworkManager.saveDataToFile(fileFullName, sb.toString());
        }
        catch (Exception e)
        {
            Log.e(TAG, "an error occured while writing file...", e);
        }
    }
}

这样就可以有两份异常文件了,插件的可以自行处理,比较灵活。

学习了Thread的默认异常处理,还有传播方法,虽然耗费了一下午的时间还是很值得的,开始只是尝试,后来才明白道理,感觉很好。

不过,这是看了Java编程思想后理解的结果,不知道对不对,在以后的学习中慢体会吧。有错误的地方,还请留言。

 
时间: 2024-10-22 11:41:53

Android程序捕获未处理异常,处理与第三方方法冲突时的异常传递的相关文章

aspcms网站访问出现3706错误, 错误描述:未找到提供程序。该程序可能未正确安装,解决的方法。

Xcode工程使用CocoaPods管理第三方库新建工程时出现异常

Xcode工程使用CocoaPods管理第三方库新建工程时出现错误工程使用CocoaPods管理第三方库,在新的目录update版本的时候出现如下问题 问题1描述:diff: /../Podfile.lock: No such file or directory diff: /Manifest.lock: No such file or directory error: The sandbox is not in sync with the Podfile.lock. Run 'pod inst

错误-终止应用程序由于未捕获的异常的nsinvalidargumentexception’,原因:“[:]:未知的UIView setImage选择器送到实例0x8d78d20”

1 UIImage *image = [UIImage imageNamed:tempAppInfo.icon]; 2 UIImageView *appIcon = (UIImageView *) [appView viewWithTag:0]; 3 appIcon.image = image; 每次执行到第三句时就报错: 更改办法: 把UIImageView的tag改掉不要用0. 原因分析:?. 错误-终止应用程序由于未捕获的异常的nsinvalidargumentexception',原因:

Android程序Crash时的异常上报

转载请注明来源:http://blog.csdn.net/singwhatiwanna/article/details/17289479 前言 大家都知道,android应用不可避免的会发生crash,无论你的程序写的多完美,总是无法完全避免crash的发生,可能是由于android系统底层的bug,也可能是由于不充分的机型适配或者是糟糕的网络状况.当crash发生时,系统会kill掉你的程序,表现就是闪退或者程序已停止运行,这对用户来说是很不友好的,也是开发者所不愿意看到的,更糟糕的是,当用户

Android应用捕获全局异常自定义处理

[2016-06-30]最新的全局异常处理DRCrashHandler已经集成在DR_support_lib库中 具体请看: https://coding.net/u/wrcold520/p/DR_support_lib/git/tree/master [2016-06-28] 1 增加log4j的支持[2016-06-28] 2 增加全局异常处理(可自定义程序崩溃提示消息,自定义发送错误报告到服务器)[2016-06-28] 3 增加两种应用退出方法:① appExit,结束掉所有Acitiv

捕获android程序崩溃日志

主要类: package com.example.callstatus; import java.io.File; import java.io.FileOutputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.lang.Thread.UncaughtExceptionHandler; import java.lang.reflect.Field; import java.net.Unkn

asp.net捕获全局未处理异常的几种方法

通过HttpModule来捕获未处理的异常[推荐] 首先需要定义一个HttpModule,并监听未处理异常,代码如下: public void Init(HttpApplication context) { context.Error += new EventHandler(context_Error); } public void context_Error(object sender, EventArgs e) { //此处处理异常 HttpContext ctx = HttpContext

Android程序崩溃异常收集框架

最近在写Android程序崩溃异常处理,完成之后,稍加封装与大家分享. 我的思路是这样的,在程序崩溃之后,将异常信息保存到一个日志文件中,然后对该文件进行处理,比如发送到邮箱,或发送到服务器. 所以,第一步是先定义一个接口,用于在保存好日志之后的回调.代码如下: /* * @(#)CrashListener.java Project: crash * Date:2014-5-27 * * Copyright (c) 2014 CFuture09, Institute of Software, *

winform程序捕获全局异常,对错误信息写入日志并弹窗

使用场景:在winform程序中如果没对方法进行try catch操作,若方法内出错,则整个程序报错并退出,如下图 如果程序已在客户手中,若没对错误的详细信息进行拍照,我们则不知道错误原因是什么.我们需要能捕获整个程序的错误信息,一旦程序出错,则跳到我们指定的方法中,执行错误信息的报错以及友好的错误提示(也可以不提示,直接退出程序). 实现代码如下:(代码放置位置:在Program.cs文件中的Main方法中) using System; using System.Collections.Gen