模版方法模式与实战:编写Android应用崩溃处理工具类

模版方法模式与实战:编写Android应用崩溃处理工具类

原理和应用

  • AsyncTask
  • View的绘制流程
    等等。。

模版方法模式注重于
- 封装不可变部分,扩展可变部分
- 提取公共部分代码,便于维护


比如View的绘制流程:

  • public final void measure(..) –> prptected void onMeasure(..)
  • public final void layout(..) –> prptected void onLayout(..)
  • public final void draw(..) –> prptected void onDraw(..)

xxx方法定义为了final,表示核心逻辑的顺序不能修改;onMeasure可以被重写,意味着可以对这部分进行自定义(扩展)。

流程确定,把具体实现细节抽象出来。

实战:应用崩溃处理

使用模版方法模式实现一个应用崩溃处理工具类。

崩溃处理的两个个流程:
- 收集崩溃数据
- 保存崩溃数据
- 应用杀死前的回调处理

我们把这三个具体细节抽象出来,让子类去实现:

public abstract class CrashHandler<T> {

    abstract T collectData(Context context, Thread t, Throwable e);

    abstract void saveData(Context context, T data);

    abstract void onProcessEnd(Context context);
}

然后编写崩溃处理的逻辑:

public void install(Context context) {
        mContext = context;
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(final Thread t, final Throwable e) {
                new Thread() {
                    @Override
                    public void run() {
                        //执行子类实现的具体逻辑
                        saveData(mContext, collectData(mContext, t, e));
                        //退出程序
                        android.os.Process.killProcess(android.os.Process.myPid());
                        System.exit(1);
                    }
                }.start();
            }
        });
    }

整个工具代码如下:

抽象类CrashHandler:

package com.newsapp.tool;

import android.content.Context;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

/**
 * 崩溃处理抽象类
 * @author mingC
 * @date 2018/8/5
 */
public abstract class CrashHandler<T> {

    private Context mContext;

    /**
     * 崩溃数据收集
     * @param t 崩溃线程
     * @param e 崩溃原因
     * @return 崩溃数据
     */
    abstract T collectData(Context context, Thread t, Throwable e);

    /**
     * 保存崩溃数据,可以在这里做保存到文件或者上传到服务器的操作
     * @param data
     */
    abstract void saveData(Context context, T data);

    /**
     * 在应用被杀死之前最后做点什么
     */
    abstract void onProcessEnd(Context context);

    private void showTipToast() {
        new Thread() {
            @Override
            public void run() {
                Looper.prepare();
                Toast.makeText(mContext, "很抱歉,程序出现异常即将退出.", Toast.LENGTH_LONG).show();
                Looper.loop();
            }
        }.start();
    }

    public void install(Context context) {
        mContext = context;
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
            @Override
            public void uncaughtException(final Thread t, final Throwable e) {
                e.printStackTrace();
                showTipToast();
                Log.e("CrashHandler", "线程" + t.getName() + "出现未捕获的异常,正在进行崩溃处理...");
                final long beginTime = System.currentTimeMillis();
                new Thread() {
                    @Override
                    public void run() {
                        long transactTime = System.currentTimeMillis() - beginTime;
                        //执行子类实现的具体逻辑
                        saveData(mContext, collectData(mContext, t, e));
                        Log.e("CrashHandler", "崩溃处理完成,处理时间为(ms):" + transactTime);
                        try {
                            Thread.sleep(1500);
                        } catch (InterruptedException e1) {
                            e1.printStackTrace();
                        }
                        //留给子类扩展
                        onProcessEnd(mContext);
                        // 退出程序,有些机型会退出失败直到ANR
                        android.os.Process.killProcess(android.os.Process.myPid());
                        System.exit(1);
                    }
                }.start();
            }
        });
    }
}

子类RealCrashHandler:

package com.newsapp.tool;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;
import android.util.Log;

import com.newsapp.main.MainActivity;
import com.newsapp.util.Util;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * 崩溃处理实现类
 * @author mingC
 * @date 2018/8/5
 */
public class RealCrashHandler extends CrashHandler<RealCrashHandler.CrashData>{

    static class CrashData {
        Map<String, String> map = new HashMap<>();
    }

    @Override
    CrashData collectData(Context context, Thread t, Throwable e) {
        CrashData data = new CrashData();
        //保存异常信息
        int s = 0;
        for (StackTraceElement stackTraceElement : e.getStackTrace()) {
            data.map.put("err-stack" + (s++), stackTraceElement.toString());
        }
        //保存应用信息
        PackageManager pm = context.getPackageManager();
        PackageInfo pi = null;
        try {
            pi = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_ACTIVITIES);
        } catch (PackageManager.NameNotFoundException e1) {
            e1.printStackTrace();
        }
        if (pi != null) {
            String versionName = pi.versionName == null ? "null" : pi.versionName;
            String versionCode = pi.versionCode + "";
            data.map.put("versionName", versionName);
            data.map.put("versionCode", versionCode);
        }
        //保存设备信息
        Field[] fields = Build.class.getDeclaredFields();
        for (Field field : fields) {
            try {
                field.setAccessible(true);
                data.map.put(field.getName(), field.get(null).toString());
                Log.d("CrashHandler", field.getName() + " : " + field.get(null));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return data;
    }

    public void readCrashInfo(Context context) {
        Log.d("RealCrashHandler", "正在读取崩溃数据");
        try {
            File file = new File(context.getFilesDir(), "crash-info");
            if (! file.exists()) {
                Log.d("RealCrashHandler", "无崩溃数据");
                return;
            }
            BufferedReader reader = new BufferedReader(new FileReader(file));
            String line;
            while ((line = reader.readLine()) != null) {
                Log.d("RealCrashHandler", line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    void saveData(Context context, RealCrashHandler.CrashData data) {
        try {
            FileOutputStream out = new FileOutputStream(new File(context.getFilesDir(), "crash-info"));
            for (Map.Entry<String, String> entry : data.map.entrySet()) {
                String row = entry.getKey() + "-" + entry.getValue() + "\n";
                out.write(row.getBytes());
            }
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    void onProcessEnd(Context context) {
        Log.d("RealCrashHandler", "应用即将重启");
        restartApp(1000);
    }

    //重启APP,用定时器实现
    private void restartApp(long delay) {
        Intent intent = new Intent(Util.getContext(), MainActivity.class);
        PendingIntent restartIntent = PendingIntent.getActivity(
                Util.getContext(), 0, intent, 0);
        AlarmManager mgr = (AlarmManager)Util.getContext().getSystemService(Context.ALARM_SERVICE);
        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + delay,
                restartIntent);
    }
}

使用,在Application中开启:

public class MyApplication extends Application{
    @Override
    public void onCreate() {
        super.onCreate();
        RealCrashHandler crashHandler = new RealCrashHandler();
        crashHandler.install(this);
        //读取上一次的崩溃数据,可以在崩溃的时候把数据上传,如果上传失败也可以在下一次应用开启时上传
        crashHandler.readCrashInfo(this);
    }
}

猜你喜欢

转载自blog.csdn.net/mingC0758/article/details/81435791
今日推荐