Android realizes multiple flashback to clear data

  • 2021-07-03 00:49:52
  • OfStack

In many cases, App may flash back due to abnormal data returned from the background. However, if these abnormal data are cached locally by App, flashback will occur even if the killed process re-enters. The only solution is to clear the App data, but users may not be aware of this or bother to stop using it directly, which is unacceptable to us. When using App such as Taobao and book chasing artifact, I found that sometimes they will flash back continuously, but they often return to normal after flashing back three times, so a mature App will do the work of clearing cached data after flashing back three times continuously. At present, the author can't find any blog to talk about this matter, so let me talk about this matter for the moment, and provide some reference for friends who want to improve the user experience of App.

ACRA
To be able to do something while flashing back, we can use ACRA, an open source project on Github that allows users to set up some Sender to do something while App flashes back. For specific use, please refer to Github directly. If you don't want to use ACRA, you can also implement an UncachedExceptionHandler and replace the default Handler, and process the data in this Handler.

Implement data clearing
ACRA provides its own Sender, such as EmailIntentSender, which uses the system mail client to send mail to a specified mailbox. We need the implements ReportSender interface if we want to record flashback times and clear data.


public class CrashHandler implements ReportSender {
  @Override
  public void send(Context context, CrashReportData errorContent) throws ReportSenderException {
    Timber.i(" Flash back , Check whether you need to empty the data ");
    new CrashModel().checkAndClearData();
  }
}

Here we wrote an CrashModel to record the flashback times and time to decide whether to empty the data. The specific code is as follows. Since no other thread can be opened at ReportSender, we cannot use SharedPerferences to clean up the data (when SP is opened, a new thread is actually opened). To do this, you need to find the location of the data cache and delete the file. By the same token, recording the flashback time can only be recorded by file. Of course, you can choose 1 files without deleting, such as user information and other data that are not easy to go wrong.


public class CrashModel {

  private static final String KEY_CRASH_TIMES = "crash_times";
  private static final String CRASH_TIME_FILE_NAME = "crash_time";
  // Can't pass App.getPackageName To get the package name, otherwise there will be problems, and it can only be defaulted to cn.campusapp.campus . So for debug Or the operating version, clearing the data will put release Clear away the 
  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<>(); // Files in this directory will not be deleted 

  static {
    FILES_DONTNEED_DELETE.add(ACCOUNT_FILE_NAME); // At present, the account information file will not be deleted, but the data will be changed manually and only retained userId accessToken  And 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(" Already in 5 Within minutes 3 Secondary flashback, need to clean up data ");
      try {
        clearData();
      } catch (Exception e) {
        Timber.e(e, " Failed to empty all data ");
      }
    } else {
      mCrashTimes.add(timeNow);
      storeCrashTimes(mCrashTimes);
      Timber.i(" There is no need to empty the data this time , %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, " Failed to save flashback time ");
    }

  }

  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, " Failed to read flashback time ");
    }
    return null;
  }

  /**
   *  Check whether you need to empty the data. The current empty strategy is to 5 Within minutes 3 The second flashback empties the data, that is, traverses from back to front, as long as the first two flashbacks occur in 5 Within minutes, the data is emptied 
   *
   * @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) {
      // In 5 Within minutes 3 Flash back, at this time, you need to empty the data 
      return true;
    } else {
      return false;
    }
  }

  /**
   *  Empty the data, including the and in the database SharedPreferences In 
   *
   * @throws Exception
   */
  private void clearData() throws Exception {
    Timber.i(" Start cleaning up data ");
    Files.deleteFilesExceptSomeInDirectory(mFileDir, FILES_DONTNEED_DELETE);
  }


}

Then we need to add CrashHandler to the exception handling Sender list of ACRA. Add the following code to your Application class.


@ReportsCrashes(
  //1 Some ACRA Settings of, specific reference ACRA Documentation, because we use custom Sender So you don't have to set it here at all 
    //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) { // Here I judge that only in non- DEBUG Data is cleared only next, mainly to preserve threads during development. 
        ACRA.init(APPLICATION_CONTEXT);
        CrashHandler handler = new CrashHandler();
        ACRA.getErrorReporter().setReportSender(handler); // Check whether you want to empty the data during flashback 
    }

  }
}

Summarize
The above is to realize the realization of clearing data after multiple flashbacks. I hope that App Bug developed by everyone will be less and less.


Related articles: