2013-02-15 56 views
0

我写了一个活动ViewPager,在AsyncTask执行后填充。每个TestDataObject都与相关的TestFragment相关联。当屏幕旋转时,由于onCreateView方法中的NullPointerException,应用程序会崩溃。我认为这是因为ViewPager /适配器onSaveInstanceState方法,onCreateView尝试在数据尚未提供时,在数据加载AsyncTask之前恢复数据。活动onDestroy-onCreate后ViewPager和活动重新创建/保留片段中的数据

我可以只是if onCreateView代码,但它不觉得我喜欢一个正确的解决方案,因为ViewPager内的片段数量可能会有所不同,因此它可能会最终做不必要的工作:恢复更改的viewpager内容,然后用初始替换。在这种情况下,onSaveInstanceState似乎过于有害。据推测,我可以扩展ViewPager或Adapter来取消保存过程 - 我觉得它也很奇怪。

你有什么更好的建议可以提供吗?

public class MainActivity extends LoggerActivity { 

    private ArrayList<TestDataObject> mDataObjects = new ArrayList<MainActivity.TestDataObject>(); 

    private ViewPager mViewPager; 
    private TestFragmentAdapter mViewPagerAdapter; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.activity_main); 

     mViewPager = (ViewPager) findViewById(R.id.pager); 
     mViewPagerAdapter = new TestFragmentAdapter(
       getSupportFragmentManager(), mDataObjects); 
     mViewPager.setAdapter(mViewPagerAdapter); 
     new TestAsyncTask().execute(); 
    } 

    private class TestAsyncTask extends AsyncTask<Void, Void, Void> { 
     @Override 
     protected Void doInBackground(Void... params) { 
      try { 
       TimeUnit.SECONDS.sleep(3); 
      } catch (InterruptedException e) { 
       // TODO Auto-generated catch block 
       e.printStackTrace(); 
      } 
      return null; 
     } 

     @Override 
     protected void onPostExecute(Void result) { 
      super.onPostExecute(result); 
      mDataObjects.add(new TestDataObject()); 
      mDataObjects.add(new TestDataObject()); 
      mDataObjects.add(new TestDataObject()); 
      mViewPagerAdapter.notifyDataSetChanged(); 

     } 
    } 

    public static class TestFragment extends Fragment { 

     private TestDataObject mDataObject; 

     public static TestFragment getInstance(TestDataObject obj) { 
      TestFragment f = new TestFragment(); 
      f.mDataObject = obj; 
      return f; 
     } 

     @Override 
     public View onCreateView(LayoutInflater inflater, ViewGroup container, 
       Bundle savedInstanceState) { 
      // layout.find... 
      mDataObject.toString(); 
      return inflater.inflate(R.layout.fragment_test, null, false); 
     } 

    } 

    public static class TestFragmentAdapter extends FragmentStatePagerAdapter { 

     private List<TestDataObject> mDataObjects; 

     public TestFragmentAdapter(FragmentManager fm, List<TestDataObject> objs) { 
      super(fm); 
      mDataObjects = objs; 
     } 

     @Override 
     public Fragment getItem(int position) { 
      return TestFragment.getInstance(mDataObjects.get(position)); 
     } 

     @Override 
     public int getCount() { 
      return mDataObjects == null ? 0 : mDataObjects.size(); 
     } 

    } 

    public static class TestDataObject { 
    } 
} 
+0

我想你需要添加一些理解代码所需的代码部分,例如onSavedInstance方法。 – 2013-02-15 11:23:13

+0

没有什么我用onSaveInstanceState做的。它是试图保存当前视图状态的组件本身。我应该看看http://stackoverflow.com/questions/9735514/persisting-list-items-in-listadapter-on-configurationchanges-with-setretaininsta – midnight 2013-02-15 13:43:59

+0

有不同的方式来坚持国家(旋转只是一个例子,你需要坚持这个状态)。一种方法是实现onSaveInstanceState并稍后检索数据。其他方式只是你的链接建议的方式,它不像实现你自己的onSaveInstanceState那样灵活。如果我记得正确的话,如果你使用这种方法,应用程序不会根据当前的新方向寻找新的布局,你应该做到这一点,如果可能的话,这是非常困难的。 – 2013-02-16 00:31:36

回答

0

我相信这是因为ViewPager /适配器的onSaveInstanceState 方法。当数据尚不可用时,onCreateView会尝试在数据加载之前恢复asynctask 之前的数据。

这是不是发生了什么(我假设你在mDataObject.toString();除外),即使AsyncTask将完成它的工作瞬间异常仍然会被抛出。在应用第一次运行后,ViewPager将在其中包含三个片段。当你打开手机时,Activity将被重新摧毁。 ViewPager将尝试在其中重新创建片段,但这次它将通过使用默认的空构造函数来完成(这就是为什么您不应该使用非空构造函数来传递数据)。如您所见,第一次由适配器创建Fragment时,它将由您通过TestDataObject对象的getInstance方法(也是初始化mDataObject的唯一一点)创建。当ViewPager重新初始化其片段时,字段将而不是也被初始化。

如果TestDataObject可以在Bundle放,那么你可以简单地调整你的getInstance方法的一些参数传递给您的片段(所以当ViewPager将重新创建这些数据字段将被初始化)。我相信你已经看到了:

public static TestFragment getInstance(TestDataObject obj) { 
     TestFragment f = new TestFragment(); 
     // f.mDataObject = obj; <- don't do this 
     // if possible 
     Bundle args = new Bundle(); 
     args.put("data", obj); // only if obj can be put in a Bundle 
     f.setArguments(args); 
     return f; 
} 

private TestDataObject mDataObject; 

@Override 
public void onCreate(Bundle savedInstance) { 
    mDataObject = getArguments().get("data"); // again, depends on your TestDataObject 
} 

另一种方法是,以最小的数据量传递给Fragment(如上),因此它有足够的信息来重新每当它重新创建它的数据。