【问题标题】:App Crashes on AsyncTask Screen Rotation应用程序在 AsyncTask 屏幕旋转时崩溃
【发布时间】:2014-09-29 04:40:16
【问题描述】:

我在活动的其中一个片段中使用 AsyncTask。

AsyncTask 工作正常,但在屏幕旋转时,它失去了对activity 的引用,变量返回NullPointerException,因此应用程序崩溃了。

我查看了类似的问题,例如 thisthisthisthis,但我不认为使用 config change hack 是解决方案。

可能导致应用程序崩溃的代码(根据 LogCat,NullPointerException 位于以下行):

Context context = MyActivity.this.getApplicationContext();

我必须在活动和片段类之外的另一个函数中传递上下文。

提前谢谢你。

更新:我的 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)

【问题讨论】:

  • 嘿,在执行任何操作之前,您必须检查它是否为空。
  • 请发布 logcat 跟踪
  • @Panther 它始终为空,因为屏幕旋转后活动被破坏。这不会导致 AsyncTask 在屏幕旋转后无法运行吗?
  • 使用Context.getApplicationContext()Activity.getApplication()代替MyActivity.this.getApplicationContext();
  • 这里出现了空指针CommonFunctions.readFile(CommonFunctions.java:262)尝试找出它是什么:-/

标签: android android-fragments android-asynctask


【解决方案1】:

对此有很多解决方案。一个简单的方法是使用所谓的“保留”片段。这是在某些 Google 示例中使用的方法。保留片段没有 UI,并且在方向更改时仍然存在。这通常定义为您的活动的静态内部类,如下所示:

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);
    }
}

然后,将 AsyncTask 放入 RetainFragment。当onPostExecute被调用时,你可以简单地使用fragment的getActivity()方法来获取它所附加的Activity。

希望对您有所帮助,如果仍有一些困惑,请告诉我!

编辑:Here 是 Google 保留片段的示例

编辑 2:(根据 cmets)

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 {
        //...
    }
}

编辑 3: 在 cmets 中,我最终还是编写了完整的代码。对于任何有兴趣的人:

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

【讨论】:

  • 这看起来足够优雅。问题:我是否必须使用静态类才能使此解决方案起作用?
  • 如果它不是静态的,你就会有内存泄漏
  • 将片段类设为静态不会导致将来出现一些不可预见的异常吗?我不知道,我只是把它扔在那里。
  • 因为这不是我的应用程序中唯一的片段。还有很多其他的,它们通过创建片段对象并将其传递给fragmentManager 来调用。其中一些片段是在同一个活动上实例化的。
  • 将类设为静态意味着它不会保留对其包含类的引用,即本例中的 Activity。这不是问题。如果它不是静态的,那将是一个问题,因为非静态内部类保留对其包含类的隐式强引用,因此即使在系统完成它之后也会保留 Activity 实例(导致内存泄漏)。
【解决方案2】:

试试这个方法

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

【讨论】:

  • 我宁愿不这样做,因为 Android 开发人员指南不建议这样做:developer.android.com/guide/topics/manifest/…
  • 我知道安卓不推荐但不拒绝使用。如果在运行时没有多种语言支持并且键盘僵硬,您可以使用它。
  • 我在很多项目中都使用过它,但由于这种方法没有发现这么大的问题。
  • 我宁愿先探索其他不那么老套的选项,然后再考虑使用它。感谢您的回复。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-11
  • 2017-05-16
  • 1970-01-01
  • 1970-01-01
  • 2023-03-03
  • 1970-01-01
相关资源
最近更新 更多