Android best way to update UI thread from AsyncTask - android

I released an app today and I am getting some crash reports that indicate the following:
java.lang.NullPointerException: Attempt to invoke virtual method 'android.view.View android.app.Activity.findViewById(int)' on a null object reference
I have on Activity and a couple of Fragments. This error happens in one my Fragments where I have an AsyncTask:
private ProgressDialog dialog;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_dial, container, false);
dialog = new ProgressDialog(getActivity());
new CountryCodeTask().execute();
return view;
}
private class CountryCodeTask extends AsyncTask<Void, Void, ArrayList<String>>
{
#Override
protected void onPreExecute()
{
if (!dialog.isShowing()) {
dialog.show();
}
}
#Override
protected void onPostExecute(ArrayList<String> result)
{
if (dialog.isShowing()) {
dialog.dismiss();
}
if (result != null) {
Spinner countryCodeSpinner = (Spinner) getActivity().findViewById(R.id.country_code_spinner);
countryCodeSpinner.setAdapter(new CountryCodeAdapter(getActivity(), result));
countryCodeSpinner.setSelection(countryCodeSpinnerValue);
countryCodes = result;
}
}
#Override
protected ArrayList<String> doInBackground(Void... params)
{
return MainActivity.apiService.getCountryCodes();
}
}
I am guessing this is not the best way to update the UI thread from a ASyncTask. This error happens when I use the back/home button and then restart the app (but not all the time). What am I doing wrong here?
The error happens on this line: Spinner countryCodeSpinner = (Spinner) getActivity().findViewById(R.id.country_code_spinner);

You're not doing anything wrong really - it's to be expected that when you navigate away from an Activity via back/home, the AsyncTask will no longer have a reference to the Activity. I would modify your code to handle this common scenario:
#Override
protected void onPostExecute(ArrayList<String> result)
{
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
}
Activity activity = getActivity();
if (activity != null) {
if (result != null) {
Spinner countryCodeSpinner = (Spinner) activity.findViewById(R.id.country_code_spinner);
countryCodeSpinner.setAdapter(new CountryCodeAdapter(activity, result));
countryCodeSpinner.setSelection(countryCodeSpinnerValue);
countryCodes = result;
} else {
cancelTaskAndShowDialog();
}
}
}

When your AsyncTask finishes, after you pressed back and left the activity, getActivityreturns null.
You need to add a null check in onPostExecute
final Activity activity = getActivity()
if (dialog =! null && dialog.isShowing()) {
dialog.dismiss();
}
if (activity != null) {
if (result != null) {
Spinner countryCodeSpinner = (Spinner) activity.findViewById(R.id.country_code_spinner);
countryCodeSpinner.setAdapter(new CountryCodeAdapter(activity, result));
countryCodeSpinner.setSelection(countryCodeSpinnerValue);
countryCodes = result;
}
}

Try this:
private ProgressDialog dialog;
private Spinner countryCodeSpinner;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_dial, container, false);
dialog = new ProgressDialog(getActivity());
new CountryCodeTask().execute();
countryCodeSpinner = (Spinner) view.findViewById(R.id.country_code_spinner);
return view;
}
private class CountryCodeTask extends AsyncTask<Void, Void, ArrayList<String>>
{
#Override
protected void onPreExecute()
{
if (!dialog.isShowing()) {
dialog.show();
}
}
#Override
protected void onPostExecute(ArrayList<String> result)
{
if (dialog.isShowing()) {
dialog.dismiss();
}
if (result != null) {
countryCodeSpinner.setAdapter(new CountryCodeAdapter(getActivity(), result));
countryCodeSpinner.setSelection(countryCodeSpinnerValue);
countryCodes = result;
}
}
#Override
protected ArrayList<String> doInBackground(Void... params)
{
return MainActivity.apiService.getCountryCodes();
}
}

Related

NullPointerException - Handler.removeCallbacks(java.lang.Runnable)

I got some crash reports from my app on console. I tried to fix it but I stucked. I'm using custom progress dialog, my problem is about that. What should I do? Should I use removeCallBack?
FlipProgressDialog fpd; // this is my custom progress dialog
#Override
public void onPause() {
super.onPause();
fpd.dismissAllowingStateLoss();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_top_feeds, container, false);
this.mView = view;
fpd = new FlipProgressDialog(); //and initialized here for escape the nullpointer exception but not worked
fpd.setImageList(imageList);
fpd.setCanceledOnTouchOutside(false);
// Handler handler = new Handler();
Runnable myRunnable = new Runnable() {
#Override
public void run() {
new Listeleme().execute();
}
};
myRunnable.run();
//handler.removeCallbacks(myRunnable);
//I guess I need call the above but I couldn't apply properly
return view;
}
private class Listeleme extends AsyncTask<Void,Void,Void>
{
String URL="...";
#Override
protected void onPreExecute()
{
super.onPreExecute();
fpd.show(getActivity().getFragmentManager(),"");
}
#Override
protected Void doInBackground(Void... params) {
//...
return null;
}
#Override
protected void onPostExecute(Void avoid)
{
if (getActivity()!= null) {
}
if (fpd.isVisible()) {
fpd.dismissAllowingStateLoss();
}
}
}
And this is the line 245 - FlipProgressDialog. java
#Override
public void onDestroyView() {
Dialog dialog = getDialog();
if (dialog != null && getRetainInstance()) {
dialog.setDismissMessage(null);
}
handler.removeCallbacks(r); // line 245
super.onDestroyView();
}
Apparently, some resources of your FlipProgressDialog are destroyed when you enter onPause(), so calling anything after everything gets destroyed causes that exception. Just try to call fpd.dismissAllowingStateLoss() before super.onPause()

Custom progressbar dialog with dialogFragment

I'm new to android programming. I want to create a custom progress dialog with some textview and button and showing the progress with two progressBar and updating them while sending the files from Asynctask, Also I want it works with minimum API 10. google doc recommend me to use DialogFragment and i do not have any idea how to update the progress bars and textviews that are in that custom layout of my fragmentDialog, when I try to reference a textview or progress bar it throw null exeption
Here is my code
public static class FireMissilesDialogFragment extends DialogFragment {
public FireMissilesDialogFragment(){
}
public static FireMissilesDialogFragment newInstance(String title) {
FireMissilesDialogFragment frag = new FireMissilesDialogFragment();
Bundle args = new Bundle();
args.putString("title", title);
frag.setArguments(args);
return frag;
}
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
// Use the Builder class for convenient dialog construction
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
// Get the layout inflater
LayoutInflater inflater = this.getActivity().getLayoutInflater();
// Inflate and set the layout for the dialog
// Pass null as the parent view because its going in the dialog layout
View view = inflater.inflate(R.layout.custom_progress, null);
ProgressBar pbCurrent = (ProgressBar) view.findViewById(R.id.current);
builder.setView(view);
builder.setMessage("Fire Missiles")
.setPositiveButton("Fire", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// FIRE ZE MISSILES!
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// User cancelled the dialog
}
});
// Create the AlertDialog object and return it
return builder.create();
}
}
I got a nullExeption here in my main activity when try to reference a view
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button frag = (Button) findViewById(R.id.frag);
frag.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FireMissilesDialogFragment fragment = FireMissilesDialogFragment.newInstance("hi") ;
fragment.getActivity().findViewById(R.id.current);// nullExeption here
// downloadAsync as = new downloadAsync();
// as.execute();
}
});
I didn't see much question and example about that, did I go all the way wrong and should pick another way to achieve my point??!!
Edit :
I'm trying to create something like this
thanks in advance
I can not do a full explanation but I can leave an example and then hopefully you can figure out a way to incorporate the things you need.
The DialogFragment with an AsyncTask and a Progress bar:
public class LoadHydrantsToMapTaskFragment extends DialogFragment {
public static final String TAG = LoadHydrantsToMapTaskFragment.class
.getSimpleName();
public interface LoadHydrantsToMapTaskCallback {
void onPreExecute(int maxProgress);
void onProgressUpdate(int progress);
void onCancelled();
void onPostExecute();
}
private LoadHydrantsToMapTask mTask;
// private ProgressBar mProgressBar;
private List<HydrantHolder> mHydrants;
private GoogleMap map;
public static LoadHydrantsToMapTaskFragment newInstance(
List<HydrantHolder> hydrants, GoogleMap map) {
LoadHydrantsToMapTaskFragment taskFragment = new LoadHydrantsToMapTaskFragment();
taskFragment.mHydrants = hydrants;
taskFragment.map = map;
return taskFragment;
}
#Override public void onAttach(Activity activity) {
super.onAttach(activity);
}
#Override public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_progress_task, container);
mProgressBar = (ProgressBar) view.findViewById(R.id.progressBar);
mProgressBar.setProgress(0);
mProgressBar.setMax(mHydrants.size());
getDialog().setTitle(getActivity().getString(R.string.adding_hydrants));
// This dialog can't be canceled by pressing the back key.
getDialog().setCancelable(false);
getDialog().setCanceledOnTouchOutside(false);
return view;
}
/**
* This method will only be called once when the retained Fragment is first
* created.
*/
#Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setStyle(SherlockDialogFragment.STYLE_NORMAL, R.style.TuriosDialog);
// Retain this fragment across configuration changes.
setRetainInstance(true);
mTask = new LoadHydrantsToMapTask(mHydrants);
mTask.setCallback(new LoadHydrantsToMapTaskCallback() {
#Override public void onPreExecute(int maxProgress) {
}
#Override public void onProgressUpdate(int progress) {
mProgressBar.setProgress(progress);
}
#Override public void onPostExecute() {
if (isResumed())
dismiss();
mTask = null;
}
#Override public void onCancelled() {
if (isResumed())
dismiss();
mTask = null;
}
});
mTask.execute();
}
#Override public void onResume() {
super.onResume();
// This is a little hacky, but we will see if the task has finished
// while we weren't
// in this activity, and then we can dismiss ourselves.
if (mTask == null)
dismiss();
}
#Override public void onDetach() {
super.onDetach();
}
// This is to work around what is apparently a bug. If you don't have it
// here the dialog will be dismissed on rotation, so tell it not to dismiss.
#Override public void onDestroyView() {
if (getDialog() != null && getRetainInstance())
getDialog().setDismissMessage(null);
super.onDestroyView();
}
// Also when we are dismissed we need to cancel the task.
#Override public void onDismiss(DialogInterface dialog) {
super.onDismiss(dialog);
// If true, the thread is interrupted immediately, which may do bad
// things.
// If false, it guarantees a result is never returned (onPostExecute()
// isn't called)
// but you have to repeatedly call isCancelled() in your
// doInBackground()
// function to check if it should exit. For some tasks that might not be
// feasible.
if (mTask != null)
mTask.cancel(false);
}
private class LoadHydrantsToMapTask extends
AsyncTask<Void, Integer, List<MarkerOptions>> {
// Before running code in separate thread
List<HydrantHolder> mHydrants;
LoadHydrantsToMapTaskCallback mLoadHydrantsToMapTaskCallback;
public LoadHydrantsToMapTask(List<HydrantHolder> hydrants) {
this.mHydrants = hydrants;
}
public void setCallback(
LoadHydrantsToMapTaskCallback loadHydrantsToMapTaskCallback) {
this.mLoadHydrantsToMapTaskCallback = loadHydrantsToMapTaskCallback;
}
#Override protected void onPreExecute() {
if (mLoadHydrantsToMapTaskCallback != null) {
mLoadHydrantsToMapTaskCallback.onPreExecute(mHydrants.size());
}
}
// The code to be executed in a background thread.
#Override protected List<MarkerOptions> doInBackground(Void... arg) {
List<MarkerOptions> markers = new ArrayList<MarkerOptions>();
for (HydrantHolder hydrant : mHydrants) {
final String hydrant_type = hydrant.getHydrantType();
final String hydrant_icon_path = hydrant.getIconPath();
double latitude = hydrant.getLatitude();
double longitude = hydrant.getLongitude();
final LatLng position = new LatLng(latitude, longitude);
final String address = hydrant.getAddress();
final String addressNumber = hydrant.getAddressNumber();
final String addressremark = hydrant.getAddressRemark();
final String remark = hydrant.getRemark();
// Log.d(TAG, hydrant.toString());
BitmapDescriptor icon = BitmapDescriptorFactory
.defaultMarker(BitmapDescriptorFactory.HUE_RED);
if (!hydrant_icon_path.isEmpty()) {
File iconfile = new File(hydrant_icon_path);
if (iconfile.exists()) {
BitmapDescriptor loaded_icon = BitmapDescriptorFactory
.fromPath(hydrant_icon_path);
if (loaded_icon != null) {
icon = loaded_icon;
} else {
Log.e(TAG, "loaded_icon was null");
}
} else {
Log.e(TAG, "iconfile did not exist: "
+ hydrant_icon_path);
}
} else {
Log.e(TAG, "iconpath was empty on hydrant type: "
+ hydrant_type);
}
StringBuffer snippet = new StringBuffer();
if (!address.isEmpty())
snippet.append("\n" + address + " " + addressNumber);
if (addressremark.isEmpty())
snippet.append("\n" + addressremark);
if (!remark.isEmpty())
snippet.append("\n" + remark);
markers.add(new MarkerOptions().position(position)
.title(hydrant_type).snippet(snippet.toString())
.icon(icon));
publishProgress(markers.size());
}
return markers;
}
// Update the progress
#Override protected void onProgressUpdate(Integer... values) {
if (mLoadHydrantsToMapTaskCallback != null) {
mLoadHydrantsToMapTaskCallback.onProgressUpdate(values[0]);
}
}
#Override protected void onCancelled() {
if (mLoadHydrantsToMapTaskCallback != null) {
mLoadHydrantsToMapTaskCallback.onCancelled();
}
}
// after executing the code in the thread
#Override protected void onPostExecute(List<MarkerOptions> markers) {
for (MarkerOptions marker : markers) {
if (marker != null && map != null)
map.addMarker(marker);
}
if (mLoadHydrantsToMapTaskCallback != null) {
mLoadHydrantsToMapTaskCallback.onPostExecute();
}
}
}
}
My dialog_progress_task layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
android:maxHeight="10dip"
android:minHeight="10dip"
android:progress="0"
android:progressDrawable="#drawable/progress_orange" />
</LinearLayout>
And finally the code I use to add it:
#Override public void loadHydrantsToMap(List<HydrantHolder> hydrants,
GoogleMap map) {
LoadHydrantsToMapTaskFragment loadHydrantsFragment;
if (fm != null) {
FragmentTransaction ft = fm.beginTransaction();
loadHydrantsFragment = (LoadHydrantsToMapTaskFragment) fm
.findFragmentByTag(LoadHydrantsToMapTaskFragment.TAG);
if (loadHydrantsFragment != null) {
Log.i("Attatching LoadHydrantsToMapTaskFragment");
ft.attach(loadHydrantsFragment);
} else {
loadHydrantsFragment = LoadHydrantsToMapTaskFragment
.newInstance(hydrants, map);
Log.i("Adding new LoadHydrantsToMapTaskFragment");
ft.add(loadHydrantsFragment, LoadHydrantsToMapTaskFragment.TAG);
}
ft.commit();
}
}

Unable to update action bar items with invalidateOptionsMenu() in a Fragment after orientation change

I have number of fragments in a root activity which I set them true for retaining their instances on config changes.
However, this arises a serious issue for me;
I have an AsyncTask in my Fragment.onResume(...) which performs a background job and calls getSherlockActivity().invalidateOptionsMenu(...) method to change the action bar items. When the app first opens fragment worked as expected and invalidating options menu recalls Fragment.oncreateOptionMenu(...) method. But when I change the orientation, AsyncTask is started again and does this background job but invalidating doesn't call Fragment.oncreateOptionMenu(...) so my action bar remains in an unwanted state.
This is my task;
private class ContactListLoader extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
Log.d("Contact List Fragment", "Task started...");
ContactListFragment.mIsRunning = true;
if (emptyView != null) {
if (emptyView instanceof TextView) {
((TextView) emptyView).setText(getResources().getString(
R.string.contact_list_loading_list_title));
}
}
getSherlockActivity().invalidateOptionsMenu();
getSherlockActivity().setSupportProgressBarIndeterminateVisibility(
true);
}
#Override
protected Void doInBackground(Void... params) {
createDataSet();
ContactListFragment.mIsRunning = false;
return null;
}
#Override
protected void onPostExecute(Void result) {
super.onPostExecute(result);
mContactsListView.setAdapter(mAdapter);
mIsContactsLoadedFirstTime = true;
if (emptyView != null) {
if (emptyView instanceof TextView) {
((TextView) emptyView).setText(getResources().getString(
R.string.contact_list_empty_list_title));
}
}
getSherlockActivity().invalidateOptionsMenu();
getSherlockActivity().setSupportProgressBarIndeterminateVisibility(
false);
if (mContactsLocal != null & !mContactsLocal.isEmpty()) {
listener.onContactListLoaded(mContactsLocal.get(0),
mContactsLocal);
}
try {
if (mCapabilityApi != null
&& mCapabilityApi.isImsConnected(getSherlockActivity()
.getApplicationContext())) {
mCapabilityApi.refreshAllCapabilities();
}
} catch (ClientApiException e) {
e.printStackTrace();
}
Toast.makeText(getSherlockActivity().getApplicationContext(),
getResources().getString(R.string.contact_list_updated),
Toast.LENGTH_SHORT).show();
}
#Override
protected void onCancelled() {
Log.d("Contact List Fragment", "task cancelled 2");
ContactListFragment.mIsRunning = false;
super.onCancelled();
}
}
Any ideas?

DialogFragment on Fragment with screen rotation

On my main activity I have a Fragment in which I apply setRetainInstance(true) so that the AsyncTask I use into it is not disturbed by orientation change.
A lot of work is processed by the AsyncTask. That's why I would like to display a dialog with a progressBar on top of my activity.
I made some researches and I succeed in doing with a DialogFragment:
public class DialogWait extends DialogFragment {
private ProgressBar progressBar;
public DialogWait() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_wait, container);
Dialog dialog = getDialog();
dialog.setTitle("Hello");
setCancelable(false);
progressBar = (ProgressBar) view.findViewById(R.id.progress);
return view;
}
public void updateProgress(int value) {
progressBar.setProgress(value);
}
And here is my AsyncTask:
public class InitAsyncTask extends AsyncTask<Void, Integer, Void> {
private Context activity;
private OnTaskDoneListener mCallback;
private DialogWait dialog;
public InitAsyncTask(Context context, OnTaskDoneListener callback, DialogWait dialogWait) {
activity = context;
mCallback = callback;
dialog = dialogWait;
}
#Override
protected Void doInBackground(Void... params) {
doStuff();
return null;
}
#Override
protected void onProgressUpdate(Integer... values) {
dialog.updateProgress(values[0]);
}
#Override
protected void onPostExecute(Void result) {
publishProgress(100);
if(dialog != null)
dialog.dismiss();
mCallback.onTaskDone();
}
private void doStuff() {
//...
}
}
If I don't change the screen rotation, it works fine. But if I do, the dialog is dismissed and a few seconds later, I got a NullPointerEsception which nonsense since I set the condition: if(dialog != null)
What am I doing wrong?
Solution found!
I was not doing the right thing with the Fragment containing my AsyncTask.
Because, I haven't really understood the concept of orientation in Fragment, I get it thanks to this link: http://www.androiddesignpatterns.com/2013/04/retaining-objects-across-config-changes.html
Override onCreate, and onDestroyView methods in your DialogWait as follows:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
#Override
public void onDestroyView() {
if (getDialog() != null && getRetainInstance()) {
getDialog().setDismissMessage(null);
}
super.onDestroyView();
}

Android save data from nested AsyncTask onPostExecute after screen rotation

I have spent many hours looking for a solution to this and need help.
I have a nested AsyncTask in my Android app Activity and I would like to allow the user to rotate his phone during it's processing without starting a new AsyncTask. I tried to use onRetainNonConfigurationInstance() and getLastNonConfigurationInstance().
I am able to retain the task; however after rotation it does not save the result from onPostExecute() to the outer class variable. Of course, I tried getters and setters. When I dump the variable in onPostExecute, that it is OK. But when I try to access to the variable from onClick listener then it is null.
Maybe the code will make the problem clear for you.
public class MainActivity extends BaseActivity {
private String possibleResults = null;
private Object task = null;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.task = getLastNonConfigurationInstance();
setContentView(R.layout.menu);
if ((savedInstanceState != null)
&& (savedInstanceState.containsKey("possibleResults"))) {
this.possibleResults = savedInstanceState
.getString("possibleResults");
}
if (this.possibleResults == null) {
if (this.task != null) {
if (this.task instanceof PossibleResultWebService) {
((PossibleResultWebService) this.task).attach();
}
} else {
this.task = new PossibleResultWebService();
((PossibleResultWebService) this.task).execute(this.matchToken);
}
}
Button button;
button = (Button) findViewById(R.id.menu_resultButton);
button.setOnClickListener(resultListener);
}
#Override
protected void onResume() {
super.onResume();
}
OnClickListener resultListener = new OnClickListener() {
#Override
public void onClick(View v) {
Spinner s = (Spinner) findViewById(R.id.menu_heatSpinner);
int heatNo = s.getSelectedItemPosition() + 1;
Intent myIntent = new Intent(MainActivity.this,
ResultActivity.class);
myIntent.putExtra("matchToken", MainActivity.this.matchToken);
myIntent.putExtra("heatNo", String.valueOf(heatNo));
myIntent.putExtra("possibleResults",
MainActivity.this.possibleResults);
MainActivity.this.startActivityForResult(myIntent, ADD_RESULT);
}
};
private class PossibleResultWebService extends AsyncTask<String, Integer, Integer> {
private ProgressDialog pd;
private InputStream is;
private boolean finished = false;
private String possibleResults = null;
public boolean isFinished() {
return finished;
}
public String getPossibleResults() {
return possibleResults;
}
#Override
protected Integer doInBackground(String... params) {
// quite long code
}
public void attach() {
if (this.finished == false) {
pd = ProgressDialog.show(MainActivity.this, "Please wait...",
"Loading data...", true, false);
}
}
public void detach() {
pd.dismiss();
}
#Override
protected void onPreExecute() {
pd = ProgressDialog.show(MainActivity.this, "Please wait...",
"Loading data...", true, false);
}
#Override
protected void onPostExecute(Integer result) {
possibleResults = convertStreamToString(is);
MainActivity.this.possibleResults = possibleResults;
pd.dismiss();
this.finished = true;
}
}
#Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (this.possibleResults != null) {
outState.putString("possibleResults", this.possibleResults);
}
}
#Override
public Object onRetainNonConfigurationInstance() {
if (this.task instanceof PossibleResultWebService) {
((PossibleResultWebService) this.task).detach();
}
return (this.task);
}
}
It is because you are creating the OnClickListener each time you instantiate the Activity (so each time you are getting a fresh, new, OuterClass.this reference), however you are saving the AsyncTask between Activity instantiations and keeping a reference to the first instantiated Activity in it by referencing OuterClass.this.
For an example of how to do this right, please see https://github.com/commonsguy/cw-android/tree/master/Rotation/RotationAsync/
You will see he has an attach() and detach() method in his RotationAwareTask to solve this problem.
To confirm that the OuterClass.this reference inside the AsyncTask will always point to the first instantiated Activity if you keep it between screen orientation changes (using onRetainNonConfigurationInstance) then you can use a static counter that gets incremented each time by the default constructor and keep an instance level variable that gets set to the count on each creation, then print that.

Categories

Resources