在 android 中恢复 activity 时出现 ClassCastException

ClassCastException when resuming activity in android

我正在尝试使用此代码

public class TabActivity extends SherlockFragmentActivity implements ActionBar.TabListener, OnItemSelectedListener
{
    enum TabType
    {
        SEARCH, LIST, FAVORITES
    }

// Tab back stacks
private HashMap<TabType, Stack<String>> backStacks;

@Override
public void onCreate(Bundle savedInstanceState)
{
    super.onCreate(savedInstanceState);

    // Initialize ActionBar
    ActionBar bar = getSupportActionBar();
    bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    // Set back stacks
    if (savedInstanceState != null)
    {
        // Read back stacks after orientation change
        backStacks = (HashMap<TabType, Stack<String>>) savedInstanceState.getSerializable("stacks");
    }
    else
    {
        // Initialize back stacks on first run
        backStacks = new HashMap<TabType, Stack<String>>();
        backStacks.put(TabType.SEARCH, new Stack<String>());
        backStacks.put(TabType.LIST, new Stack<String>());
        backStacks.put(TabType.FAVORITES, new Stack<String>());
    }

    // Create tabs
    bar.addTab(bar.newTab().setTag(TabType.SEARCH).setText("Search").setTabListener(this));
    bar.addTab(bar.newTab().setTag(TabType.LIST).setText("List").setTabListener(this));
    bar.addTab(bar.newTab().setTag(TabType.FAVORITES).setText("Favorites").setTabListener(this));
}

@Override
protected void onResume()
{
    super.onResume();
    // Select proper stack
    Tab tab = getSupportActionBar().getSelectedTab();
    Stack<String> backStack = backStacks.get(tab.getTag());
    if (! backStack.isEmpty())
    {
        // Restore topmost fragment (e.g. after application switch)
        String tag = backStack.peek();
        Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
        if (fragment.isDetached())
        {
            FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
            ft.attach(fragment);
            ft.commit();
        }
    }
}

@Override
protected void onPause()
{
    super.onPause();
    // Select proper stack
    Tab tab = getSupportActionBar().getSelectedTab();
    Stack<String> backStack = backStacks.get(tab.getTag());
    if (! backStack.isEmpty())
    {
        // Detach topmost fragment otherwise it will not be correctly displayed
        // after orientation change
        String tag = backStack.peek();
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
        ft.detach(fragment);
        ft.commit();
    }
}

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState)
{
    super.onRestoreInstanceState(savedInstanceState);
    // Restore selected tab
    int saved = savedInstanceState.getInt("tab", 0);
    if (saved != getSupportActionBar().getSelectedNavigationIndex())
        getSupportActionBar().setSelectedNavigationItem(saved);
}

@Override
protected void onSaveInstanceState(Bundle outState)
{
    super.onSaveInstanceState(outState);
    // Save selected tab and all back stacks
    outState.putInt("tab", getSupportActionBar().getSelectedNavigationIndex());
    outState.putSerializable("stacks", backStacks);
}

@Override
public void onBackPressed()
{
    // Select proper stack
    Tab tab = getSupportActionBar().getSelectedTab();
    Stack<String> backStack = backStacks.get(tab.getTag());
    String tag = backStack.pop();
    if (backStack.isEmpty())
    {
        // Let application finish
        super.onBackPressed();
    }
    else
    {
        FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
        Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
        // Animate return to previous fragment
        ft.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left);
        // Remove topmost fragment from back stack and forget it
        ft.remove(fragment);
        showFragment(backStack, ft);
        ft.commit();
    }
}

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft)
{
    // Select proper stack
    Stack<String> backStack = backStacks.get(tab.getTag());
    if (backStack.isEmpty())
    {
        // If it is empty instantiate and add initial tab fragment
        Fragment fragment;
        switch ((TabType) tab.getTag())
        {
            case SEARCH:
                fragment = Fragment.instantiate(this, SearchFragment.class.getName());
                break;
            case LIST:
                fragment = Fragment.instantiate(this, ListFragment.class.getName());
                break;
            case FAVORITES:
                fragment = Fragment.instantiate(this, FavoritesFragment.class.getName());
                break;
            default:
                throw new java.lang.IllegalArgumentException("Unknown tab");
        }
        addFragment(fragment, backStack, ft);
    }
    else
    {
        // Show topmost fragment
        showFragment(backStack, ft);
    }
}

@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft)
{
    // Select proper stack
    Stack<String> backStack = backStacks.get(tab.getTag());
    // Get topmost fragment
    String tag = backStack.peek();
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
    // Detach it
    ft.detach(fragment);
}

@Override
public void onTabReselected(Tab tab, FragmentTransaction ft)
{
    // Select proper stack
    Stack<String> backStack = backStacks.get(tab.getTag());

    if (backStack.size() > 1)
        ft.setCustomAnimations(R.anim.slide_from_right, R.anim.slide_to_left);
    // Clean the stack leaving only initial fragment
    while (backStack.size() > 1)
    {
        // Pop topmost fragment
        String tag = backStack.pop();
        Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
        // Remove it
        ft.remove(fragment);
    }
    showFragment(backStack, ft);
}

private void addFragment(Fragment fragment)
{
    // Select proper stack
    Tab tab = getSupportActionBar().getSelectedTab();
    Stack<String> backStack = backStacks.get(tab.getTag());

    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    // Animate transfer to new fragment
    ft.setCustomAnimations(R.anim.slide_from_left, R.anim.slide_to_right);
    // Get topmost fragment
    String tag = backStack.peek();
    Fragment top = getSupportFragmentManager().findFragmentByTag(tag);
    ft.detach(top);
    // Add new fragment
    addFragment(fragment, backStack, ft);
    ft.commit();
}

private void addFragment(Fragment fragment, Stack<String> backStack, FragmentTransaction ft)
{
    // Add fragment to back stack with unique tag
    String tag = UUID.randomUUID().toString();
    ft.add(android.R.id.content, fragment, tag);
    backStack.push(tag);
}

private void showFragment(Stack<String> backStack, FragmentTransaction ft)
{
    // Peek topmost fragment from the stack
    String tag = backStack.peek();
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
    // and attach it
    ft.attach(fragment);
}

// The following code shows how to properly open new fragment. It assumes
// that parent fragment calls its activity via interface. This approach
// is described in Android development guidelines.
@Override
public void onItemSelected(String item)
{
    ItemFragment fragment = new ItemFragment();
    Bundle args = new Bundle();
    args.putString("item", item);
    fragment.setArguments(args);
    addFragment(fragment);
}

}

在我的应用程序中,使用选项卡进行导航。我知道,有很多不赞成使用的方法,但我想从这里开始。一切都很好,除非应用程序进入后台并需要恢复(经过更长的时间,我认为它已从 rams 中清除)。我得到

java.lang.ClassCastException: java.util.ArrayList cannot be cast to java.util.Stack

调用时(代码中的 73 行)

Stack backStack = backStacks.get(tab.getTag());

怎么了?为什么它在 activity 第一次启动时有效,但 onResume 却给出 ANR?

我会说这是因为 backStacks.get(tab.getTag()); returns List 而不是 Stack。试试这个:

List backStack = backStacks.get(tab.getTag());

您可能也不应该使用原始类型。

好的,我自己找到了解决方案。正如我发现的那样,JAVA 中的 HashMap 序列化存在问题,因此我将 HashMap 作为对象保存到缓存中,并在需要时从缓存中打开它。一切都按预期工作。

/**
 * In case that there is kinda bug in JAVA serializing HASHMAP, backstacks hashmap is writing to cache as object.
 */
private void serializeBackStack() {
    try {
        FileOutputStream fos =
                new FileOutputStream(getCacheDir() + "backstack.ser");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(backStacks);
        oos.close();
        fos.close();
    } catch (IOException ioe) {
        ioe.printStackTrace();
    }
}


/**
 * Hashmap with backstacks is getting from object saved in cahse.
 *
 * @return HashMap
 */
private HashMap<TabType, Stack<String>> deserializeBackStack() {
    HashMap<TabType, Stack<String>> map = new HashMap<>();
    try {
        FileInputStream fis = new FileInputStream(getCacheDir() + "backstack.ser");
        ObjectInputStream ois = new ObjectInputStream(fis);
        map = (HashMap<TabType, Stack<String>>) ois.readObject();
        ois.close();
        fis.close();
    } catch (IOException ioe) {
        ioe.printStackTrace();
        return new HashMap<>();
    } catch (ClassNotFoundException c) {
        c.printStackTrace();
        return new HashMap<>();
    }

    if (map != null)
        return map;
    else
        return new HashMap<>();
}