App Crashes on AsyncTask Screen Rotation

坚强是说给别人听的谎言 提交于 2019-12-30 13:59:17

问题


I'm using AsyncTask in one of the fragments of an activity.

The AsyncTask is working perfectly but on screen rotation, it loses the reference to activity and the variable returns NullPointerException thus the app crashes.

I looked at similar questions like this, this, this, and this, but I don't think using config change hack is the solution.

The code that maybe crashing the application (according to LogCat, the NullPointerException is at the following line):

Context context = MyActivity.this.getApplicationContext();

I've to pass the context in another function residing outside of the activity and fragment class.

Thank you in advance.

Update: My LogCat

09-29 09:40:53.415: E/AndroidRuntime(21997): FATAL EXCEPTION: AsyncTask #2
09-29 09:40:53.415: E/AndroidRuntime(21997): java.lang.RuntimeException: An error occured while executing doInBackground()
09-29 09:40:53.415: E/AndroidRuntime(21997):    at android.os.AsyncTask$3.done(AsyncTask.java:278)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at java.util.concurrent.FutureTask.run(FutureTask.java:137)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:208)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at java.lang.Thread.run(Thread.java:856)
09-29 09:40:53.415: E/AndroidRuntime(21997): Caused by: java.lang.NullPointerException
09-29 09:40:53.415: E/AndroidRuntime(21997):    at com.example.CommonClasses.CommonFunctions.readFile(CommonFunctions.java:262)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at com.example.CommonClasses.CommonFunctions.readFileContents(CommonFunctions.java:308)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at com.example.android.AvailabilityFragment$AvailabilityData.doInBackground(AvailabilityFragment.java:160)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at com.example.android.AvailabilityFragment$AvailabilityData.doInBackground(AvailabilityFragment.java:1)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at android.os.AsyncTask$2.call(AsyncTask.java:264)
09-29 09:40:53.415: E/AndroidRuntime(21997):    at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)

回答1:


There are plenty of solutions to this. One simple one for you is to use what's known as a "Retain" fragment. This is an approach that is used in some of the Google samples. A retain fragment has no UI, and persists across orientation change. This is usually defined as a static inner class of your activity like so:

public static class RetainFragment extends Fragment {
    private static final String TAG = "RetainFragment";

    public RetainFragment() {}

    public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
        RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
        if (fragment == null) {
            fragment = new RetainFragment();
        }
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }
}

Then, put your AsyncTask inside of your RetainFragment. When onPostExecute is called, you can simply use the fragment's getActivity() method to get the Activity that it is attached to.

Hope that helps, let me know if there is still some confusion!

Edit: Here is a Google sample of a retain fragment

Edit 2: (as per comments)

public class A {

    // This class holds a reference to it's outer A instance. It can be
    // accessed using A.this.
    public class innerClassA {
        //...
    }

    // This class does not hold a reference to it's outer A instance.
    public static class innerClassB {
        //...
    }
}

Edit 3: In the comments, I ended up writing the full code anyway. For anyone interested:

public class MyActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_my);

        Button b = (Button) findViewById(R.id.aaa);
        b.setOnClickListener(new View.OnClickListener() {
            @Override public void onClick(View v) {
                RetainFragment retainFragment =
                        RetainFragment.findOrCreateRetainFragment(getFragmentManager());
                retainFragment.loadAsync();
            }
        });
    }

    private void handleResponse(String response) {
        // do something with the response...
    }

    public static class RetainFragment extends Fragment {
        private static final String TAG = "RetainFragment";

        public RetainFragment() {
        }

        public static RetainFragment findOrCreateRetainFragment(FragmentManager fm) {
            RetainFragment fragment = (RetainFragment) fm.findFragmentByTag(TAG);
            if (fragment == null) {
                fragment = new RetainFragment();
                fm.beginTransaction().add(fragment, TAG).commit();
            }
            return fragment;
        }

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setRetainInstance(true);
        }

        private void loadAsync() {
            new AsyncTask<Void, Void, String>() {
                @Override protected String doInBackground(Void... params) {
                    // Do some work...
                    return null;
                }

                @Override protected void onPostExecute(String response) {
                    MyActivity myActivity = (MyActivity)getActivity();
                    if (myActivity != null) {
                        myActivity.handleResponse(response);
                    }
                }
            }.execute();
        }
    }
}



回答2:


Try this way

<activity
    android:name=".ActivityName"
    android:configChanges="orientation|screenSize|keyboardHidden"/>


来源:https://stackoverflow.com/questions/26092671/app-crashes-on-asynctask-screen-rotation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!