sockeqwe / mosby

A Model-View-Presenter / Model-View-Intent library for modern Android apps
http://hannesdorfmann.com/mosby/
Apache License 2.0
5.49k stars 841 forks source link

MvpQueuingBasePresenter method onceViewAttached() not help after restore activity. #289

Closed alexei-28 closed 6 years ago

alexei-28 commented 6 years ago
implementation "com.hannesdorfmann.mosby3:mvp:3.1.0"
implementation "com.hannesdorfmann.mosby3:viewstate:3.1.0"
implementation "com.hannesdorfmann.mosby3:mvp-queuing-presenter:3.1.0"

In my phone when minimize aplicatoin then the Android destroy activity. In Developer options I turn "Don't keep activities".

Case:

  1. User click on button in the activity
  2. In activity call method: onClickDownloadPdf()
  3. In presenter in method downloadToAppDircall method ifViewAttached(OfferDetailsView::showProgress);
  4. Start async http request: model.downloadFile(pdfUrl, new DefaultRestClientCallback<ResponseBody>())
  5. After 10 seconds presenter get success http response
  6. As result in method onSuccess()the presenter call method from view: onceViewAttached(OfferDetailsView::hideProgress); And this method success call (in logcat has text: OfferDetailsPdfActivity( 2759): hideProgress:)
  7. As result progress bar is hide.

Here logcat:

12-11 14:24:21.736 D/com.myproject.mvp.presenter.OfferDetailsPdfPresenterImplMvp( 2759): downloadToAppDir: START_DOWNLOADING...
12-11 14:24:21.736 D/com.myproject.ui.OfferDetailsPdfActivity( 2759): showProgress: 

12-11 14:24:32.996 D/com.myproject.mvp.presenter.OfferDetailsPdfPresenterImplMvp( 2759): onSuccess: SUCCESS_DOWNLOAD
12-11 14:24:33.106 D/com.myproject.util.FileUtil( 2759): writeToDir: success_write_to_file
12-11 14:24:33.126 D/com.myproject.mvp.presenter.OfferDetailsPdfPresenterImplMvp( 2759):  downloadToAppDir: TRY_HIDE_PROGRESS
12-11 14:24:33.126 D/com.myproject.ui.OfferDetailsPdfActivity( 2759): hideProgress: 

Here my activity

public class OfferDetailsPdfActivity extends MvpViewStateActivity<OfferDetailsPdfMvp.View, OfferDetailsPdfPresenterImplMvp, OfferDetailsPdfViewState>
implements OnPageChangeListener, OfferDetailsPdfMvp.View {

@NonNull
    @Override
    public OfferDetailsPdfPresenterImplMvp createPresenter() {
        presenter = new OfferDetailsPdfPresenterImplMvp(this, offerId);
        return presenter;
}

   @OnClick(R.id.imageViewDownloadPdf)
    public void onClickDownloadPdf() {
        presenter.downloadToAppDir(false);
    }

@Override
    public void hideProgress() {
        Debug.d(TAG, "hideProgress: ");
        containerProgressBarLayout.setVisibility(View.GONE);
    }

    @Override
    public void setDownloadMode() {
        Debug.d(TAG, "setDownloadMode: ");
        imageViewDownloadPdf.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_details_download_active));
        imageViewDownloadPdf.setEnabled(false);
}

    @Override
    public void onNewViewStateInstance() {
        presenter.viewIsReady(true);
    }

    @Override
    public void onViewStateInstanceRestored(boolean instanceStateRetained) {
        presenter.viewIsReady(false);
    }

}

In activity I have 2 methods that must call when async http request finish.

Here presenter:

public class OfferDetailsPdfPresenterImplMvp extends MvpQueuingBasePresenter<OfferDetailsPdfMvp.View> implements OfferDetailsPdfMvp.Presenter<OfferDetailsPdfMvp.View> {
@Override
    public void downloadToAppDir(final boolean isDownloadToAppCacheDir) {
        Debug.d(TAG, "downloadToAppDir: START_DOWNLOADING...");
        markStartDownload();
        countDownloadAndWriteFiles = 0;
        final int totalCountFiles = 1;
        ifViewAttached(OfferDetailsView::showProgress);

        model.downloadFile(pdfUrl, new DefaultRestClientCallback<ResponseBody>() {

         @Override
            public void onSuccess(Response<ResponseBody> response) {
            super.onSuccess(response);
                Debug.d(TAG, "onSuccess: SUCCESS_DOWNLOAD");
                String fileName = StringUtil.toFileName(pdfUrl);
                boolean isWriteToAppDir = false;
                File file = null;
                if (isDownloadToAppCacheDir) {
                    isWriteToAppDir = FileUtil.writeToAppCacheDir(context, Offer.TABLE_NAME, fileName, response.body().byteStream());
                    FileUtil.getDirSize(FileUtil.getAppCacheDir(context));
                    if (isWriteToAppDir) {
                        file = getFileFromAppCacheDir();
                    }
                } else {
                    isWriteToAppDir = FileUtil.writeToAppPrivateDir(context, Offer.TABLE_NAME, fileName, response.body().byteStream());
                    FileUtil.getDirSize(FileUtil.getAppPrivateDir(context));
                }
                if (isWriteToAppDir) {
                    countDownloadAndWriteFiles++;
                    if (countDownloadAndWriteFiles == totalCountFiles) {
                        markFinishDownload(true);
                        Debug.d(TAG, " downloadToAppDir: TRY_HIDE_PROGRESS");
                        onceViewAttached(OfferDetailsView::hideProgress);
                    }
                }
            }
    }
}

And all work fine. Nice.

But has problem when minimize activity.

Case:

  1. User click on button in the activity

  2. In activity call method: onClickDownloadPdf()

  3. In presenter in method downloadToAppDircall method ifViewAttached(OfferDetailsView::showProgress);

  4. Start async http request: model.downloadFile(pdfUrl, new DefaultRestClientCallback<ResponseBody>())

  5. User minimize application

  6. Activity is destroy

  7. After 2 seconds user return to application

  8. Activity show again with progress. OK (It's correct)

  9. After 5 seconds presenter get success http response

  10. As result in method onSuccess()the presenter TRY to call method from view: onceViewAttached(OfferDetailsView::hideProgress); And this method is NOT call (in logcat has NO text: OfferDetailsPdfActivity( 2759): hideProgress:).

  11. As result progress bar is NOT hide.

Here logcat:

12-11 14:29:39.736 D/com.myproject.ui.OfferDetailsPdfActivity( 2759): showProgress: 
12-11 14:29:41.752 D/com.myproject.mvp.presenter.OfferDetailsPdfPresenterImplMvp( 2362): downloadToAppDir: START_DOWNLOADING...
12-11 14:29:48.292 D/com.myproject.mvp.presenter.OfferDetailsPdfPresenterImplMvp( 2362): onSuccess: SUCCESS_DOWNLOAD
12-11 14:29:48.482 D/com.myproject.mvp.presenter.OfferDetailsPdfPresenterImplMvp( 2362): markFinishDownload: isFinishDownload = true, isSuccessFinishDownload = true
12-11 14:29:48.482 D/com.myproject.mvp.presenter.OfferDetailsPdfPresenterImplMvp( 2362):  downloadToAppDir: TRY_HIDE_PROGRESS

Questions:

  1. Why method onceViewAttached(OfferDetailsView::hideProgress)not help here?
  2. How I can fix this problem?
sockeqwe commented 6 years ago

I just can speculate, but it seems like you have two instances of your presenter. The first time your Activity is instantiated your Presenter (lets call it PresenterInstance1) is instantiated too and you begin to download your file.

Next you "minimise" the activity but since you have "Don't keep activities" enabled, the whole activity is destroyed (and so is the PresenterInstance1, presenter.destroy() should be called). But it seems that you never cancel the download file request (should be done in presenter.destroy() ).

So then you reopen your Activity and a new presenter instance (lets call it presenterInstance2) is created and starts downloading again ...

Meanwhile, the presenterInstance1 get's its callback onSuccess() called, but this presenter is already "destroyed", so the View (activity) will never be attached to presenterInstance1. This is usually a memory leak. You better move downloading files into a background service (out of the lifecycle of the activity / presenter) and let the presenter just listen to the download service.