在Android中创建单例片段集合的正确方法是什么?

时间:2012-10-19 05:05:07

标签: java android singleton android-fragments

背景

我有一个最多可显示4个片段的ViewPager。在任何给定时间可用的数量是动态的(可以是1,2,3或4)。让这些片段自我管理是最有意义的。我的意思是他们是单身人士。我没有使用new关键字创建新片段,而是编写了一个'getInstance(String key)'方法,该方法尝试从地图中检索指定键的片段,或者如果映射中不存在,则创建一个新实例,使用给定键将片段放在映射中,使用包含该键的包设置片段参数,以便片段可以在onCreate()上检索它,然后返回该片段的新实例。

对于那些没有关注的人,这里是代码:

public class DishListFragment extends ListFragment {

    public static final String MENU = "menu";

    ...

    private static Map<String, DishListFragment> mInstances = new HashMap<String, DishListFragment>();        

    public static DishListFragment getInstance(String menuKey) {
        if (mInstances.containsKey(menuKey))
            return mInstances.get(menuKey);

        Bundle b = new Bundle();
        b.putString(MENU, menuKey);
        DishListFragment dlf = new DishListFragment();
        dlf.setArguments(b);
        mInstances.put(menuKey, dlf);
        return dlf;

    }
}

您可能已经注意到这不是线程安全的。


操作实例

OnCreate,我的主要活动设置其视图然后产生一个AsyncTask负责从服务器获取数据,该服务器将填充我的DishListFragments。同时,主要活动继续并设置PagerAdapter。当PagerAdapter请求新的DishListFragment时,该数字将动态转换为一个键,并从DishListFragment.getInstance(key)转换为值;方法被返回。最初,ListFragments观察到的内容类具有虚拟数据,因此总会显示一个页面,因此ViewPager始终有一个页面可供显示。

AsyncTask启动时,会显示进度对话框。完成后,它会关闭进度对话框并调用我的活动中的方法,然后在我的内容类中设置数据。然后调用一个方法refresh(),该方法告诉现有的所有DishListFragments通知其适配器数据集已更改。该方法完成后,将通知ViewPagerAdapter其数据集已更改。

在我的主要活动中:

public void onRetrieveData(Result result) {
    switch(result.getCode()) {
    case Result.SUCCESS:
    Log.i(UITHREAD, "Menu successfully loaded!");
        /* On SUCCESS the MenuContent class should be given the data and 
         * the adapters notified. */
        mRequestedDate = mPendingDate;
        MenuContent.setMenuData(result.getValue());
        DishListFragment.refresh();
        mMenuPagerAdapter.notifyDataSetChanged();

    ...

    }
}

DishListFragment类中的refresh方法:

public static void refresh() {
    for (String key : mInstances.keySet()) {
        mInstances.get(key).mListAdapter.notifyDataSetChanged();
        Log.d("glic", "DishListFragment: " + key + " refreshed");
    }
    Log.d("glic", "DishListFragments refreshed");
}

问题

问题是,在冷启动时,除了第一页之外的所有内容都会更新。此外,从我的Logcat输出中,我收集到我的地图中只有一个DishListFragment:

10-18 22:11:41.624: I/##############(16802): Menu successfully loaded!
10-18 22:11:41.744: D/glic(16802): DishListFragment: breakfast refreshed 
10-18 22:11:41.744: D/glic(16802): DishListFragments refreshed
10-18 22:11:41.744: D/GLIC(16802): lunch

但是,我的视图寻呼机显示4个页面,第一个页面仍然包含虚拟数据。如果我旋转屏幕,则会更新第一页并显示正确的信息。此外,如果我在虚拟列表中选择一个项目,则会显示一个详细信息片段,其中包含我选择的项目位置的真实(来自服务器)信息 - 即使我只选择了一个虚拟项目。

我的第一个想法是我可能有两个单例碎片实例。一个包含带有初始虚拟数据的片段的映射,另一个包含从服务器返回的实际数据。我想这是可能的,因为我的单例不是线程安全的。但是,我不认为这会导致任何问题。我的DishListFragments不包含它们显示的数据,它们只观察它,所以不管我可能有多少个相同的 DishListFragment实例(同一个是同一页面),它们都应该观察相同的数据并且不应显示不同的数据 - 当解析并添加来自服务器的新数据时,将清除虚拟数据。

但是,我的mInstances映射的两个实例可以解释为什么视图没有更新。也许只有其中一组DishListFragments中的适配器被通知其数据集已更改。根据我的Logcat, breakfast ,这是我的第一页和填充虚拟数据的那个,确实被通知其数据集已经改变。有趣的是,其他人不是。


问题..

所以,至于我原来的问题:我是否正确实现了单个碎片分组?如果是这样,还有哪些其他因素可能导致我遇到的奇怪行为。提前致谢(=

0 个答案:

没有答案