zoukankan      html  css  js  c++  java
  • Android 对程序异常崩溃的捕捉

    转载博客:http://blog.csdn.net/i_lovefish/article/details/17719081

    以下为异常捕捉处理代码:  
    import java.io.BufferedReader;  
    import java.io.File;  
    import java.io.FileInputStream;  
    import java.io.FileNotFoundException;  
    import java.io.FileOutputStream;  
    import java.io.IOException;  
    import java.io.InputStreamReader;  
    import java.io.PrintWriter;  
    import java.io.StringWriter;  
    import java.io.Writer;  
    import java.lang.Thread.UncaughtExceptionHandler;  
    import java.lang.reflect.Field;  
    import java.text.DateFormat;  
    import java.text.SimpleDateFormat;  
    import java.util.Date;  
    import java.util.HashMap;  
    import java.util.Map;  
      
    import android.content.Context;  
    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.util.Log;  
    import android.widget.Toast;  
          
    /**   
     * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.  
     *  
     *  需要在Application中注册,为了要在程序启动器就监控整个程序。 
     */public class CrashHandler implements UncaughtExceptionHandler {      
              
        public static final String TAG = "CrashHandler";      
              
        //系统默认的UncaughtException处理类       private Thread.UncaughtExceptionHandler mDefaultHandler;      
        //CrashHandler实例      private static CrashHandler instance;  
       //程序的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() {      
            if(instance == null)  
                instance = new CrashHandler();     
            return instance;      
        }      
          
        /**   
         * 初始化   
         */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;      
            }      
            //收集设备参数信息               collectDeviceInfo(mContext);      
              
            //使用Toast来显示异常信息      new Thread() {      
                @Override      
                public void run() {      
                    Looper.prepare();      
                    Toast.makeText(mContext, "很抱歉,程序出现异常,即将退出.", Toast.LENGTH_SHORT).show();      
                    Looper.loop();      
                }      
            }.start();      
            //保存日志文件               saveCatchInfo2File(ex);    
            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) {      
                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);      
                }      
            }      
        }      
          
        /**   
         * 保存错误信息到文件中   
         *    
         * @param ex   
         * @return  返回文件名称,便于将文件传送到服务器   
         */private String saveCatchInfo2File(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 + "
    ");      
            }      
                  
            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 = "/mnt/sdcard/crash/";      
                    File dir = new File(path);      
                    if (!dir.exists()) {      
                        dir.mkdirs();      
                    }      
                    FileOutputStream fos = new FileOutputStream(path + fileName);      
                    fos.write(sb.toString().getBytes());    
                    //发送给开发人员  
                    sendCrashLog2PM(path+fileName);  
                    fos.close();      
                }      
                return fileName;      
            } catch (Exception e) {      
                Log.e(TAG, "an error occured while writing file...", e);      
            }      
            return null;      
        }      
          
        /** 
         * 将捕获的导致崩溃的错误信息发送给开发人员 
         *  
         * 目前只将log日志保存在sdcard 和输出到LogCat中,并未发送给后台。 
         */private void sendCrashLog2PM(String fileName){  
            if(!new File(fileName).exists()){  
                Toast.makeText(mContext, "日志文件不存在!", Toast.LENGTH_SHORT).show();  
                return;  
            }  
            FileInputStream fis = null;  
            BufferedReader reader = null;  
            String s = null;  
            try {  
                fis = new FileInputStream(fileName);  
                reader = new BufferedReader(new InputStreamReader(fis, "GBK"));  
                while(true){  
                    s = reader.readLine();  
                    if(s == null) break;  
                    //由于目前尚未确定以何种方式发送,所以先打出log日志。  
                    Log.i("info", s.toString());  
                }  
            } catch (FileNotFoundException e) {  
                e.printStackTrace();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }finally{   // 关闭流  try {  
                    reader.close();  
                    fis.close();  
                } catch (IOException e) {  
                    e.printStackTrace();  
                }  
            }  
        }  
    }

    针对异常的捕捉要进行全局监控整个项目,所以要将其在Application中注册(也就是初始化):

    import android.app.Application;  
      
    public class CrashApplication extends Application {  
      
        @Override  
        public void onCreate() {  
            super.onCreate();  
            CrashHandler catchHandler = CrashHandler.getInstance();  
            catchHandler.init(getApplicationContext());  
        }  
    }

    现在模拟一个空指针异常:

    import android.app.Activity;  
    import android.os.Bundle;  
      
    public class CatchExceptionLogActivity extends Activity {  
        /** Called when the activity is first created. */private String s;  
        @Override  
        public void onCreate(Bundle savedInstanceState) {  
            super.onCreate(savedInstanceState);  
            setContentView(R.layout.main);  
            System.out.println(s.equals("hello"));  // s没有进行赋值,所以会出现NullPointException异常      }  
    }

    别忘了在配置文件中对Application进行注册:

    <?xml version="1.0" encoding="utf-8"?>  
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"  
        package="com.forms.catchlog"  
        android:versionCode="1"  
        android:versionName="1.0" >  
      
        <uses-sdk android:minSdkVersion="8" />  
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>  
          
      
        <application  
            android:icon="@drawable/ic_launcher"  
            android:label="@string/app_name"   
            <span style="color:#ff0000;">android:name=".CrashApplication"></span>  
            <activity  
                android:label="@string/app_name"  
                android:name=".CatchExceptionLogActivity" >  
                <intent-filter >  
                    <action android:name="android.intent.action.MAIN" />  
      
                    <category android:name="android.intent.category.LAUNCHER" />  
                </intent-filter>  
            </activity>  
        </application>  
      
    </manifest>
    添加异常捕获之后的日志提示

    由于Android设备各异,第三方定制的Android系统也非常多,我们不可能对所有的设备场景都进行测试,因而开发一款完全无bug的应用几乎是不可能的任务,那么当应用在用户的设备上Force Close时,我们是不是可以捕获这个错误,记录用户的设备信息,然后让用户选择是否反馈这些堆栈信息,通过这种bug反馈方式,我们可以有针对性地对bug进行修复。

    当我们的的应用由于运行时异常导致Force Close的时候,可以设置主线程的UncaughtExceptionHandler,实现捕获运行时异常的堆栈信息。同时用户可以把堆栈信息通过发送邮件的方式反馈给我们。下面是实现的代码:

    代码下载请按此

    例子:点击按钮后,会触发一个NullPointerException的运行时异常,这个例子实现了捕获运行时异常,发送邮件反馈设备和堆栈信息的功能。

    界面1(触发运行时异常)

      

    界面2(发送堆栈信息)

      

    TestActivity.java
    package com.zhuozhuo; 
     
    import java.io.PrintWriter; 
    import java.io.StringWriter; 
    import java.lang.Thread.UncaughtExceptionHandler; 
     
    import android.app.Activity; 
    import android.content.Intent; 
    import android.net.Uri; 
    import android.os.Build; 
    import android.os.Bundle; 
    import android.util.Log; 
    import android.view.View; 
    import android.view.View.OnClickListener; 
    import android.widget.EditText; 
    import android.widget.TextView; 
     
    public class TestActivity extends Activity { 
        /** Called when the activity is first created. */ 
     
     
        @Override 
        public void onCreate(Bundle savedInstanceState) { 
            super.onCreate(savedInstanceState); 
            setContentView(R.layout.main); 
            Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {//给主线程设置一个处理运行时异常的handler  
                @Override 
                public void uncaughtException(Thread thread, final Throwable ex) { 
     
                    StringWriter sw = new StringWriter(); 
                    PrintWriter pw = new PrintWriter(sw); 
                    ex.printStackTrace(pw); 
                     
                    StringBuilder sb = new StringBuilder(); 
                     
                    sb.append("Version code is "); 
                    sb.append(Build.VERSION.SDK_INT + "
    ");//设备的Android版本号 
                    sb.append("Model is "); 
                    sb.append(Build.MODEL+"
    ");//设备型号                 sb.append(sw.toString()); 
     
                    Intent sendIntent = new Intent(Intent.ACTION_SENDTO); 
                    sendIntent.setData(Uri.parse("mailto:csdn@csdn.com"));//发送邮件异常到csdn@csdn.com邮箱 
                    sendIntent.putExtra(Intent.EXTRA_SUBJECT, "bug report");//邮件主题 
                    sendIntent.putExtra(Intent.EXTRA_TEXT, sb.toString());//堆栈信息                 startActivity(sendIntent); 
                    finish(); 
                } 
            }); 
             
            findViewById(R.id.button).setOnClickListener(new OnClickListener() { 
                 
                @Override 
                public void onClick(View v) { 
                    Integer a = null; 
                    a.toString();//触发nullpointer运行时错误                  
                } 
            }); 
             
        } 
    } 
    main.xml
    <?xml version="1.0" encoding="utf-8"?> 
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
        android:orientation="vertical" android:layout_width="fill_parent" 
        android:layout_height="fill_parent"> 
        <TextView android:layout_width="fill_parent" 
            android:layout_height="wrap_content" android:text="@string/hello" /> 
        <EditText android:id="@+id/editText1" android:layout_width="match_parent" 
            android:text="点击按钮触发运行时异常" android:layout_height="wrap_content" 
            android:layout_weight="1" android:gravity="top"></EditText> 
        <Button android:text="按钮" android:id="@+id/button" 
            android:layout_width="wrap_content" android:layout_height="wrap_content" 
            android:layout_gravity="center_horizontal"></Button> 
    </LinearLayout> 

     

    08-01 17:25:10.273 25744-25744/com.errey I/info: SUPPORTED_64_BIT_ABIS=[Ljava.lang.String;@392c43ac
    08-01 17:25:10.274 25744-25744/com.errey I/info: versionCode=1                    非用户可见版本号
    08-01 17:25:10.274 25744-25744/com.errey I/info: BOARD=m3note                     手机型号
    08-01 17:25:10.275 25744-25744/com.errey I/info: BOOTLOADER=unknown               引导程序=未知的
    08-01 17:25:10.275 25744-25744/com.errey I/info: TYPE=user                        类型=用户
    08-01 17:25:10.275 25744-25744/com.errey I/info: ID=LMY47I                        ID
    08-01 17:25:10.275 25744-25744/com.errey I/info: TIME=1466191121000               时间
    08-01 17:25:10.275 25744-25744/com.errey I/info: BRAND=Meizu                      手机品牌
    08-01 17:25:10.276 25744-25744/com.errey I/info: TAG=Build
    08-01 17:25:10.276 25744-25744/com.errey I/info: SERIAL=91QECP43FK5Q              手机序列号
    08-01 17:25:10.276 25744-25744/com.errey I/info: HARDWARE=mt6755
    08-01 17:25:10.276 25744-25744/com.errey I/info: SUPPORTED_ABIS=[Ljava.lang.String;@177f6875
    08-01 17:25:10.277 25744-25744/com.errey I/info: CPU_ABI=arm64-v8a
    08-01 17:25:10.277 25744-25744/com.errey I/info: RADIO=unknown
    08-01 17:25:10.277 25744-25744/com.errey I/info: IS_DEBUGGABLE=false               是否可调试
    08-01 17:25:10.277 25744-25744/com.errey I/info: MANUFACTURER=Meizu                制造商
    08-01 17:25:10.277 25744-25744/com.errey I/info: SUPPORTED_32_BIT_ABIS=[Ljava.lang.String;@3c593d5f
    08-01 17:25:10.277 25744-25744/com.errey I/info: TAGS=release-keys
    08-01 17:25:10.277 25744-25744/com.errey I/info: CPU_ABI2=
    08-01 17:25:10.277 25744-25744/com.errey I/info: UNKNOWN=unknown
    08-01 17:25:10.277 25744-25744/com.errey I/info: USER=flyme                         用户
    08-01 17:25:10.277 25744-25744/com.errey I/info: FINGERPRINT=Meizu/meizu_m3note/m3note:5.1/LMY47I/1466190928:user/release-keys
    08-01 17:25:10.277 25744-25744/com.errey I/info: HOST=mz-builder-8
    08-01 17:25:10.277 25744-25744/com.errey I/info: PRODUCT=m3note
    08-01 17:25:10.277 25744-25744/com.errey I/info: versionName=1.0                    用户可见的版本号
    08-01 17:25:10.277 25744-25744/com.errey I/info: DISPLAY=Flyme 5.1.3.4A             手机系统版本号
    08-01 17:25:10.277 25744-25744/com.errey I/info: MODEL=m3 note                      手机型号
    08-01 17:25:10.277 25744-25744/com.errey I/info: DEVICE=m3note                      设备
    08-01 17:25:10.277 25744-25744/com.errey I/info: java.lang.ClassCastException: android.widget.TextView cannot be cast to android.widget.Button    错误日志
    08-01 17:25:10.277 25744-25744/com.errey I/info:     at com.errey.Main2Activity.onClick(Main2Activity.java:24)
    08-01 17:25:10.277 25744-25744/com.errey I/info:     at android.view.View.performClick(View.java:4909)
    08-01 17:25:10.277 25744-25744/com.errey I/info:     at android.view.View$PerformClick.run(View.java:20390)
    08-01 17:25:10.277 25744-25744/com.errey I/info:     at android.os.Handler.handleCallback(Handler.java:821)
    08-01 17:25:10.277 25744-25744/com.errey I/info:     at android.os.Handler.dispatchMessage(Handler.java:104)
    08-01 17:25:10.277 25744-25744/com.errey I/info:     at android.os.Looper.loop(Looper.java:194)
    08-01 17:25:10.277 25744-25744/com.errey I/info:     at android.app.ActivityThread.main(ActivityThread.java:5742)
    08-01 17:25:10.277 25744-25744/com.errey I/info:     at java.lang.reflect.Method.invoke(Native Method)
    08-01 17:25:10.278 25744-25744/com.errey I/info:     at java.lang.reflect.Method.invoke(Method.java:372)
    08-01 17:25:10.278 25744-25744/com.errey I/info:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1104)
    08-01 17:25:10.278 25744-25744/com.errey I/info:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:870)





  • 相关阅读:
    eclipse中文乱码问题解决方案
    修改Tomcat的JDK目录
    Tomcat 5.5 修改服务器的侦听端口
    HTML DOM教程 27HTML DOM Button 对象
    HTML DOM教程 24HTML DOM Frameset 对象
    Navicat for MySQL v8.0.27 的注册码
    HTML DOM教程 25HTML DOM IFrame 对象
    Tomcat 5.5 的下载和安装
    android manifest相关属性
    ubuntu10.04 下 eclipse 小结
  • 原文地址:https://www.cnblogs.com/jeffen/p/6970633.html
Copyright © 2011-2022 走看看