1

我有FragmentActivity两个标签,它们是ListFragments。每个ListFragment都有一个回调。

注意:如果有人注意到,这个问题的大部分是从我自己的问题Callback after orientation change become null重新散列的,但这是一个不同的问题,因为这与旋转无关,但我希望了解我的实际问题回调。)

回调示例

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

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通过这个正常工作的回调与通信。

在应用程序被发送到后台一段时间后,我将应用程序带到前面并触发将使用回调的东西,回调为,我的应用程序崩溃。我相信一旦它在后台并且系统进行垃圾收集就会发生这种情况。

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

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

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

问题

  1. 如果东西被垃圾收集,为什么mTab​​1Callback被垃圾收集而不是恢复?我的印象是Activity生命周期和Fragment生命周期将重新启动,包括onAttach(...)如果部分引用被垃圾收集。
  2. 这是如何正确修复的?

示例应用

我创建了一个展示这种行为的示例应用程序。完整的源代码在这里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()在哪里为,并且在旋转或长期在后台运行之后,它唯一为空的地方是触发回调的toggleEnabled()函数内部。

这似乎表明罪魁祸首是不再存在的MainActivity引用 a 。Fragment

结合空间代码,这本质上是我在单击in后引用toggleEnabled()函数的方式。(完整代码见附件示例)CheckBoxTab1

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

所以我深入研究TabsAdaptergetItem(...)。这里有什么

私有 ArrayList mFragments = new ArrayList(); // 设置标签等的代码 @Override public Fragment getItem(int position) { Fragment frag = mFragments.get(position); if (frag.getActivity() == null) Log.d(TAG, "getItem: Activity is null");

return mFragments.get(position);

}

在旋转之前会发生什么,getActivity()不是null。旋转后,getActivity()null。这似乎表明它没有重新创建我认为Fragment的正确的。Context然而,这就是我对这类事情的了解结束的地方。

我可以制作mFragments静态并且它可以工作,但我觉得这不是一个正确的解决方案。

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

4

2 回答 2

1

OnAttach 看起来以类似于 onCreate 的方式调用,即仅在创建时调用一次,因此当您从后台恢复时,将跳过此方法

于 2012-12-16T22:38:54.750 回答
1

有谁知道为什么片段没有正确附加的活动?

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

主要问题是getItem不能调用 aFragmentPagerAdapter来获得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. 它应该放弃任何手动创建Fragments的有利于 ViewPager 自己的片段。看看它是否有效。

还:

  • 不要使用setRetainInstance(true)
  • 使用引用的静态字段时要小心,Context以避免内存泄漏(例如,您MyObjectAdapter应该将其设为实例字段而不是静态字段)。
于 2012-12-17T19:18:43.290 回答