• Android实现多次闪退清除数据


    背景

    很多时候由于后台返回的数据异常,可能会导致App闪退。而如果这些异常数据被App本地缓存下来,那么即使杀掉进程重新进入还是会发生闪退。唯一的解决方法就是清除App数据,但是用户可能没有这个意识或者嫌麻烦就直接不再使用了,这是我们无法接受的。在使用淘宝、追书神器等App时我发现有时候它们也会连续闪退,但是往往闪退三次后就恢复正常了,所以一般成熟的App都会做连续闪退三次后清除缓存数据的工作。而目前笔者搜不到有哪篇blog来讲这方面的事情,所以就姑且由我来讲讲此事,为希望提高App用户体验的朋友提供些许参考。

    ACRA

    为了能够在闪退的时候做一些事情,我们可以使用ACRA,这是Github上的一个开源项目,允许使用者设置一些Sender在App闪退的时候做一些事情。具体使用可以直接参考Github。如果不希望使用ACRA,那么也可以自己实现一个UncachedExceptionHandler并替换系统默认的Handler,并在这个Handler里面对数据进行处理。

    实现清除数据

    ACRA提供了自己的一些Sender,如使用系统邮件客户端向指定邮箱发送邮件的EmailIntentSender。而我们希望记录闪退次数和清除数据则需要implements ReportSender接口。

    public class CrashHandler implements ReportSender {
        @Override
        public void send(Context context, CrashReportData errorContent) throws ReportSenderException {
            Timber.i("闪退,检查是否需要清空数据");
            new CrashModel().checkAndClearData();
        }
    }
    

    这里我们写了一个CrashModel用来记录闪退次数和时间决定是否需要清空数据,具体代码如下。 由于在ReportSender的时候无法打开其它线程,所以我们无法使用SharedPerferences来清理数据(打开SP的时候其实打开了一个新线程)。为此需要找到数据缓存的位置并将文件删除。同样道理,记录闪退时间也只能通过文件记录。当然,你可以选择一些文件不进行删除,如用户信息等不太容易出问题的数据。

    public class CrashModel {
    
        private static final String KEY_CRASH_TIMES = "crash_times";
        private static final String CRASH_TIME_FILE_NAME = "crash_time";
        //不能通过App.getPackageName来获取包名,否则会有问题,只能默认为cn.campusapp.campus。所以对于debug或者运营版本,清数据会把release的清掉
        private static final String FILE_DIR = String.format("/data/data/%s/", BuildConfig.APPLICATION_ID);
        private static final String ACCOUNT_FILE_NAME = String.format("%s%s", FILE_DIR, "shared_prefs/account_pref.xml");
        private static ArrayList<String> FILES_DONTNEED_DELETE = new ArrayList<>();  //该目录中的文件不会被删除
    
        static {
            FILES_DONTNEED_DELETE.add(ACCOUNT_FILE_NAME);  //目前账号信息文件不会被删除,但是会手动改变数据,只保留userId  accessToken 和school
        }
    
        protected ArrayList<Long> mCrashTimes;
        Gson gson = new Gson();
        private File mFileDir;
    
        public CrashModel() {
            mFileDir = new File(FILE_DIR);
            mCrashTimes = readCrashTimes();
            if (mCrashTimes == null) {
                mCrashTimes = new ArrayList<>();
                storeCrashTimes(mCrashTimes);
            }
        }
    
    
        public void checkAndClearData() {
            long timeNow = System.currentTimeMillis();
    
            if (checkClearData(timeNow, new ArrayList<>(mCrashTimes))) {
                Timber.i("已经在5分钟之内有三次闪退,需要清理数据");
                try {
                    clearData();
                } catch (Exception e) {
                    Timber.e(e, "清空所有数据失败");
                }
            } else {
                mCrashTimes.add(timeNow);
                storeCrashTimes(mCrashTimes);
                Timber.i("此次不需要清空数据, %s", gson.toJson(mCrashTimes));
            }
        }
    
        private void storeCrashTimes(ArrayList<Long> crashTimes) {
            try {
                String str = gson.toJson(crashTimes);
                Files.writeToFile(mFileDir, CRASH_TIME_FILE_NAME, str);
            } catch (Exception e) {
                Timber.e(e, "保存闪退时间失败");
            }
    
        }
    
        private ArrayList<Long> readCrashTimes() {
            try {
                String timeStr = Files.readFileContent(mFileDir, CRASH_TIME_FILE_NAME);
                return gson.fromJson(timeStr, new TypeToken<ArrayList<Long>>() {
                }.getType());
            } catch (Exception e) {
                Timber.e(e, "读取闪退时间失败");
            }
            return null;
        }
    
        /**
         * 检查是否需要清空数据,目前的清空策略是在5分钟之内有三次闪退的就清空数据,也就是从后往前遍历,只要前两次闪退发生在5分钟之内,就清空数据
         *
         * @return
         */
        private boolean checkClearData(long time, ArrayList<Long> crashTimes) {
            Timber.i(gson.toJson(crashTimes));
            int count = 0;
            for (int i = crashTimes.size() - 1; i >= 0; i--) {
                long crashTime = crashTimes.get(i);
                if (time - crashTime <= 5 * 60 * 1000) {
                    count++;
                    if (count >= 2) {
                        break;
                    }
                }
            }
            if (count >= 2) {
                //在5分钟之内有三次闪退,这时候需要清空数据
                return true;
            } else {
                return false;
            }
        }
    
        /**
         * 清空数据,包括数据库中的和SharedPreferences中的
         *
         * @throws Exception
         */
        private void clearData() throws Exception {
            Timber.i("开始清理数据");
            Files.deleteFilesExceptSomeInDirectory(mFileDir, FILES_DONTNEED_DELETE);
        }
    
    
    }
    
    

    然后我们需要将CrashHandler 添加到ACRA的异常处理Sender列表中。在你的Application类中添加如下代码。

    @ReportsCrashes(
    		  //一些ACRA的设置,具体参考ACRA文档,因为我们使用自定义Sender,所以这里完全可以不用设置
            //mailTo = "bugs@treeholeapp.cn",
            //mode = ReportingInteractionMode.TOAST,
            //resToastText = R.string.crash_toast_text
    )
    public class App extends Application {
    
        @Override
        public void onCreate() {
        	if (!BuildConfig.DEBUG) {  //这里我判断只有在非DEBUG下才清除数据,主要是为了在开发过程中能够保留线程。
                    ACRA.init(APPLICATION_CONTEXT);
                    CrashHandler handler = new CrashHandler();
                  	ACRA.getErrorReporter().setReportSender(handler);  //在闪退时检查是否要清空数据
            }
    
        }
    }
    
    

    总结

    以上即为实现多次闪退后清除数据的实现,希望大家开发的App Bug越来越少,鲁棒性越来越强。

  • 相关阅读:
    将博客搬至CSDN
    Linux-进程管理&网络管理
    MySQL架构备份之双机热备
    Linux目录结构&文件管理
    markdown文件即 .md 的基本常用编写语法
    Nginx
    Java 面试题(收集整理...ing)
    CentOS7 VS CentOS8
    Linux 学习
    vi/vim 命令整理
  • 原文地址:https://www.cnblogs.com/dongweiq/p/5411875.html
Copyright © 2020-2023  润新知