APP开发实战152-Android使用UncaughtExceptionHandler处理异常

来源:互联网 发布:淘宝客服骂人怎么投诉 编辑:程序博客网 时间:2024/05/18 02:37

25.2.2使用UncaughtExceptionHandler处理异常

在使用APP的时候,在任何时候,都可能遇到异常,能够预判到的异常只是极少数,对于不能预判到的异常,可以统一使用UncaughtExceptionHandler接口处理。

如果子线程中出现异常,在主线程代码中,使用try…catch…是无法捕获到异常的,必须使用UncaughtExceptionHandler来进行处理。

在实现UncaughtExceptionHandler时,必须重载uncaughtException(Threadthread, Throwable ex) ,如果没有实现该接口,也就是没有显示捕捉异常,则ex为空,否则ex不为空,thread 则为出异常的线程。

如下代码实现UncaughtExceptionHandler接口,并保存异常日志到本机:

publicclass rwUncaughtExceptionHandler implements

Thread.UncaughtExceptionHandler{
    privateThread.UncaughtExceptionHandler mDefaultHandler;
    public static final String TAG ="rwUncaught";

    private staticrwUncaughtExceptionHandler INSTANCE = new

rwUncaughtExceptionHandler();

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

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

SimpleDateFormat("yyyy-MM-dd-HH-mm-ss");
    /** 保证只有一个rwUncaughtExceptionHandler实例 */
    private rwUncaughtExceptionHandler(){

    }

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

    public void init(Context context){
        mContext = context;

        //获取系统默认的UncaughtException处理器
        mDefaultHandler =Thread.getDefaultUncaughtExceptionHandler();

        //设置该rwUncaughtExceptionHandler为程序的默认处理器
       Thread.setDefaultUncaughtExceptionHandler(this);
   }

    @Override
    public void uncaughtException(Threadthread, Throwable ex) {
        if (!handleException(ex)&& mDefaultHandler != null) {
            //如果用户没有处理则让系统默认的异常处理器来处理
           mDefaultHandler.uncaughtException(thread, ex);
        } else {
            try {
                Thread.sleep(2000);
            } catch (InterruptedExceptione) {
                Log.e(TAG, "error :", e);
            }
            Intent intent = newIntent(mContext.getApplicationContext(), ViewPagerDemo.class);
            PendingIntent restartIntent =PendingIntent.getActivity(mContext.getApplicationContext(), 0,
                   intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK),
                    0);
            //退出程序
            AlarmManager mgr = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
            mgr.set(AlarmManager.RTC,System.currentTimeMillis() + 1000,
                    restartIntent); // 1秒钟后重启应用

//关闭所有的Activity
           exceptionHandlerApplication.finishActivity();
       }
    }

    /**
     * 自定义错误处理,收集错误信息发送错误报告等操作均在此完成.
     *
     * @param ex
     * @return true:如果处理了该异常信息;否则返回false.
     */
    private booleanhandleException(Throwable ex) {
        if (ex == null) {
            return false;
        }
        //使用Toast来显示异常信息
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext,"很抱歉,程序出现异常,即将退出并重启。",
                       Toast.LENGTH_SHORT).show();
                Looper.loop();
            }
        }.start();

        // 收集设备参数信息
        collectDeviceInfo(mContext);
        // 保存日志文件
        saveCrashInfo(ex);

        return true;
    }

    /**
     * 收集设备参数信息
     *
     * @param context
     */
    public void collectDeviceInfo(Contextcontext) {
        try {
            PackageManager pm =context.getPackageManager();// 获得包管理器
            PackageInfo pi =pm.getPackageInfo(context.getPackageName(),
                   PackageManager.GET_ACTIVITIES);// 得到该应用的信息,即主Activity
            if (pi != null) {
                String versionName =pi.versionName == null ? "null"
                        : pi.versionName;
                String versionCode =pi.versionCode + "";
                info.put("versionName",versionName);
               info.put("versionCode", versionCode);

                /**
                 * 获取手机型号,系统版本,以及SDK版本
                 */
                info.put("手机型号:",android.os.Build.MODEL);
                info.put("系统版本",""+android.os.Build.VERSION.SDK);
                info.put("Android版本",android.os.Build.VERSION.RELEASE);
            }
        } catch(PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
               field.setAccessible(true);
                info.put(field.getName(),field.get("").toString());
                Log.d(TAG,field.getName() + ":" + field.get(""));
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch(IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private String saveCrashInfo(Throwableex) {
        StringBuffer sb = new StringBuffer();
        for (Map.Entry<String,String> entry : info.entrySet()) {
            String key = entry.getKey();
            String value =entry.getValue();
            sb.append(key + "="+ value + "\r\n");
        }
        Writer writer = newStringWriter();
        PrintWriter pw = newPrintWriter(writer);
        ex.printStackTrace(pw);
        Throwable cause = ex.getCause();
        // 循环着把所有的异常信息写入writer中
        while (cause != null) {
            cause.printStackTrace(pw);
            cause = cause.getCause();
        }
        pw.close();// 记得关闭
        String result =writer.toString();
        sb.append(result);
        // 保存文件
        long timetamp =System.currentTimeMillis();
        String time = format.format(newDate());

        String fileName = "crash_"+ time + "_"+ ".log";
        if(Environment.getExternalStorageState().equals(
               Environment.MEDIA_MOUNTED)) {
            try {
                File dir = new

File(Environment.getExternalStorageDirectory().getAbsolutePath()+ File.separator + "crash");
               Log.i("CrashHandler", dir.toString());
                if (!dir.exists())
                    dir.mkdir();
                FileOutputStream fos =new FileOutputStream(new File(dir,
                        fileName));
               fos.write(sb.toString().getBytes());
                fos.close();
                return fileName;
            } catch(FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

}


实现Application的子类,初始化异常处理类:

publicclass exceptionHandlerApplication extends Application {
    private static ArrayList<Activity>list = new ArrayList<Activity>();

    @Override
    public void onCreate() {
        super.onCreate();
       rwUncaughtExceptionHandler.getInstance().init(this);
    }

    /**
     * Activity关闭时,删除Activity列表中的Activity对象*/
    public void removeActivity(Activitya){
        list.remove(a);
    }

    /**
     * 向Activity列表中添加Activity对象*/
    public static voidaddActivity(Activity a){
        list.add(a);
    }

    /**
     * 关闭Activity列表中的所有Activity*/
    public static void finishActivity(){
        for (Activity activity : list) {
            if (null != activity) {
                activity.finish();
            }
        }
        //杀死该应用进程
       android.os.Process.killProcess(android.os.Process.myPid());
    }

}

 

在创建每个Activity时,都需要把当前Activity加入到列表中,以便后续退出应用时关闭:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   initView();

  exceptionHandlerApplication.addActivity(this);
}

还需在AndroidManifest.xml中增加自定义Application类的声明:

<application
    android:name=".exceptionHandlerApplication"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme">
    <activity
        android:name=".MainActvity"
        android:label="@string/title_activity_view_pager_demo">
        <intent-filter>
            <action android:name="android.intent.action.MAIN"/>

            <category android:name="android.intent.category.LAUNCHER"/>
        </intent-filter>
    </activity>
    <activity
        android:name=".ListActivity">
    </activity>
</application>

0 0