`
abc20899
  • 浏览: 939380 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Android 程序错误处理全局处理

阅读更多
UncaughtExceptionHandler接口实现

首先创建一个OPhone项目(项目的创建请参考OPhoneSDN上的其他文章),本文示例项目名称为:CrashReporter ;包名为:org.goodev.cr;并创建一个默认的Activity名字为:ReporterTest。然后创建CrashHandler类实现UncaughtExceptionHandler接口,并实现其函数:public void uncaughtException(Thread thread, Throwable ex)。CrashHandler类实现了错误报告的主要处理逻辑,该类代码如下(在代码中会有详细注释来解释各种处理情况):


package org.goodev.cr;   
  
Import 省略...;   
/**  
* UncaughtException处理类,当程序发生Uncaught异常的时候,有该类  
* 来接管程序,并记录 发送错误报告.  
*  
*/  
public class CrashHandler implements UncaughtExceptionHandler {   
    /** Debug Log tag*/  
    public static final String TAG = "CrashHandler";   
    /** 是否开启日志输出,在Debug状态下开启,  
     * 在Release状态下关闭以提示程序性能  
     * */  
    public static final boolean DEBUG = true;   
    /** 系统默认的UncaughtException处理类 */  
    private Thread.UncaughtExceptionHandler mDefaultHandler;   
    /** CrashHandler实例 */  
    private static CrashHandler INSTANCE;   
    /** 程序的Context对象 */  
    private Context mContext;   
       
    /** 使用Properties来保存设备的信息和错误堆栈信息*/  
    private Properties mDeviceCrashInfo = new Properties();   
    private static final String VERSION_NAME = "versionName";   
    private static final String VERSION_CODE = "versionCode";   
    private static final String STACK_TRACE = "STACK_TRACE";   
    /** 错误报告文件的扩展名 */  
    private static final String CRASH_REPORTER_EXTENSION = ".cr";   
       
    /** 保证只有一个CrashHandler实例 */  
    private CrashHandler() {}   
    /** 获取CrashHandler实例 ,单例模式*/  
    public static CrashHandler getInstance() {   
        if (INSTANCE == null) {   
            INSTANCE = new CrashHandler();   
        }   
        return INSTANCE;   
    }   
  
    /**  
     * 初始化,注册Context对象,  
     * 获取系统默认的UncaughtException处理器,  
     * 设置该CrashHandler为程序的默认处理器  
     *   
     * @param ctx  
     */  
    public void init(Context ctx) {   
        mContext = ctx;   
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();   
        Thread.setDefaultUncaughtExceptionHandler(this);   
    }   
  
    /**  
     * 当UncaughtException发生时会转入该函数来处理  
     */  
    @Override  
    public void uncaughtException(Thread thread, Throwable ex) {   
        if (!handleException(ex) && mDefaultHandler != null) {   
            //如果用户没有处理则让系统默认的异常处理器来处理   
            mDefaultHandler.uncaughtException(thread, ex);   
        } else {   
            //Sleep一会后结束程序   
            try {   
                Thread.sleep(3000);   
            } catch (InterruptedException e) {   
                Log.e(TAG, "Error : ", e);   
            }   
            Android.os.Process.killProcess(android.os.Process.myPid());   
            System.exit(10);   
        }   
    }   
  
    /**  
     * 自定义错误处理,收集错误信息  
     * 发送错误报告等操作均在此完成.  
     * 开发者可以根据自己的情况来自定义异常处理逻辑  
     * @param ex  
     * @return true:如果处理了该异常信息;否则返回false  
     */  
    private boolean handleException(Throwable ex) {   
        if (ex == null) {   
            return true;   
        }   
        final String msg = ex.getLocalizedMessage();   
        //使用Toast来显示异常信息   
        new Thread() {   
            @Override  
            public void run() {   
                Looper.prepare();   
                Toast.makeText(mContext, "程序出错啦:" + msg, Toast.LENGTH_LONG)   
                        .show();   
                Looper.loop();   
            }   
  
        }.start();   
        //收集设备信息   
        collectCrashDeviceInfo(mContext);   
        //保存错误报告文件   
        String crashFileName = saveCrashInfoToFile(ex);   
        //发送错误报告到服务器   
        sendCrashReportsToServer(mContext);   
        return true;   
    }   
  
    /**  
     * 在程序启动时候, 可以调用该函数来发送以前没有发送的报告  
     */  
    public void sendPreviousReportsToServer() {   
        sendCrashReportsToServer(mContext);   
    }   
  
    /**  
     * 把错误报告发送给服务器,包含新产生的和以前没发送的.  
     *   
     * @param ctx  
     */  
    private void sendCrashReportsToServer(Context ctx) {   
        String[] crFiles = getCrashReportFiles(ctx);   
        if (crFiles != null && crFiles.length > 0) {   
            TreeSet<String> sortedFiles = new TreeSet<String>();   
            sortedFiles.addAll(Arrays.asList(crFiles));   
  
            for (String fileName : sortedFiles) {   
                File cr = new File(ctx.getFilesDir(), fileName);   
                postReport(cr);   
                cr.delete();// 删除已发送的报告   
            }   
        }   
    }   
  
    private void postReport(File file) {   
        // TODO 使用HTTP Post 发送错误报告到服务器   
        // 这里不再详述,开发者可以根据OPhoneSDN上的其他网络操作   
        // 教程来提交错误报告   
    }   
  
    /**  
     * 获取错误报告文件名  
     * @param ctx  
     * @return  
     */  
    private String[] getCrashReportFiles(Context ctx) {   
        File filesDir = ctx.getFilesDir();   
        FilenameFilter filter = new FilenameFilter() {   
            public boolean accept(File dir, String name) {   
                return name.endsWith(CRASH_REPORTER_EXTENSION);   
            }   
        };   
        return filesDir.list(filter);   
    }   
    /**  
     * 保存错误信息到文件中  
     * @param ex  
     * @return  
     */  
    private String saveCrashInfoToFile(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();   
        mDeviceCrashInfo.put(STACK_TRACE, result);   
  
        try {   
            long timestamp = System.currentTimeMillis();   
            String fileName = "crash-" + timestamp + CRASH_REPORTER_EXTENSION;   
            FileOutputStream trace = mContext.openFileOutput(fileName,   
                    Context.MODE_PRIVATE);   
            mDeviceCrashInfo.store(trace, "");   
            trace.flush();   
            trace.close();   
            return fileName;   
        } catch (Exception e) {   
            Log.e(TAG, "an error occured while writing report file...", e);   
        }   
        return null;   
    }   
  
  
    /**  
     * 收集程序崩溃的设备信息  
     *   
     * @param ctx  
     */  
    public void collectCrashDeviceInfo(Context ctx) {   
        try {   
            PackageManager pm = ctx.getPackageManager();   
            PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),   
                    PackageManager.GET_ACTIVITIES);   
            if (pi != null) {   
                mDeviceCrashInfo.put(VERSION_NAME,   
                        pi.versionName == null ? "not set" : pi.versionName);   
                mDeviceCrashInfo.put(VERSION_CODE, pi.versionCode);   
            }   
        } catch (NameNotFoundException e) {   
            Log.e(TAG, "Error while collect package info", e);   
        }   
        //使用反射来收集设备信息.在Build类中包含各种设备信息,   
        //例如: 系统版本号,设备生产商 等帮助调试程序的有用信息   
//具体信息请参考后面的截图   
        Field[] fields = Build.class.getDeclaredFields();   
        for (Field field : fields) {   
            try {   
                field.setAccessible(true);   
                mDeviceCrashInfo.put(field.getName(), field.get(null));   
                if (DEBUG) {   
                    Log.d(TAG, field.getName() + " : " + field.get(null));   
                }   
            } catch (Exception e) {   
                Log.e(TAG, "Error while collect crash info", e);   
            }   
  
        }   
  
    }   
  


在上面CrashHandler实现中,当错误发生的时候使用Toast显示错误信息,然后收集错误报告并保存在文件中。 发送错误报告代码请读者自己实现。在uncaughtException函数中调用了Thread.sleep(3000);来让线程停止一会是为了显示Toast信息给用户,然后Kill程序。如果你不用Toast来显示信息则可以去除该代码。除了Toast外,开发者还可以选择使用Notification来显示错误内容并让用户选择是否提交错误报告而不是自动提交。关于Notification的实现请读者参考:NotificationManager。在发送错误报道的时候,可以先检测网络是否可用,如果不可用则可以在以后网络情况可用的情况下发送。 网络监测代码如下:

/**  
     * 检测网络连接是否可用  
     * @param ctx  
     * @return true 可用; false 不可用  
     */  
    private boolean isNetworkAvailable(Context ctx) {   
        ConnectivityManager cm =    
            (ConnectivityManager) ctx.getSystemService(Context.CONNECTIVITY_SERVICE);   
        if(cm == null) {   
            return false;   
        }   
        NetworkInfo[] netinfo = cm.getAllNetworkInfo();   
        if(netinfo == null) {   
            return false;   
        }   
        for (int i = 0; i < netinfo.length; i++) {   
            if(netinfo[i].isConnected()) {   
                return true;   
            }   
        }   
        return false;   
    } 

下面是在模拟器和笔者手机(Dell mini3i OPhone1.5系统)上收集的具体信息截图:



Application 实现

实现一个自定义Application来注册CrashHandler. 代码如下:

public class CrashApplication extends Application {   
  
    @Override  
    public void onCreate() {   
        super.onCreate();   
        CrashHandler crashHandler = CrashHandler.getInstance();   
        //注册crashHandler   
        crashHandler.init(getApplicationContext());   
        //发送以前没发送的报告(可选)   
        crashHandler.sendPreviousReportsToServer();   
    }   
       


在AndroidManifest.xml中注册

最后只要在AndroidManifest.xml中注册CrashApplication就可以了。代码如下:

<?xml version="1.0" encoding="utf-8"?>  
<manifest xmlns:Android="http://schemas.android.com/apk/res/android"  
      package="org.goodev.cr"  
      Android:versionCode="1"  
      Android:versionName="1.0">  
    <application Android:icon="@drawable/icon" android:label="@string/app_name"    
        Android:name=".CrashApplication">  
        <activity Android:name=".ReporterTest"  
                  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>  
</manifest>  
  

总结:通过本文示例的方式,开发者可以在程序中收集详细的崩溃信息,从而为调试程序带来便利,如果您的程序还没有该功能赶快加入吧。crashReporter.zip中包含本文使用的项目文件及资源。

本篇文章来源于 Linux公社网站(www.linuxidc.com)  原文链接:http://www.linuxidc.com/Linux/2011-04/34224p2.htm
分享到:
评论

相关推荐

    Android 自定义全局Loading页面

    在Android应用开发中,为了提供良好的用户体验,我们经常需要在数据加载、网络请求或其它耗时操作时显示全局Loading页面,以告知用户程序正在进行后台处理并提示他们稍等片刻。本篇文章将深入探讨如何在Android中...

    android_异常处理_对异常进行全局捕捉

    在Android开发中,异常处理是一个重要环节,尤其是在产品发布后,为了确保应用的稳定性和良好的用户体验,对异常进行全局捕捉就显得尤为重要。Android提供了UncaughtExceptionHandler接口,用于全局捕捉未捕获的异常...

    Android 全局异常处理

    在Android应用开发中,全局异常处理是一个至关重要的环节,它能确保应用在遇到错误时能够稳定运行,并提供良好的用户体验。本文将深入探讨Android全局异常处理的实现方式,以及如何将异常信息存储本地并上传到服务器...

    Android 捕获全局异常处理

    为了提高应用的健壮性,开发者需要学会捕获并处理全局异常。本文将详细讲解如何在Android中实现全局异常捕获,包括在崩溃前显示提示信息、保存错误日志到本地、发送错误报告邮件以及将错误信息上传至后台服务器。 ...

    Android-Android和Java的错误处理库

    在Android应用开发中,错误处理是一项至关重要的任务,它确保了程序的稳定性和用户体验。Java作为Android的主要编程语言,其错误处理机制对于开发者来说是必须掌握的。本篇将深入探讨Android与Java中的错误处理库...

    android全局异常处理

    "android全局异常处理"这个主题关注的就是如何在整个Android应用中设置一个统一的错误处理机制,以便在发生异常时进行记录、通知用户或采取其他必要的恢复措施。 首先,我们需要理解Android应用中的异常类型。在...

    Android全局异常捕获CrashHandler

    2. **Android异常处理流程**: 在Android中,当出现未捕获的异常时,系统会默认调用`Thread.getDefaultUncaughtExceptionHandler()`,并传递异常信息。默认的异常处理器通常会终止应用,显示错误报告。全局异常捕获...

    android工程全局异常处理-将未处理异常日志保存在文件中,可后续开发提交至服务器

    在Android应用开发中,异常处理是一项至关重要的任务,它能够确保程序在遇到错误时不会突然崩溃,而是能够优雅地处理并记录错误信息。本篇主要介绍如何实现一个全局的异常处理机制,以便将未处理的异常日志保存到...

    Android 全局捕获异常消息示例.rar

    android全局异常捕获,全局捕获异常消息示例,当程序发生Uncaught异常的时候,有该类来接管程序,并记录错误日志,使用系统默认的UncaughtException处理类,实现自定义错误处理,收集错误信息 发送错误报告等操作,还...

    Android 全局异常捕获

    当然,全局异常捕获并不能替代对每个可能出现异常的地方进行详细的错误处理。在编写具体业务逻辑时,应尽可能地预测并处理可能出现的异常情况,避免依赖全局异常捕获来处理所有的异常。全局异常捕获应当被视为最后的...

    Android捕获全局异常信息替换系统错误

    首先,我们需要创建一个自定义的应用程序(Application)类,它是整个应用程序的入口点,可以在此监听并处理全局异常。在AndroidManifest.xml文件中,将默认的Application类替换为我们自定义的类,例如`...

    android全局异常捕获

    总结起来,Android全局异常捕获是一个关键的错误处理机制,它可以帮助开发者在异常发生时采取适当的措施,防止应用崩溃,提升用户体验,同时也有利于问题的定位和修复。通过创建自定义的`Thread....

    android全局异常捕获 exception_global

    标题"android全局异常捕获 exception_global"和描述都指向了这个主题,表明我们将探讨如何在Android应用程序中实现全面的异常处理。 全局异常捕获通常涉及到创建一个全局的错误处理器,它能在应用程序的任何地方...

    AndroidCrash全局崩溃异常捕获

    AndroidCrash全局崩溃异常捕获机制就是为了这一目的而设计的。它是一种技术手段,用于在应用程序出现未预期错误导致崩溃时,提供友好的用户反馈界面,同时收集设备信息和异常详情,并将这些数据发送到服务器进行分析...

    android适用与全局的网络请求dialog提示点击重新加载

    7. **异常处理**:对于可能出现的网络异常,如无网络连接、超时等,应有适当的错误处理机制,提供友好的错误信息提示。 通过以上步骤,我们可以创建一个适应于Android应用全局的网络请求对话框,它能在数据加载或...

    Android全局异常捕获

    这个处理器会捕获那些没有被其他异常处理器处理的未捕获异常,从而提供一个统一的错误处理机制。 下面我们将详细探讨如何在Android中实现全局异常捕获,以及涉及的相关技术。 1. **自定义异常处理器**: - 首先,...

    android全局crash捕获

    // 可能的错误处理,如重启应用或提示用户 handleException(ex); // 默认处理,结束进程 System.exit(1); } } ``` 2. **设置全局的异常处理器** 在应用启动时,我们需要将自定义的异常处理器设置为全局处理器...

    android 错误收集工具

    1. **异常捕获**:CrashHelper会在应用程序的全局异常处理器中注册,当程序发生未捕获异常时,它会优先捕获到这些异常。 2. **错误信息收集**:在捕获到异常后,CrashHelper会收集一系列关键信息,如异常类型、堆栈...

    安卓全局异常捕获处理

    "安卓全局异常捕获处理"是Android应用开发中的一个核心概念,旨在统一处理应用程序中可能出现的各种异常,防止程序崩溃,并提供友好的错误提示或日志记录,以便于开发者进行调试和优化。 首先,我们来看一下如何...

    Android全局异常捕获及上传服务器

    全局异常捕获是指在整个应用程序运行过程中,无论在哪个线程、哪个模块抛出未被捕获的异常时,都能进行有效的处理,避免应用突然崩溃。这通常通过实现自定义的异常处理器来实现。本主题将深入探讨Android全局异常...

Global site tag (gtag.js) - Google Analytics