0

I have a side menu drawer in my activity that has 2 options ("My files" and "Sync"), each of which is a fragment. When I am in "Sync" fragment, there is a button to start downloading files from the server. This is done through an intent service which is running in the background. I use a result receiver in my fragment which keeps getting the download progress (%) from the service and displays it in the fragment.

The problem is that if I switch to the "My Files" fragment while the download is going on and come back to the "Sync" fragment, the view is reset and the progress is lost. The service keeps running in the background but the fragment does not show the progress.

My question is that when I switch the fragment, does the "Sync" fragment still receive the progress from the service that keeps running in the background. How do I start receiving the progress updates from the service when I go back to the "Sync" fragment.

Below is the code in the fragment that starts the service.

intent.putExtra("link", downloadLink);
syncReceiver = new SyncReceiver(new Handler());
intent.putExtra("result_receiver", syncReceiver);
getContext().startService(intent);

Code in the service that sends the download progress to the fragment.

resultReceiver = intent.getParcelableExtra("result_receiver");
link = intent.getStringExtra("link");

while ((bytesRead = inputStream.read(buffer)) != -1) {
      fileOutputStream.write(buffer, 0, bytesRead);
      byteCount += bytesRead;
      String kbs = String.valueOf(byteCount / 1024);
      bundle = new Bundle();
      bundle.putString("response", kbs);
      bundle.putString("total_data", total_file_size);
      resultReceiver.send(CONTENT_PROGRESS, bundle);
}

The progress receiving code in the fragment.

public class SyncReceiver extends ResultReceiver {
    private static final int CONTENT_PROGRESS = 2;

    public SyncReceiver(Handler handler) {
        super(handler);
    }

    public void onReceiveResult(int resultCode, Bundle data) {
        super.onReceiveResult(resultCode, data);

        String response = data.getString("response");
        if (resultCode == CONTENT_PROGRESS) {
            updateContentProgress(response, data.getString("total_file_size");
        }
    }
}


private void updateContentProgress(String progress, String total_file_size)  {
    double current = Double.parseDouble(progress);
    double totalData = 0;
    totalData = Double.parseDouble(total_file_size);
    String percent = String.valueOf((current / totalData) * 100);
    status.setText(R.string.download_progress);
    status.append(" " + percent + "%");
}
Sid
  • 435
  • 4
  • 9
  • 19

2 Answers2

0

The Fragment won't get updates since it might get destroyed and recreated.

I had a similar problem and my solution was to have an extra background-Fragment to keep the ResultReceiver, that doesn't get destroyed by setting setRetainInstance(true).

An explanation and a possible solution can be found here: https://stanmots.blogspot.com/2016/10/androids-bad-company-intentservice.html

Another good read concerning this problem: https://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html

So my solution was to hold the ResultReceiver inside an extra Fragment having setRetainInstance(true).

To get the right state when (re-)creating the View, I do the following in my onCreate():

final FragmentManager manager = ((Activity) getContext()).getFragmentManager();

// Try to find the Fragment by tag
final IntentObserverFragment intentObserverFragment =
                (IntentObserverFragment) manager.findFragmentByTag(IntentObserverFragment.TAG);

if (intentObserverFragment == null) {

    // Service is not active
    this.progressBar.setVisibility(View.GONE);

} else {

    // Service is working - show ProgressBar or else
    this.progressBar.setVisibility(View.VISIBLE);

    // Stay informed and get the result when it's available 
    intentObserverFragment.setCallbackClass( this );

}

In my IntentObserverFragment I start the work at onAttach() - not at onCreate(), because the needed Context isn't available yet, which would result in a NPE using e.g. getActivity()!

@Override
public void onAttach(Context context) {

    super.onAttach(context);

    final MyResultReceiver myResultReceiver = new MyResultReceiver();

    final Intent intent = new Intent( context, MyIntentService.class );
    intent.putExtra(MyIntentService.BUNDLE_KEY_RECEIVER, myResultReceiver);
    context.startService(intent);
}
Stephan
  • 1
  • 1
0

First move your classes that do the downloading into you Activity. Then from You fragment call into the Classes to to check on status of Your Download with Snippet below...Since activity will still hold reference to your Initial Download you will be able to Track progress. When you switch fragments your activity automatically kills reference to download thread since fragment is removed. Keeping reference in activity allows you to track across multiple fragments.Make sure you classes are public.

((YourBaseActivityWithProperClasses) getActivity()).trackDownloadProgress();
skryshtafovych
  • 572
  • 4
  • 16