【发布时间】:2012-12-18 03:18:43
【问题描述】:
我有一个FragmentActivity,其中有两个标签是ListFragments。每个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 并且在旋转或长期在后台运行之后,它唯一为空的地方是 toggleEnabled () 触发回调的函数。
这似乎表明罪魁祸首是 MainActivity 引用了一个不再存在的 Fragment。
结合空间代码,这基本上是我在单击Tab1 中的CheckBox 后引用toggleEnabled() 函数的方式。 (完整代码见附件示例)
public void onClick(View v) {
Tab1 tab = (Tab1)mTabsAdapter.getItem(0);
tab.toggleEnabled(v);
}
所以我挖了TabsAdapter 来关注getItem(...)。这是那里的东西
私有 ArrayList mFragments = new ArrayList(); // 设置标签等的代码 @覆盖 公共片段getItem(int位置){ 片段片段 = mFragments.get(position); if (frag.getActivity() == null) Log.d(TAG, "getItem: Activity 为空");
return mFragments.get(position);
}
发生在旋转之前,getActivity() 是 not null。旋转后,getActivity() 为 null。这似乎表明它并没有重新创建 Fragment 关于正确的 Context 我相信。然而,这就是我对这类事情的了解结束的地方。
我可以将mFragments 设为静态并且它可以工作,但我觉得这不是一个正确的解决方案。
有谁知道为什么Fragments 没有正确附加Activity?
【问题讨论】:
-
您能否分享一个显示此行为的示例应用程序?
-
我添加了一个示例应用程序和该应用程序的堆栈跟踪。
-
我没有看到那个异常。在你的片段中丢失
setRetainInstance(true);调用(你为什么使用它?),看看你是否再次看到这种行为。 -
我试试看。我使用它的原因是在另一个
Activity/Fragment组合中,它有许多 UI 元素,我用来保留它们的状态。我想我在这部分应用程序中遇到的Fragments 没有必要 -
您不需要保留实例,尤其是当您使用
ViewPager中的片段时,您只有两个片段,因此它们都可用。即使这样,您的代码也应该可以工作,因为即使您保留实例,仍会调用onAttach回调。