简体   繁体   中英

Android best way to update UI thread from AsyncTask

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, getActivity returns 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(); 
    } 
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM