我有一个FragmentActivity,其中有两个选项卡是ListFragment。每个ListFragment都有一个回调。

(注意:万一注意到它,大部分问题将从我自己的问题Callback after orientation change becomes null中消失,但这是一个不同的问题,因为这与轮换无关,但我希望了解回调的实际问题。)

回调示例

回调与onAttach(...)方法内部相关联,如http://developer.android.com/training/basics/fragments/communicating.html所述

OnTab1Listener mTab1Callback;

public interface OnTab1Listener {
    public void onTab1Update();
}

@Override
public void onAttach(Activity activity) {
    Log.d(TAG, "onAttach");
    super.onAttach(activity);

    try {
        mTab1Callback = (OnTab1Listener)activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " must implement OnTab1Listener");
    }
}


稍后,我通过此回调与FragmentActivity通信,该回调正常工作。

在将应用程序发送到后台一段时间后,我将应用程序置于最前面并触发了将使用回调的内容,该回调为null,我的应用程序崩溃了。我相信一旦在后台并且系统进行垃圾收集,就会发生这种情况。

这是一个示例调用,在正常执行期间可以正常工作。

public void toggleEnabled(View v) {
    Log.d(TAG, "toggleEnabled");

    // null pointer here
    mTab1Callback.onTab1Update();
}





为什么mTab1Callback垃圾会被收集,而如果事情被垃圾收集,则不会还原?我的印象是,如果部分引用被垃圾回收,则Activity生命周期和Fragment生命周期将重新启动,包括onAttach(...)。
如何正确解决?


示例应用

我创建了一个展示这种行为的示例应用程序。完整的源代码在这里http://www.2shared.com/file/kkgFejUq/broken_example.html

应用程序堆栈跟踪示例

FATAL EXCEPTION: main
java.lang.IllegalStateException: Could not execute method of the activity
at android.view.View$1.onClick(View.java:3591)
at android.view.View.performClick(View.java:4084)
at android.widget.CompoundButton.performClick(CompoundButton.java:100)
at android.view.View$PerformClick.run(View.java:16966)
at android.os.Handler.handleCallback(Handler.java:615)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at android.view.View$1.onClick(View.java:3586)
... 12 more
Caused by: java.lang.NullPointerException
at com.example.myapp.Tab2.toggleEnabled(Tab1.java:87)
at com.example.myapp.MainActivity.onClick(MainActivity.java:50)
... 15 more


这是Tab1.java:87

mTab1Callback.onTab1Update();


如果您让应用程序转到后台,做一些其他事情(例如玩点游戏,听音乐等),然后将其返回到前台并单击其中一个复选框,则它将变为空指针。

如果有问题,我将应用程序图标放在启动器上,然后从启动器启动它,然后在运行其他应用程序一段时间后将其置于前台。这是当它崩溃时。

另外,我不能让它在模拟器上崩溃,只能在真正的硬件上崩溃。将电话插入计算机时,崩溃的可能性似乎较小,而在测试过程中拔掉电话则更容易崩溃。

TabsAdapter是否与此有关?

此外,这将使用文件中包含的SherlockActionBar。它应该用作MyApp的库。

更新资料

我开始看到getActivity()为null的位置,并且在旋转或长期在后台运行后,它唯一为null的位置位于触发回调的toggleEnabled()函数内部。

这似乎表明罪魁祸首是引用不再存在的MainActivityFragment

组合代码以获取空间,这基本上就是我单击CheckBox中的Tab1后引用toggleEnabled()函数的方式。 (有关完整代码,请参见随附的示例)

public void onClick(View v) {
     Tab1 tab = (Tab1)mTabsAdapter.getItem(0);
     tab.toggleEnabled(v);
}


所以我钻入TabsAdapter跟随getItem(...)。这是那里

私有ArrayList mFragments = new ArrayList();
//设置标签等的代码
@Override
public Fragment getItem(int position){
    片段碎片= mFragments.get(position);
    如果(frag.getActivity()== null)
        Log.d(TAG,“ getItem:活动为空”);

return mFragments.get(position);


}

发生在轮换之前,getActivity()不为null。旋转后,getActivity()为null。这似乎表明它没有针对我认为正确的Fragment重新创建Context。但是,这就是我对这类事情的了解的结尾。

我可以将mFragments设为静态,并且可以工作,但是我觉得那不是正确的解决方案。

有谁知道为什么Fragment没有正确连接的Activity

最佳答案

有谁知道为什么碎片没有正确连接
  活动?


这是因为ViewPager的工作方式。首次设置ViewPager(以及实例化两个片段)时,它将调用getItem()方法,并且它将收到正确的Fragment。旋转手机后,Activity以及其中的所有Fragments将被销毁并重新创建。此时,ViewPager将尝试在内部重新创建其状态,并使Fragments匹配先前的状态,并构造自己的Fragments。通过Fragments方法添加的您自己的addTab将不会使用,因为旋转手机后,初始getItem()不会调用Fragments方法。问题在于,使用您当前的代码,您调用getItem方法以获取FragmentsViewPager,该方法将从列表中检索Fragments。这些片段是不与Activity绑定的简单实例,因此在onAttach()方法中设置的回调是null

主要问题是不能在getItem上调用FragmentPagerAdapter来获取该位置的Fragment。关于如何做到这一点,有很多关于stackoverflow的问题。我编写了以下方法,无论ViewPager做什么(通过删除例如您设置的片段),始终在片段列表中始终具有正确的片段。您的当前代码将保持不变,只需添加:

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment created = (Fragment) super
            .instantiateItem(container, position);
    if (position < mFragments.size()) {
        Fragment fromList = mFragments.get(position);
        if (!created.equals(fromList)) {
            mFragments.set(position, created);
        }
    } else {
        int difference = position - mFragments.size();
        if (difference == 0) {
            mFragments.add(created);
        } else {
            for (int i = 0; i < difference - 1; i++) {
                mFragments.add(null);
            }
            mFragments.add(created);
        }
    }
    return created;
}


TabsAdapter。它应该删除任何有利于ViewPager自身片段的手动创建的Fragments。看看是否有效。

也:


不要使用setRetainInstance(true)
使用引用了Context的静态字段时要小心,以避免内存泄漏(例如,应将MyObjectAdapter设置为实例字段而不是静态字段)。

10-08 18:26