Android中处理崩溃闪退错误

摘要:
但首先,让我们了解以下两个类:android。应用程序。应用程序和java。lang.线程未捕获异常处理程序应用程序:用于管理应用程序的全局状态。当应用程序启动时,将首先创建应用程序,然后根据情况启动相应的活动和服务。在本例中,未捕获的异常处理程序将注册到自定义增强版的Application中。如果程序中出现未捕获的异常,默认情况下会弹出系统中的强制关闭对话框。我们需要实现这个接口并注册它,因为程序在默认情况下不会捕获异常处理。

Android中处理崩溃闪退异常

大家都知道,现在安装Android系统的手机版本和设备千差万别,在模拟器上运行良好的程序安装到某款手机上说不定就出现崩溃的现象,开发者个人不可能购买所有设备逐个调试,所以在程序发布出去之后,如果出现了崩溃现象,开发者应该及时获取在该设备上导致崩溃的信息,这对于下一个版本的bug修复帮助极大,所以今天就来介绍一下如何在程序崩溃的情况下收集相关的设备参数信息和具体的异常信息,并发送这些信息到服务器供开发者分析和调试程序。

我们需要的是软件有一个全局的异常捕获器,当出现一个我们没有发现的异常时,捕获这个异常,并且将异常信息记录下来,上传到服务器公开发这分析出现异常的具体原因。不过首先我们还是来了解以下两个类:android.app.Application和java.lang.Thread.UncaughtExceptionHandler。

Application:用来管理应用程序的全局状态。在应用程序启动时Application会首先创建,然后才会根据情况(Intent)来启动相应的Activity和Service。本示例中将在自定义加强版的Application中注册未捕获异常处理器。

Thread.UncaughtExceptionHandler:线程未捕获异常处理器,用来处理未捕获异常。如果程序出现了未捕获异常,默认会弹出系统中强制关闭对话框。我们需要实现此接口,并注册为程序中默认未捕获异常处理。这样当未捕获异常发生时,就可以做一些个性化的异常处理操作。

AppException.java实现了Thread.UncaughtExceptionHandler,使我们用来处理未捕获异常的主要成员,代码如下:

packagecn.com.ista.pdachina.app;
importjava.io.File;  
importjava.io.FileOutputStream;  
importjava.io.PrintWriter;  
importjava.io.StringWriter;  
importjava.io.Writer;  
importjava.lang.Thread.UncaughtExceptionHandler;  
importjava.lang.reflect.Field;  
importjava.text.DateFormat;  
importjava.text.SimpleDateFormat;  
importjava.util.Date;  
importjava.util.HashMap;  
importjava.util.Map;  
importandroid.content.Context;  
importandroid.content.pm.PackageInfo;  
importandroid.content.pm.PackageManager;  
importandroid.content.pm.PackageManager.NameNotFoundException;  
importandroid.os.Build;  
importandroid.os.Environment;  
importandroid.os.Looper;  
importandroid.util.Log;  
importandroid.widget.Toast;  
/**
 * AppException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告. 
 *  
 * @authoruser 
 *  
 */  
public class AppException implementsUncaughtExceptionHandler {  
    public static final String TAG = "AppException";  
    //系统默认的UncaughtException处理类   
    privateThread.UncaughtExceptionHandler mDefaultHandler;  
    //CrashHandler实例  
    private static AppException INSTANCE = newAppException();  
    //程序的Context对象  
    privateContext mContext;  
    //用来存储设备信息和异常信息  
    private Map<String, String> infos = new HashMap<String, String>();  
    //用于格式化日期,作为日志文件名的一部分  
    private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");  
    /**保证只有一个AppEeception实例 */  
    privateAppException() {  
    }  
    /**获取AppException实例 ,单例模式 */  
    public staticAppException getInstance() {  
        returnINSTANCE;  
    }  
    /**
     * 初始化 
     *  
     * @paramcontext 
     */  
    public voidinit(Context context) {  
        mContext =context;  
        //获取系统默认的UncaughtException处理器  
        mDefaultHandler =Thread.getDefaultUncaughtExceptionHandler();  
        //设置该CrashHandler为程序的默认处理器  
        Thread.setDefaultUncaughtExceptionHandler(this);  
    }  
    /**
     * 当UncaughtException发生时会转入该函数来处理 
     */
    @Override  
    public voiduncaughtException(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);  
        }  
    }  
    /**
     * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成. 
     *  
     * @paramex 
     * @returntrue:如果处理了该异常信息;否则返回false. 
     */  
    private booleanhandleException(Throwable ex) {  
        if (ex == null) {  
            return false;  
        }  
        //使用Toast来显示异常信息  
        newThread() {  
            @Override  
            public voidrun() {  
                Looper.prepare();  
                Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_LONG).show();  
                Looper.loop();  
            }  
        }.start();  
        //收集设备参数信息   
collectDeviceInfo(mContext);  
        //保存日志文件   
saveCrashInfo2File(ex);  
        return true;  
    }  
    /**
     * 收集设备参数信息 
     * @paramctx 
     */  
    public voidcollectDeviceInfo(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) {  
            Log.e(TAG, "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());  
                Log.d(TAG, field.getName() + " : " + field.get(null));  
            } catch(Exception e) {  
                Log.e(TAG, "an error occured when collect crash info", e);  
            }  
        }  
    }  
    /**
     * 保存错误信息到文件中 
     *  
     * @paramex 
     * @return返回文件名称,便于将文件传送到服务器 
     */  
    privateString saveCrashInfo2File(Throwable ex) {  
        StringBuffer sb = newStringBuffer();  
        for (Map.Entry<String, String>entry : infos.entrySet()) {  
            String key =entry.getKey();  
            String value =entry.getValue();  
            sb.append(key + "=" + value + "
");  
        }  
        Writer writer = newStringWriter();  
        PrintWriter printWriter = newPrintWriter(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(newDate());  
            String fileName = "log-" + time + "-" + timestamp + ".log";  
            if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {  
                String path = android.os.Environment.getExternalStorageDirectory().getAbsolutePath() + "/PdaChina/";  
                File dir = newFile(path);  
                if (!dir.exists()) {  
                    dir.mkdirs();  
                }  
                FileOutputStream fos = new FileOutputStream(path +fileName);  
                fos.write(sb.toString().getBytes());  
                fos.close();  
            }  
            returnfileName;  
        } catch(Exception e) {  
            Log.e(TAG, "an error occured while writing file...", e);  
        }  
        return null;  
    }  
}  

完成这个AppException后,我们需要在一个Application环境中让其运行,为此,我们继承android.app.Application,添加自己的代码,AppContext.java代码如下:

packagecn.com.ista.pdachina.app;
importandroid.app.Application;
importandroid.content.Context;
/**
 * 全局获取上下文类:用于保存和调用全局应用配置及访问网络数据
 * @authorguopeng
 * @version1.0
 * @created 2015-10-26
 */
public class AppContext extendsApplication {
    private staticContext instance;
    @Override
    public voidonCreate() 
    {
        instance =getApplicationContext();
        AppException appException =AppException.getInstance();
        appException.init(instance);
    }
    public staticContext getContext()
    {
        returninstance;
    }
}

最后,为了让我们的AppContext取代android.app.Application的地位,在我们的代码中生效,我们需要修改AndroidManifest.xml:

<application android:name=".CrashApplication" ...>  
</application>  

因为我们上面的AppException中,遇到异常后要保存设备参数和具体异常信息到SDCARD,所以我们需要在AndroidManifest.xml中加入读写SDCARD权限:

<!--SD卡读写权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> 

大功告成

免责声明:文章转载自《Android中处理崩溃闪退错误》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇【转】使用itms-services从浏览器发布iOS App遇到的问题总结linux 网卡buffer大小下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

StackExchange.Redis.DLL 操作redis加强版

直接引用StackExchange.Redis.dll这一个dll来操作redis App.config配置 <?xml version="1.0" encoding="utf-8" ?> <configuration> <startup> <supportedRuntime...

.NET操作Excel笔记

如果你新建一个项目的话,首先要添加Microsoft.Office.Core 与Microsoft.Office.Interop.Exce这两个应用,然后就能很方便的操作了,示例代码(只实现了简单的读写):   1 privateExcel._Application excelApp;  2 privateWorkbook wbclass; ...

[WEB前端工程师编程能力成长之路]

【背景】 如果你是刚进入WEB前端研发领域,想试试这潭水有多深,看这篇文章吧;如果你是做了两三年WEB产品前端研发,迷茫找不着提高之路,看这篇文章吧;如果你是四五年的前端开发高手,没有难题能难得住你的寂寞高手,来看这篇文章吧; WEB 前端研发工程师,在国内是一个朝阳职业,自07-08年正式有这个职业以来,也不过三四年的时间。这个领域没有学校的正规教育,没...

.NET系统框架

本书是一本讲解.NET技术的书籍,目标读者群也是在.NET框架(.NET Framework)下进行开发的程序员,因此我们无法回避的问题就是:什么是.NET框架?它包含了哪些内容?为开发程序提供了哪些支持?很多朋友对这类个问题的第一反应可能是.NET框架所提供的庞大类库及编写代码所采用的C#语言,实际上远不止这些。 要描述.NET框架,自然会遇到与其相关的...

ASP.NET Core的Kestrel服务器(转载)

Kestrel是一个基于libuv的跨平台ASP.NET Core web服务器,libuv是一个跨平台的异步I/O库。ASP.NET Core模板项目使用Kestrel作为默认的web服务器。Kestrel支持以下功能: HTTPS 用于启用不透明升级的WebSockets 位于Nginx之后的高性能Unix sockets Kestrel 被.N...

C# PropertyGrid控件应用心得

最近项目中做一模块时偶发奇想,希望使用propertygrid的控件实现类似visual studio的属性样式,于是拿来一用,发现还真不是自己想象的那么简单,如果要实现一个比较好的展示,还真的需要不少技巧,通过自己的实践和网络的力量,“逢山开道,遇水搭桥”,总算是摸出一些门道,不敢私藏,拿出来与大家分享,呵呵。 先来转一个基础的,源自msdn http:...