具有 AsyncTaskLoader 的片段会影响其他片段'依附于活动
我使用 AsyncTaskLoader 从数据库查询加载游标。 我遵循 Android 开发人员示例: http://developer.android.com/reference/android/content/AsyncTaskLoader.html
但不知何故,在此片段(使用加载器)之后添加到页面适配器的片段不会附加到活动,并且当它尝试使用需要活动的方法(如 getString())时抛出异常并说该片段未附加到任何活动。
以下是一些代码:
将片段添加到页面适配器。
mAdapter = new PlaceFragmentPagerAdapter(getSupportFragmentManager()); NewFragment newFrag = new NewFragment(); mAdapter.addFragment(newShiftFrag); ListFragment listFrag = new ListFragment(); mAdapter.addFragment(listFrag); SettingsFragment settingsFrag = new SettingsFragment(); mAdapter.addFragment(settingsFrag); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter);
AsyncTaskLoader 实现:
抽象公共类 AbstractCursorLoader 扩展 AsyncTaskLoader {
Abstract protected Cursor buildCursor(); 光标lastCursor=null; 公共 AbstractCursorLoader(上下文上下文){ 超级(上下文); } /** * 在工作线程上运行,加载我们的数据。代表们 * 真正的工作是具体子类的 buildCursor() 方法。 */ @覆盖 公共光标loadInBackground() { 光标cursor=buildCursor(); if (光标!=null) { // 确保光标窗口被填满 游标.getCount(); } 返回(光标); } /** * 在UI线程上运行,将结果路由到 * 任何使用光标的后台线程 *(例如,CursorAdapter)。 */ @覆盖 公共无效交付结果(光标光标){ 如果(isReset()){ // 加载器停止时传入异步查询 if (光标!=null) { 光标.close(); } 返回; } 光标oldCursor=lastCursor; 最后光标=光标; 如果(已开始()){ super.deliverResult(光标); } if (oldCursor!=null && oldCursor!=cursor && !oldCursor.isClosed()) { oldCursor.close(); } } /** * 启动列表数据的异步加载。 * 当结果准备好时,回调将被调用 * 在 UI 线程上。如果之前的加载已完成 * 并且仍然有效,结果可以传递给 * 立即回调。 * * 必须从UI线程调用。 */ @覆盖 受保护无效 onStartLoading() { if (lastCursor!=null) { 交付结果(最后一个光标); } if (takeContentChanged() || lastCursor==null) { 强制加载(); } } /** * 必须从UI线程调用,由a触发 * 调用stopLoading()。 */ @覆盖 受保护无效 onStopLoading() { // 如果可能的话尝试取消当前的加载任务。 取消加载(); } /** * 必须从UI线程调用,由a触发 * 调用cancel()。在这里,我们确保我们的光标 * 已关闭(如果它仍然存在且尚未关闭)。 */ @覆盖 公共无效onCanceled(光标光标){ if (cursor!=null && !cursor.isClosed()) { 光标.close(); } } /** * 必须从UI线程调用,由a触发 * 调用reset()。在这里,我们确保我们的光标 * 已关闭(如果它仍然存在且尚未关闭)。 */ @覆盖 受保护无效 onReset() { super.onReset(); // 确保加载器已停止 onStopLoading(); if (lastCursor!=null && !lastCursor.isClosed()) { 最后的游标.close(); } 最后光标=空; } } 私有静态类 ListLoader 扩展 AbstractCursorLoader { 私有字符串 mName; 公共 ShiftsListLoader(上下文上下文,字符串名称){ 超级(上下文); mName = 姓名; } @覆盖 受保护的游标 buildCursor() { PlacesHandler wph = new PlacesHandler(this.getContext()); 返回 wph.GetShifts(mName); } }
初始化加载器:
<预><代码> @Override 公共无效onActivityCreated(捆绑保存实例状态){ super.onActivityCreated(savedInstanceState); // 如果没有数据,则提供一些文本来显示。在真实的 // 应用程序这将来自资源。 // TODO 更改资源并返回 setEmptyText("这里什么都没有.."); // 从进度指示器开始。 setListShown(假); // 准备加载器。要么重新连接现有的, // 或者开始一个新的。 getLoaderManager().initLoader(0, null, this);}
<预><代码> @Override 公共加载器onCreateLoader(int id, Bundle args) { 返回新的 ListLoader(getActivity(), mWorkPlaceName); } @覆盖 public void onLoadFinished(Loader loader, 光标数据) { // 创建一个空适配器,我们将用它来显示加载的数据。 mAdapter = new ListCursorAdapter(getActivity(), data, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); setListAdapter(mAdapter); } @覆盖 public void onLoaderReset(Loader loader) { // TODO 自动生成的方法存根 }
我真的不知道为什么会发生这种情况。
PS 我在评论中制作代码块时遇到了一些问题,我认为它有一个错误,很抱歉。
预先感谢,埃拉德。
I've used AsyncTaskLoader to load a cursor from a database query.
I followed Android Developers sample:
http://developer.android.com/reference/android/content/AsyncTaskLoader.html
But somehow the fragments that being added to the page adapter after this fragment (which uses the loader) doesn't get attached to the activity and when it tries to use methods that need an activity (like getString()) an exception is being thrown and says that this fragment isn't attached to any activity.
Here is some code:
Adding the fragments to the page adapter.
mAdapter = new PlaceFragmentPagerAdapter(getSupportFragmentManager()); NewFragment newFrag = new NewFragment(); mAdapter.addFragment(newShiftFrag); ListFragment listFrag = new ListFragment(); mAdapter.addFragment(listFrag); SettingsFragment settingsFrag = new SettingsFragment(); mAdapter.addFragment(settingsFrag); mPager = (ViewPager)findViewById(R.id.pager); mPager.setAdapter(mAdapter);
AsyncTaskLoader implementation:
abstract public class AbstractCursorLoader extends AsyncTaskLoader {
abstract protected Cursor buildCursor(); Cursor lastCursor=null; public AbstractCursorLoader(Context context) { super(context); } /** * Runs on a worker thread, loading in our data. Delegates * the real work to concrete subclass' buildCursor() method. */ @Override public Cursor loadInBackground() { Cursor cursor=buildCursor(); if (cursor!=null) { // Ensure the cursor window is filled cursor.getCount(); } return(cursor); } /** * Runs on the UI thread, routing the results from the * background thread to whatever is using the Cursor * (e.g., a CursorAdapter). */ @Override public void deliverResult(Cursor cursor) { if (isReset()) { // An async query came in while the loader is stopped if (cursor!=null) { cursor.close(); } return; } Cursor oldCursor=lastCursor; lastCursor=cursor; if (isStarted()) { super.deliverResult(cursor); } if (oldCursor!=null && oldCursor!=cursor && !oldCursor.isClosed()) { oldCursor.close(); } } /** * Starts an asynchronous load of the list data. * When the result is ready the callbacks will be called * on the UI thread. If a previous load has been completed * and is still valid the result may be passed to the * callbacks immediately. * * Must be called from the UI thread. */ @Override protected void onStartLoading() { if (lastCursor!=null) { deliverResult(lastCursor); } if (takeContentChanged() || lastCursor==null) { forceLoad(); } } /** * Must be called from the UI thread, triggered by a * call to stopLoading(). */ @Override protected void onStopLoading() { // Attempt to cancel the current load task if possible. cancelLoad(); } /** * Must be called from the UI thread, triggered by a * call to cancel(). Here, we make sure our Cursor * is closed, if it still exists and is not already closed. */ @Override public void onCanceled(Cursor cursor) { if (cursor!=null && !cursor.isClosed()) { cursor.close(); } } /** * Must be called from the UI thread, triggered by a * call to reset(). Here, we make sure our Cursor * is closed, if it still exists and is not already closed. */ @Override protected void onReset() { super.onReset(); // Ensure the loader is stopped onStopLoading(); if (lastCursor!=null && !lastCursor.isClosed()) { lastCursor.close(); } lastCursor=null; } } private static class ListLoader extends AbstractCursorLoader { private String mName; public ShiftsListLoader(Context context, String name) { super(context); mName = name; } @Override protected Cursor buildCursor() { PlacesHandler wph = new PlacesHandler(this.getContext()); return wph.GetShifts(mName); } }
Initializng the loader:
@Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); // Give some text to display if there is no data. In a real // application this would come from a resource. // TODO change to resource and back setEmptyText("Nothing here.."); // Start out with a progress indicator. setListShown(false); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this);
}
@Override public Loader<Cursor> onCreateLoader(int id, Bundle args) { return new ListLoader(getActivity(), mWorkPlaceName); } @Override public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Create an empty adapter we will use to display the loaded data. mAdapter = new ListCursorAdapter(getActivity(), data, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); setListAdapter(mAdapter); } @Override public void onLoaderReset(Loader<Cursor> loader) { // TODO Auto-generated method stub }
I really don't have a clue why is it happening.
P.S. I had some problems making the code blocks in the comment I thing it has a bug, so sorry.
Thanks in advance, Elad.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
简而言之,您永远不应该假定片段处于任何特定状态,直到它收到适当的生命周期回调信号为止。
您所看到的是 ViewPager 利用的 ICS 期间添加的优化。 FragmentPagerAdapter 通过调用 setUserVisibleHint。 FragmentManager 使用它来优先考虑加载程序的执行方式,以便用户首先看到完全加载的可见页面,并且加载侧页不会减慢加载可见页面的过程。具体来说,它延迟将片段移至“已启动”状态,这也是加载器开始运行的时间。
如果用户在此过程中滚动到另一个页面,FragmentManager 会将片段移至启动状态,并作为 FragmentPagerAdapter#setPrimaryItem() 的一部分立即开始运行其加载程序,因为此方法将当前页面标记为现在是用户页面可见的。
The short answer is that you should never assume a fragment is in any specific state until it receives the appropriate lifecycle callback signaling it.
What you're seeing is an optimization added during ICS that ViewPager takes advantage of. FragmentPagerAdapter specially marks offscreen fragments as not being user-visible by calling setUserVisibleHint. The FragmentManager uses this to prioritize how loaders are executed so that the user will see the fully loaded visible page first, and loading side pages does not slow down the process of loading the visible page. Specifically, it delays moving the fragment into the "started" state, which is also when loaders start running.
If the user scrolls over to another page while this is in process the FragmentManager will move the fragment into the started state and begin running its loaders immediately as part of FragmentPagerAdapter#setPrimaryItem(), since this method marks the current page as now being user-visible.