Class with multiple parameters as the object of a WeakReference

Posted on

Problem

I just read this post on how to avoid memory leaks with AsyncTask. The post proposed using a WeakReference and supplying a TextView as the object of the WeakReference.

In my code, I need to supply multiple parameters to the AsyncTask (not just one as shown in the post). So what I did was create an inner class with the parameters needed in the AsyncTask and use the said class as the object of the WeakReference.

private static class AddBooksToDatabase extends AsyncTask<String, String, String> {
        private final WeakReference<AddBooksDbParams> mReference;
        private String TAG = "TownFragment";
        Context mContext;
        WaveLoadingView waveView;
        TextView infoText;
        String townName;
        File mFile;

        public AddBooksToDatabase(AddBooksDbParams params) {
            this.mReference = new WeakReference<    >(params);
            mContext = mReference.get().mContext;
            infoText = mReference.get().infoText;
            townName = mReference.get().townName;
            mFile = mReference.get().mFile;
            waveView = mReference.get().waveView;
        }

        @Override
        protected String doInBackground(String... strings) {
            TownHelper helper = TownHelper.getInstance(mContext, dbName);
            SQLiteDatabase database = helper.getWritableDatabase();
            int booksSize = getFilesInFolder(mFile).size();
            //Stuffs
            return null;
        }

        @Override
        protected void onPreExecute() {
            if (waveView != null) {
                waveView.setVisibility(View.VISIBLE);
            }
        }

        @Override
        protected void onPostExecute(String s) {
            if (waveView != null) {
                waveView.setVisibility(View.GONE);
            }
        }

        @Override
        protected void onProgressUpdate(String... values) {
            super.onProgressUpdate(values);
            Log.d(TAG, "Progress report = " + values[0]);
            infoText.setText(values[0]);
        }

        @Override
        protected void onCancelled() {
            cancel(true);
        }
    }


    //Parameters for AddBooksToDatabase. This is to enable holding of a
    //single object of this class in WeakReference
    private class AddBooksDbParams {
        Context mContext;
        WaveLoadingView waveView;
        TextView infoText;
        String townName;
        File mFile;

        AddBooksDbParams(TextView infoText, Context context, File file,
                         String townName, WaveLoadingView waveView) {
            this.infoText = infoText;
            mContext = context;
            mFile = file;
            this.townName = townName;
            this.waveView = waveView;
        }
    }

When I want to execute the AsyncTask:

AddTownsDbParams params = new AddTownsDbParams(infoText, getActivity(), folder, mShelfLabel, mWave);
addBooksTask = new AddBooksToDatabase(params).execute();

The code is working quite aright but I want to know if I am doing wrong.

Solution

What about this

private static class AddBooksToDatabase extends AsyncTask<String, String, String> {

  private String TAG = "TownFragment";

  private WeakReference<Context> mContextRef;
  private WeakReference<TextView> mInfoTextRef;
  private WeakReference<String> mTownNameRef;
  private WeakReference<File> mFileRef;
  private WeakReference<WaveLoadingView> mWaveViewRef;

  public AddBooksToDatabase(Context context, TextView infoText, String townName, File file, WaveLoadingView waveView) {
    mContextRef = new WeakReference<>(context);
    mInfoTextRef = new WeakReference<>(infoText);
    mTownNameRef = new WeakReference<>(townName);
    mFileRef = new WeakReference<>(file);
    mWaveViewRef = new WeakReference<>(waveView);
  }

  @Override
  protected String doInBackground(String... strings) {
    TownHelper helper = TownHelper.getInstance(mContextRef, dbName);
    SQLiteDatabase database = helper.getWritableDatabase();
    int booksSize = getFilesInFolder(mFileRef).size();
    //Stuffs
    return null;
  }

  @Override
  protected void onPreExecute() {
    WaveLoadingView waveView = mWaveViewRef.get();
    if(waveView != null) {
      waveView.setVisibility(View.VISIBLE);
    }
  }

  @Override
  protected void onPostExecute(String s) {
    WaveLoadingView waveView = mWaveViewRef.get();
    if(waveView != null) {
      waveView.setVisibility(View.GONE);
    }
  }

  @Override
  protected void onProgressUpdate(String... values) {
    super.onProgressUpdate(values);
    Log.d(TAG, "Progress report = " + values[0]);
    TextView infoText = mInfoTextRef.get();
    infoText.setText(values[0]);
  }

  @Override
  protected void onCancelled() {
    cancel(true);
  }
}

Without seeing the surrounding code or understanding too much about it’s purpose/function, here is how I would change the code

private static class AddBooksToDatabase extends AsyncTask<String, String, String> {

  private String TAG = "TownFragment";

  private WeakReference<Context> mContextRef;

  private String mTownName;
  private File mFile;

  private Dialog mProgressDialog;

  private TextView mInfoText;
  private WaveLoadingView mWaveView;

  /*
  * A constructor/method should rarely exceed 3 parameters.
  * */
  public AddBooksToDatabase(Context context, String townName, File booksFolder) throws IOException {
    /*
    * Since the file parameter must be a directory add in this
    * check as early as possible and throw a checked exception
    * which the caller must handle if it fails.
    * */
    if(!booksFolder.isDirectory()) {
      throw new IOException("the file " + booksFolder.getName() + " is not a directory");
    }
    /*
    * Holding a WeakReference to the Context because
    * the AsyncTask should not outlive the Context.
    * */
    mContextRef = new WeakReference<>(context);

    mTownName = townName;
    mFile = booksFolder;

    /*
    * The progress views are initialised inside the
    * AsyncTask, no need to pass them in as arguments.
    * */
    mInfoText = new TextView(context);
    mWaveView = new WaveLoadingView(context);

    /*
    * Create a dialog to display the progress views.
    * */
    LinearLayout progressDialogView = new LinearLayout(context);
    progressDialogView.setOrientation(LinearLayout.VERTICAL);
    progressDialogView.addView(mInfoText);
    progressDialogView.addView(mWaveView);
    mProgressDialog = new AlertDialog.Builder(context)
        .setTitle("Adding books")
        .setView(progressDialogView)
        .create();
  }

  @Override
  protected void onPreExecute() {
    /*
    * Display the progress dialog
    * when the task starts executing.
    * */
    mProgressDialog.show();
  }

  @Override
  protected String doInBackground(String... strings) {
    final Context context = mContextRef.get();
    TownHelper helper = TownHelper.getInstance(context, dbName);
    SQLiteDatabase database = helper.getWritableDatabase();
    int booksSize = getFilesInFolder(mFile).size();
    //Stuffs
    return null;
  }

  @Override
  protected void onProgressUpdate(String... values) {
    super.onProgressUpdate(values);
    Log.d(TAG, "Progress report = " + values[0]);
    mInfoText.setText(values[0]);
  }

  @Override
  protected void onPostExecute(String s) {
    /*
    * Dismiss the progress dialog
    * when the task finishes executing.
    * */
    mProgressDialog.dismiss();
  }

  @Override
  protected void onCancelled() {
    cancel(true);
  }

}

Leave a Reply

Your email address will not be published. Required fields are marked *