Вопрос

As the title says it, I am starting a Loader on a button click and then I am doing an orientation change. In the recreated activity onLoadFinished is not called automatically on startup. My suspicion is that LoaderManager has no idea of this newly created activity instance as I don't think it's tied that much with activity life cycle. But I may be wrong ...

This is the loader, providing an array of Strings and simulating it's delaying data result with 3 seconds. Implementation is a simplified version of AsyncTaskLoader sample code.

import android.content.Context;
import android.support.v4.content.AsyncTaskLoader;

public class ContentAsyncLoader extends AsyncTaskLoader<String[]> {
    private String[] data;

    public ContentAsyncLoader(Context context) {
        super(context);
    }

    @Override
    protected void onStartLoading() {
        if (data != null) {
            deliverResult(data);
        } else {
            forceLoad();
        }
    }

    @Override
    public String[] loadInBackground() {
        synchronized (this) {
            try {
                wait(3000);
            } catch (InterruptedException e) {
            }
        }
        final String[] result = { "Dell Inspiron", "HTC One X", "HTC Wildfire S", "HTC Sense", "HTC Sensation XE", "iPhone 4S", "Samsung Galaxy Note 800", "Samsung Galaxy S3", "MacBook Air",
                "Mac Mini", "MacBook Pro" };
        return result;
    }

    @Override
    protected void onStopLoading() {
        cancelLoad();
    }

    @Override
    protected void onReset() {
        super.onReset();
        onStopLoading();
        data = null;
    }

    @Override
    public void deliverResult(String[] data) {
        if (isReset()) {
            return;
        }
        this.data = data;
        if (isStarted()) {
            super.deliverResult(data);
        }
    }
}

And below is the activity that starts the loader on the button click - a ListView and a Button:

public class MainActivity extends FragmentActivity implements OnClickListener, LoaderCallbacks<String[]> {

    private static final int LOADER_ID = 1;
    private ListView lv;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        lv = (ListView) findViewById(R.id.list_view);
        findViewById(R.id.btnSomeAction).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        getSupportLoaderManager().initLoader(LOADER_ID, null, this);
    }

    @Override
    public Loader<String[]> onCreateLoader(int id, Bundle bundle) {
        ContentAsyncLoader loader = new ContentAsyncLoader(this);
        return loader;
    }

    @Override
    public void onLoaderReset(Loader<String[]> loader) {
    }

    @Override
    public void onLoadFinished(Loader<String[]> loader, String[] data) {
        if (data != null) {
            ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, android.R.id.text1, data);
            lv.setAdapter(adapter);
        }
    }
}

If somebody wants a clear question: how to properly make use of a Loader that is started on a button click so that the data is automatically re-provided when the activity is recreated?

I have some dirty work-around to fix this by keeping a flag set to true when button is clicked, save it when onSaveInstanceState is called and then in onCreate check it if present and if so call initLoader; but I hate to have this in such cases and when I am starting different loaders from an activity I need to keep a flag for each such loader as I tend to be lazy ... For example:

private boolean loaderCreated;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    lv = (ListView) findViewById(R.id.list_view);
    findViewById(R.id.btnSomeAction).setOnClickListener(this);
    if (savedInstanceState != null) {
        loaderCreated = savedInstanceState.getBoolean("TEMP", false);
        if(loaderCreated) {
            getSupportLoaderManager().initLoader(LOADER_ID, null, this);
        }
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean("TEMP", loaderCreated);
}

@Override
public void onClick(View v) {
    loaderCreated = true;
    getSupportLoaderManager().initLoader(LOADER_ID, null, this);
}

Isn't there a better way to do it?

Это было полезно?

Решение

getSupportLoaderManager().initLoader(LOADER_ID, null, this); should be called onCreate every time if button was pressed before.

Because now after orientation savedInstanceState is not null and you are not asking loader about data.

And i see that you making it...;) and this is only way.

Regards

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top