1

I'm working on an Android application built with ActionBarSherlock and ViewPagerIndicator with a minSDK of 8 (Android 2.2) and targetSDK of 16 (Android 4.1) making use of the Android Compatibility Library. I have a messaging portion of the app where messages are stored in a SQLite database and I'm using CommonsWare's cwac-loaderex to use a cursor loader with the SQLite database. When the activity loads, everything works great and the messages are displayed, but when I rotate the device it just displays a ListFragment with the loading cicle.

Turning on LoaderManager.enableDebugLogging(true) existing loaders are being reused when I rotate, I also tried getSupportLoaderManager().destroyLoader(ID) in onDestroy and new loaders are being created but I still have the same result, a ListFragment with the loading circle. I have tested in both orientations before opening the messaging activity and they both work fine, it's just an issue when I rotate when the messaging activity is visible. I have also tried going in to the messaging activity in portrait, clicking on a message which takes me to another activity without destroying the messaging activity, changing to landscape and then pressing back to the messaging activity and everything displays fine.

I haven't been able to figure out why messages are not being displayed after a rotate. I'd be very greatful for any ideas or help. I have included my messaging activity below.

import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.ListFragment;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.Loader;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.SimpleCursorAdapter;

import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;
import com.commonsware.cwac.loaderex.acl.SQLiteCursorLoader;
import com.viewpagerindicator.TitlePageIndicator;

import com.lukekorth.DB.DatabaseHelper;
import com.lukekorth.Helpers.MessageFragment;

public class MyMessagesActivity extends SherlockFragmentActivity implements
LoaderManager.LoaderCallbacks<Cursor> {

        private FragmentAdapter mFragmentAdapter;
    private ViewPager mPager;
    private DatabaseHelper mDB;
    private SimpleCursorAdapter mInboxCursorAdapter;
    private SimpleCursorAdapter mArchiveCursorAdapter;

    private MessageFragment[] content = new MessageFragment[2];

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.messages);

        getSupportActionBar().setHomeButtonEnabled(true);
        getSherlock().getActionBar().setDisplayHomeAsUpEnabled(true);

        mDB = new DatabaseHelper(this);

        content[0] = new MessageFragment();
        content[1] = new MessageFragment();

        /////////--------------------///////
        LoaderManager.enableDebugLogging(true);

        mInboxCursorAdapter = new SimpleCursorAdapter(this, R.layout.message_item, null, new String[] { DatabaseHelper.senderName,
                DatabaseHelper.lastUpdate, DatabaseHelper.subject, DatabaseHelper.lastSnippet }, new int[] { R.id.name, R.id.date,
                R.id.subject, R.id.snippet });

        mArchiveCursorAdapter = new SimpleCursorAdapter(this, R.layout.message_item, null, new String[] { DatabaseHelper.senderName,
                DatabaseHelper.lastUpdate, DatabaseHelper.subject, DatabaseHelper.lastSnippet }, new int[] { R.id.name, R.id.date,
                R.id.subject, R.id.snippet });

        content[0].setListAdapter(mInboxCursorAdapter);
        content[1].setListAdapter(mArchiveCursorAdapter);

        getSupportLoaderManager().initLoader(0, null, this);
        getSupportLoaderManager().initLoader(1, null, this);
        ///////////////------------------////////////

        mFragmentAdapter = new FragmentAdapter(getSupportFragmentManager());
        mFragmentAdapter.updateContent(content);

        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mFragmentAdapter);

        TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.indicator);
        indicator.setViewPager(mPager);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();

        getSupportLoaderManager().destroyLoader(0);
        getSupportLoaderManager().destroyLoader(1);

        mDB.close();
    }

    @Override
    public Loader<Cursor> onCreateLoader(int folder, Bundle arg1) {
        String query = "select " + DatabaseHelper.threads + "." + DatabaseHelper.threadKey + "," + DatabaseHelper.subject +
                "," + DatabaseHelper.archive + "," + DatabaseHelper.otherUser + "," + DatabaseHelper.lastUpdate + "," +
                DatabaseHelper.lastSnippet + "," + DatabaseHelper.senderPicture + "," + DatabaseHelper.senderName + " from " +
                DatabaseHelper.threads + " left join " + DatabaseHelper.senders + " on " + DatabaseHelper.threads + "." +
                DatabaseHelper.otherUser + " = " + DatabaseHelper.senders + "." + DatabaseHelper.senderKey + " where " +
                DatabaseHelper.archive;

        if(folder == 0)
            query += " = 0 order by ";
        else
            query += " = 1 order by ";

        query += DatabaseHelper.lastUpdate + " desc";

        return new SQLiteCursorLoader(this, mDB, query, null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        if(loader.getId() == 0)
            mInboxCursorAdapter.swapCursor(cursor);
        else
            mArchiveCursorAdapter.swapCursor(cursor);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        if(loader.getId() == 0)
            mInboxCursorAdapter.swapCursor(null);
        else
            mArchiveCursorAdapter.swapCursor(null);
    }
}

class FragmentAdapter extends FragmentPagerAdapter {
    protected static final String[] TITLE = new String[] { "Inbox", "Archive", };

    private ListFragment[] mContent;

    private int mCount = TITLE.length;

    public FragmentAdapter(FragmentManager fm) {
        super(fm);
    }

    public void updateContent(MessageFragment[] content){
        mContent = content;
    }

    @Override
    public Fragment getItem(int position) {
        return mContent[position % mContent.length];
    }

    @Override
    public int getCount() {
        return mCount;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return FragmentAdapter.TITLE[position % TITLE.length];
    }
}
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
Luke
  • 1,069
  • 18
  • 28
  • If you can create a complete project that demonstrates the issue, upload the source somewhere and I will take a look at it. – CommonsWare Jul 25 '12 at 17:00
  • Ok, will do after lunch. Thanks a bunch – Luke Jul 25 '12 at 17:06
  • I've uploaded it here: https://github.com/lkorth/CursorLoaderDemo Thanks again – Luke Jul 25 '12 at 18:10
  • I will try to look at this tomorrow and will see if it is a `SQLiteCursorLoader` bug/limitation. – CommonsWare Jul 25 '12 at 18:18
  • 1
    Well, I can tell you that it does not appear to be a problem with `SQLiteCursorLoader`, which was my particular area of concern. The `Cursor` objects seem fine after the configuration change in `onLoadFinished()`. My guess is that your problem lies with the fragments -- you are trying to recreate the fragments, but Android will have already recreated them for you after the configuration change, and so it may be that the old fragments (with new empty `ListViews`) are what you are seeing in the `ViewPager`. – CommonsWare Jul 25 '12 at 23:04

1 Answers1

1

It turns out there is some strangeness with ViewPager and fragment tags. My solution was to add the fragments in a transaction in onSaveInstanceState() and then in onCreate() (second piece of code) to get the fragments by tag using the tags generated in the ViewPager. I was able to figure out the tag scheme used from this question. Hope this can save someone a few days work.

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    FragmentManager fm = getSupportFragmentManager();
    FragmentTransaction ft = fm.beginTransaction();

    ft.add(content[0], content[0].getTag());
    ft.add(content[1], content[1].getTag());

    ft.commit();
}




    FragmentManager fm = getSupportFragmentManager();

    content[0] = (MessageFragment) fm.findFragmentByTag("android:switcher:"+ R.id.pager + ":0");
    content[1] = (MessageFragment) fm.findFragmentByTag("android:switcher:"+ R.id.pager + ":1");

    if(content[0] == null || content[1] == null){
        content[0] = new MessageFragment();
        content[1] = new MessageFragment();

        content[0].setListAdapter(mInboxCursorAdapter);
        content[1].setListAdapter(mArchiveCursorAdapter);
    }
Community
  • 1
  • 1
Luke
  • 1,069
  • 18
  • 28