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?