问题
I have an activity which requires no data from server on load - just plain init for ui
UI has several buttons.
User clicks one of them and app sends request to server (rest call) While request is processing spinner is shown (for about 10 seconds)
For now it uses AsyncTask - so if app changes portrait to landscape - activity is restarted and I loose the process
Second option is to use Loader - the problem is that it is started on button tap - not on activity start
This leads to many exceptions - when LoaderManager sends events to non-started item
Is there any solution?
few comments: - 10 seconds is just for example - lock user to one orientation is not an option - service is overkill for simple rest call
回答1:
public class TestActivity extends FragmentActivity {
private Button one;
private Button two;
private final int ONE_ID = 0;
private final int TWO_ID = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
one = (Button) findViewById(R.id.one);
two = (Button) findViewById(R.id.two);
one.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getLoaderManager().restartLoader(ONE_ID, null, callbacks);
}
});
two.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
getLoaderManager().restartLoader(ONE_ID, null, callbacks);
}
});
Loader<AsyncTaskLoaderResult<Result>> loader = getLoaderManager().getLoader(ONE_ID);
if (loader != null) {
getLoaderManager().initLoader(ONE_ID, null, callbacks);
}
loader = getLoaderManager().getLoader(TWO_ID);
if (loader != null) {
getLoaderManager().initLoader(TWO_ID, null, callbacks);
}
}
public static class AsyncTaskLoaderResult<E> {
public E data;
public Bundle args;
}
public static class Result {
}
private LoaderManager.LoaderCallbacks<AsyncTaskLoaderResult<Result>> callbacks = new LoaderManager.LoaderCallbacks<AsyncTaskLoaderResult<Result>>() {
@Override
public Loader<AsyncTaskLoaderResult<Result>> onCreateLoader(int id, Bundle args) {
/**
* according different Id, create different AsyncTaskLoader
*/
switch (id) {
case ONE_ID:
return new OneAsyncTaskLoader(TestActivity.this);
case TWO_ID:
return new TwoAsyncTaskLoader(TestActivity.this);
}
return null;
}
@Override
public void onLoadFinished(Loader<AsyncTaskLoaderResult<Result>> loader, AsyncTaskLoaderResult<Result> data) {
/**
* handle result
*/
switch (loader.getId()) {
}
getLoaderManager().destroyLoader(loader.getId());
}
@Override
public void onLoaderReset(Loader<AsyncTaskLoaderResult<Result>> loader) {
}
};
public static class OneAsyncTaskLoader extends AsyncTaskLoader<AsyncTaskLoaderResult<Result>> {
private AsyncTaskLoaderResult<Result> result;
public OneAsyncTaskLoader(Context context) {
super(context);
}
@Override
protected void onStartLoading() {
super.onStartLoading();
if (result != null) {
deliverResult(result);
} else {
forceLoad();
}
}
@Override
public AsyncTaskLoaderResult<Result> loadInBackground() {
/**
* send request to server
*/
result = new AsyncTaskLoaderResult<Result>();
result.data = null; // result.data comes from server's response
return result;
}
}
public static class TwoAsyncTaskLoader extends AsyncTaskLoader<AsyncTaskLoaderResult<Result>> {
private AsyncTaskLoaderResult<Result> result;
public TwoAsyncTaskLoader(Context context) {
super(context);
}
@Override
protected void onStartLoading() {
super.onStartLoading();
if (result != null) {
deliverResult(result);
} else {
forceLoad();
}
}
@Override
public AsyncTaskLoaderResult<Result> loadInBackground() {
/**
* send request to server
*/
result = new AsyncTaskLoaderResult<Result>();
result.data = null; // result.data comes from server's response
return result;
}
}
}
回答2:
First, you can eliminate the orienatation change issue by declaring
android:configChanges="orientation"
or savedInstanceState()
But the real problem here is having the user stare at a spinner for 10 seconds. Most users aren't going to be patient enough for this. I don't know what your app is doing so its hard to give an accurate suggestion but I can say that you need to do your network stuff in your AsyncTask
but allow the user to do other things
You can allow the user to do other things while the AsyncTask
finishes or put that code in a [Service(http://developer.android.com/guide/components/services.html). Either way, don't make your users stare at a screen for 10 seconds of spinning...they won't be YOUR users for long
回答3:
If you're using an AsyncTask
for this you might want to either use a Service
instead or use onRetainNonConfigurationInstance or Fragment.setRetainInstance
to allow the AsyncTask
to live through configuration changes.
Or disable configuration changes: I've used that in the past with some success.
回答4:
Here's a good article on the subject:
http://www.javacodegeeks.com/2013/01/android-loaders-versus-asynctask.html
Anyways, as @codeMagic mentioned, AsyncTask
with android:configChanges="orientation|screenSize"
should be enough for you (it prevents activity from being recreated on config changes)
来源:https://stackoverflow.com/questions/16803865/android-loader-vs-asynctask-on-button-tap