Search code examples
javaandroidclasscastexceptiononresume

ClassCastException when resuming activity in android


I am trying to use this code

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);
}

}

in my app, doing navigation with tabs. I know, there are used a lot of deprecated methods, but I want to start from this. Everything is working great except when app is getting to background and need to be resumed (after longer time, i think it is cleared from rams). I am getting

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

when calling (73 line in code)

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

What is wrong? Why its works when activity is starting first time, but onResume it gives ANR?


Solution

  • Okey, I found solution by my self. As I find out, there is the problem with HashMap serialization in JAVA, so I saved HashMap as object to cache and when needed - opening it from cache. Everything works as expected.

    /**
     * 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<>();
    }