I'm trying to use an AsyncTaskLoader to load data in the background to populate a detail view in response to a list item being chosen.  I've gotten it mostly working but I'm still having one issue.  If I choose a second item in the list and then rotate the device before the load for the first selected item has completed, then the onLoadFinished() call is reporting to the activity being stopped rather than the new activity.  This works fine when choosing just a single item and then rotating.
Here is the code I'm using. Activity:
public final class DemoActivity extends Activity
        implements NumberListFragment.RowTappedListener,
                   LoaderManager.LoaderCallbacks<String> {
    private static final AtomicInteger activityCounter = new AtomicInteger(0);
    private int myActivityId;
    private ResultFragment resultFragment;
    private Integer selectedNumber;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        myActivityId = activityCounter.incrementAndGet();
        Log.d("DemoActivity", "onCreate for " + myActivityId);
        setContentView(R.layout.demo);
        resultFragment = (ResultFragment) getFragmentManager().findFragmentById(R.id.result_fragment);
        getLoaderManager().initLoader(0, null, this);
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.d("DemoActivity", "onDestroy for " + myActivityId);
    }
    @Override
    public void onRowTapped(Integer number) {
        selectedNumber = number;
        resultFragment.setResultText("Fetching details for item " + number + "...");
        getLoaderManager().restartLoader(0, null, this);
    }
    @Override
    public Loader<String> onCreateLoader(int id, Bundle args) {
        return new ResultLoader(this, selectedNumber);
    }
    @Override
    public void onLoadFinished(Loader<String> loader, String data) {
        Log.d("DemoActivity", "onLoadFinished reporting to activity " + myActivityId);
        resultFragment.setResultText(data);
    }
    @Override
    public void onLoaderReset(Loader<String> loader) {
    }
    static final class ResultLoader extends AsyncTaskLoader<String> {
        private static final Random random = new Random();
        private final Integer number;
        private String result;
        ResultLoader(Context context, Integer number) {
            super(context);
            this.number = number;
        }
        @Override
        public String loadInBackground() {
            // Simulate expensive Web call
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "Item " + number + " - Price: $" + random.nextInt(500) + ".00, Number in stock: " + random.nextInt(10000);
        }
        @Override
        public void deliverResult(String data) {
            if (isReset()) {
                // An async query came in while the loader is stopped
                return;
            }
            result = data;
            if (isStarted()) {
                super.deliverResult(data);
            }
        }
        @Override
        protected void onStartLoading() {
            if (result != null) {
                deliverResult(result);
            }
            // Only do a load if we have a source to load from
            if (number != null) {
                forceLoad();
            }
        }
        @Override
        protected void onStopLoading() {
            // Attempt to cancel the current load task if possible.
            cancelLoad();
        }
        @Override
        protected void onReset() {
            super.onReset();
            // Ensure the loader is stopped
            onStopLoading();
            result = null;
        }
    }
}
List fragment:
public final class NumberListFragment extends ListFragment {
    interface RowTappedListener {
        void onRowTapped(Integer number);
    }
    private RowTappedListener rowTappedListener;
    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        rowTappedListener = (RowTappedListener) activity;
    }
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ArrayAdapter<Integer> adapter = new ArrayAdapter<Integer>(getActivity(),
                                                                  R.layout.simple_list_item_1,
                                                                  Arrays.asList(1, 2, 3, 4, 5, 6));
        setListAdapter(adapter);
    }
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        ArrayAdapter<Integer> adapter = (ArrayAdapter<Integer>) getListAdapter();
        rowTappedListener.onRowTapped(adapter.getItem(position));
    }
}
Result fragment:
public final class ResultFragment extends Fragment {
    private TextView resultLabel;
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View root = inflater.inflate(R.layout.result_fragment, container, false);
        resultLabel = (TextView) root.findViewById(R.id.result_label);
        if (savedInstanceState != null) {
            resultLabel.setText(savedInstanceState.getString("labelText", ""));
        }
        return root;
    }
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putString("labelText", resultLabel.getText().toString());
    }
    void setResultText(String resultText) {
        resultLabel.setText(resultText);
    }
}
I've been able to get this working using plain AsyncTasks but I'm trying to learn more about Loaders since they handle the configuration changes automatically.
EDIT: I think I may have tracked down the issue by looking at the source for LoaderManager.  When initLoader is called after the configuration change, the LoaderInfo object has its mCallbacks field updated with the new activity as the implementation of LoaderCallbacks, as I would expect.
public <D> Loader<D> initLoader(int id, Bundle args, LoaderManager.LoaderCallbacks<D> callback) {
    if (mCreatingLoader) {
        throw new IllegalStateException("Called while creating a loader");
    }
    LoaderInfo info = mLoaders.get(id);
    if (DEBUG) Log.v(TAG, "initLoader in " + this + ": args=" + args);
    if (info == null) {
        // Loader doesn't already exist; create.
        info = createAndInstallLoader(id, args,  (LoaderManager.LoaderCallbacks<Object>)callback);
        if (DEBUG) Log.v(TAG, "  Created new loader " + info);
    } else {
        if (DEBUG) Log.v(TAG, "  Re-using existing loader " + info);
        info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
    }
    if (info.mHaveData && mStarted) {
        // If the loader has already generated its data, report it now.
        info.callOnLoadFinished(info.mLoader, info.mData);
    }
    return (Loader<D>)info.mLoader;
}
However, when there is a pending loader, the main LoaderInfo object also has an mPendingLoader field with a reference to a LoaderCallbacks as well, and this object is never updated with the new activity in the mCallbacks field.  I would expect to see the code look like this instead:
// This line was already there
info.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
// This line is not currently there
info.mPendingLoader.mCallbacks = (LoaderManager.LoaderCallbacks<Object>)callback;
It appears to be because of this that the pending loader calls onLoadFinished on the old activity instance.  If I breakpoint in this method and make the call that I feel is missing using the debugger, everything works as I expect.
The new question is: Have I found a bug, or is this the expected behavior?
 
     
     
     
    