AsyncTask在ViewPager的每个页面上运行

我在Android开发教程中有3个选项卡

现在我想要做的事情非常简单,我在每个页面上都使用Fragments。 我想在每个页面上显示来自RSS源的不同内容。

问题是当我去到下一个选项卡它运行前一个片段的AsyncTask(这是在onCreateView)。

所以,你从第一页开始就把内容加载好了。 然后当你去到页面2再次运行页面片段的onCreateView。 显然给了一个NullException。 关键是它不应该在第2页上运行第1页的AsyncTask。

我不认为有任何示例代码需要的话,告诉我你需要看哪个部分。 然后,我将编辑我的问题。

一个ListFragment中的AsyncTask:

public class MyAsyncTask extends AsyncTask<List<String>, Void, List<String>> { // List of messages of the rss feed private List<Message> messages; private volatile boolean running = true; @SuppressWarnings("unused") private WeakReference<NieuwsSectionFragment> fragmentWeakRef; private MyAsyncTask(NieuwsSectionFragment fragment) { this.fragmentWeakRef = new WeakReference<NieuwsSectionFragment>(fragment); } @Override protected void onPreExecute() { super.onPreExecute(); mProgress.show(); // progress.setVisibility(View.VISIBLE); //<< set here } @Override protected void onCancelled() { Log.w("onCancelled", "now cancelled"); running = false; } @Override protected List<String> doInBackground(List<String>... urls) { FeedParser parser = FeedParserFactory.getParser(); messages = parser.parse(); List<String> titles = new ArrayList<String>(messages.size()); for (Message msg : messages) { titles.add(msg.getTitle()); // Log.w("doInBackground", msg.getTitle()); } return titles; } @Override protected void onPostExecute(List<String> result) { super.onPostExecute(result); mProgress.dismiss(); if (result != null) { PostData data = null; listData = new PostData[result.size()]; for (int i = 0; i < result.size(); i++) { data = new PostData(); data.postTitle = result.get(i); data.postThumbUrl = "http://igo.nl/foto/app_thumb/28991-Taxi-vast-na-poging-tot-nemen-van-sluiproute.jpg"; listData[i] = data; Log.w("onPostExecute", "" + listData[i].postTitle); } adapter = new PostItemAdapter (getActivity(), android.R.layout.simple_list_item_1, listData); setListAdapter(adapter); adapter.notifyDataSetChanged(); } } } 

它在一个方法内调用,并且该方法在ListFragment的onCreateView内执行:

 @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { startNewAsyncTask(); View rootView = inflater.inflate(R.layout.fragment_section_nieuws, container, false); return rootView; } @SuppressWarnings("unchecked") public void startNewAsyncTask() { MyAsyncTask asyncTask = new MyAsyncTask(this); this.asyncTaskWeakRef = new WeakReference<MyAsyncTask>(asyncTask); asyncTask.execute(); } 

LogCat: logcat的

Solutions Collecting From Web of "AsyncTask在ViewPager的每个页面上运行"

onPostExecute()之前尝试使用isAdded() onPostExecute() 。 如果片段当前被添加到其活动,则isAdded()返回true。

http://developer.android.com/reference/android/app/Fragment.html#isAdded(&#xFF09;

 @Override protected void postExecute(){ if(isAdded()){ //perform Display Changes } } 

移动你的

 startNewAsyncTask(); 

到onActivityCreated()

我假设你的ViewPager使用FragmentPagerAdapter

要启用平滑的animation, ViewPager默认保持当前片段和两个邻居处于恢复状态。 所以onCreateView不是启动AsyncTask的最佳地方。

相反,你需要创build一个自定义的监听器接口。 ViewPagerfragments应该实现它,并从ViewPager's OnPageChangeListener调用新的接口。

看看我对这个问题的回答,或者你可以在这里阅读整个教程。

这里棘手的一点是,在你进入下一个片段( Fragment 2 )之后,来自Fragment 1 AsyncTask仍在运行。 你可能会想:“好吧,在这种情况下,我会停止onDestroyView AsyncTask ”,但另一个棘手的问题是,在调用.cancel方法之后, AsyncTask不会立即停止。 所以,问题是:在切换到Fragment 2后, onPostExecute引用了不存在的资源。 让我们来看看ArrayAdapter:310 :

 mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 

很明显, contextnull 。 让我们深入一下,看看getActivity方法 – 它只是返回mActivity字段的值。

在这种情况下为什么它是null ? 因为一旦Fragment被移除, FragmentManager调用initState ,它会显式地移除到Activity对象的链接:

 void initState() { ... mActivity = null; ... } 

如何保护您的NPE ? 总是检查onPostExecutenullgetActivity的结果。

你得到这个exception,因为你太早调用getActivity() 。 你应该在onActivityCreated()之后做(见下图 )

在后台执行onCreateView()是好的,实际上是默认的行为。 问题是, ViewPager已经过优化,可以在后台加载邻居不可见页面的内容来提高用户体验。 你可以这样做: mViewPager.setOffscreenPageLimit(2); (默认值为1)一次加载所有3个页面(1加载为当前可见,其他2为优化)。 或将其设置为0来禁用此行为,但这不是最好的主意。

一般来说,您应该为您的已加载数据取现,并且不要通过尽可能使您的片段的生命周期方法再次加载。 2的页面限制为3页是好的,但如果你有例如10页,限制9是太多了。

如果我已经理解你的问题,我认为你需要每个片段的独特内容吗?

尝试使用execute方法的variables参数。 例如:

 yourTask.execute(<some-unique-URL>, parameter, one-more-parameter); 

通过这种方式,您可以为每个片段表单传递一个唯一的URL,您可以获取您的内容。 我觉得你已经有了这个 doInBackground方法具有URL列表。 你只需要在执行方法中传递这些信息,并在doInBackground中使用它。

希望这可以帮助!

由于ViewPager + PagerAdapter组合可以加载当前,上一个和下一个Fragment ,所以它从相邻Fragments运行AsyncTask是正常的。

你应该关注问题不要停止运行AsyncTask ,而是让它运行而不抛出NullPointerException

下面应该在onCreateView()里面调用

 adapter = new PostItemAdapter (getActivity(), android.R.layout.simple_list_item_1, myList); setListAdapter(adapter); 

然后, onPostExecute()

 myList.clear(); myList.addAll(listData); adapter.notifyDataSetChanged(); 

ViewPager将为相邻页面中的片段创build视图,并销毁与当前页面不相邻的片段的视图。 因此,如果您从page1-> page2-> page3-> page2导航,则页面1的onCreateView将被调用。 您可以使用ViewPager.setOffscreenPageLimit让viewpager在内存中保留更多的页面。

fragmentPagerAdapter保留片段对象。 只有意见被破坏。 因此,当viewpage重新创buildpage1的视图时,fragment对象是一样的。 因此,片段中的所有字段将被保留。 与大多数没有实时数据的应用程序一样,每次创build片段视图时不需要加载数据,您可以在加载后将数据存储在片段对象中。 然后,在onCreateView / onActivityCreated中启动AsyncTask之前,检查数据是否以前已经加载。

 class PageFragment { private List<String> mData; ... void onActivityCreated() { if (data == null) { // OR if the data is expired startAsyncTask(); } else { updateViews(); } } void updateViews() { // Display mData in views } class LoadDataTask extends AsyncTask<List<String>, ..., ...> { ... void onPostExecute(List<String> result) { PageFragment.this.mData = result; PageFragment.this.updateViews(); } } 

我build议你使用加载器来加载一个片段的数据。 出于您的目的,您可以configuration一个加载器来只加载一次数据。 这是一个很好的装载机教程。 http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html

在本教程中,加载器被configuration为立即返回以前的数据(如果可用),然后在后台获取数据并在获取完成后返回数据。 因此,新的数据被下载后,UI将会被更新,但是同时它会在下载发生的时候显示之前的数据。

您可以使用其他活动 – 该活动将运行asynctask,然后移至与片段相关的活动。 通过这种方式,它只应该调用一次。

如果您需要使用此AsyncTask更新Fragment UI,则使用静态方法通过AsyncTask进行调用。