问题
I have an Activity that uses fragments. These fragments may come and go, based on the users interactions. Many of these fragments launch jobs to an IntentService, which get to run async this way. How should the IntentService report back the results of these jobs?
The fragment that started the job may of may not be present. If a job finishes and the starting fragment is currently active, then it should get notified about this, and act accordingly. If it's not, then no action is needed.
I've thought about using broadcast intents and BroadcastReceiver components, but fragments can't register receivers, only activities.
What solution would you suggest?
回答1:
I noticed the same problem in IOSched App (Google I/O App for Android).
They created DetachableResultReceiver, which extends SDK class ResultReciever.
And they easily use it in Fragments.
Receiver:
/**
* Proxy {@link ResultReceiver} that offers a listener interface that can be
* detached. Useful for when sending callbacks to a {@link Service} where a
* listening {@link Activity} can be swapped out during configuration changes.
*/
public class DetachableResultReceiver extends ResultReceiver {
private static final String TAG = "DetachableResultReceiver";
private Receiver mReceiver;
public DetachableResultReceiver(Handler handler) {
super(handler);
}
public void clearReceiver() {
mReceiver = null;
}
public void setReceiver(Receiver receiver) {
mReceiver = receiver;
}
public interface Receiver {
public void onReceiveResult(int resultCode, Bundle resultData);
}
@Override
protected void onReceiveResult(int resultCode, Bundle resultData) {
if (mReceiver != null) {
mReceiver.onReceiveResult(resultCode, resultData);
} else {
Log.w(TAG, "Dropping result on floor for code " + resultCode + ": "
+ resultData.toString());
}
}
}
Activity and Fragment:
public class HomeActivity extends BaseActivity {
private static final String TAG = "HomeActivity";
private SyncStatusUpdaterFragment mSyncStatusUpdaterFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//...
mTagStreamFragment = (TagStreamFragment) fm.findFragmentById(R.id.fragment_tag_stream);
mSyncStatusUpdaterFragment = (SyncStatusUpdaterFragment) fm
.findFragmentByTag(SyncStatusUpdaterFragment.TAG);
if (mSyncStatusUpdaterFragment == null) {
mSyncStatusUpdaterFragment = new SyncStatusUpdaterFragment();
fm.beginTransaction().add(mSyncStatusUpdaterFragment,
SyncStatusUpdaterFragment.TAG).commit();
triggerRefresh();
}
}
private void triggerRefresh() {
final Intent intent = new Intent(Intent.ACTION_SYNC, null, this, SyncService.class);
intent.putExtra(SyncService.EXTRA_STATUS_RECEIVER, mSyncStatusUpdaterFragment.mReceiver);
startService(intent);
if (mTagStreamFragment != null) {
mTagStreamFragment.refresh();
}
}
/**
* A non-UI fragment, retained across configuration changes, that updates its activity's UI
* when sync status changes.
*/
public static class SyncStatusUpdaterFragment extends Fragment
implements DetachableResultReceiver.Receiver {
public static final String TAG = SyncStatusUpdaterFragment.class.getName();
private boolean mSyncing = false;
private DetachableResultReceiver mReceiver;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
mReceiver = new DetachableResultReceiver(new Handler());
mReceiver.setReceiver(this);
}
/** {@inheritDoc} */
public void onReceiveResult(int resultCode, Bundle resultData) {
HomeActivity activity = (HomeActivity) getActivity();
if (activity == null) {
return;
}
switch (resultCode) {
case SyncService.STATUS_RUNNING: {
mSyncing = true;
break;
}
//...
}
activity.updateRefreshStatus(mSyncing);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((HomeActivity) getActivity()).updateRefreshStatus(mSyncing);
}
}
}
来源:https://stackoverflow.com/questions/6101958/how-should-a-fragment-get-notified-about-the-result-of-an-asynchronous-task