I am using a cursor loader to create the tabs in an activity. I am using swipable tabs as detailed on the android View Pager documentation.
http://developer.android.com/reference/android/support/v4/view/ViewPager.html
So far I haven’t implemented putting any of my pages that are loaded onto the back stack, so when I click back it jumps to the previous activity. (This is fine for now.) Clicking either the back or up button results in a Run time Exception java.lang.RuntimeException: Unable to destroy activity: java.lang.IllegalStateException: Activity has been destroyed
The only differences I know of between what I have and the example is the use of the cursor, and that the example sets the ViewPager itself as the content view, whereas I’ve used a layout file that contains a ViewPager. I’m also using a FragmentStatePagerAdapter rather than FragmentPagerAdapter, but switching between these two made no difference.
I’m not sure what in the android onDestroy call would try to destroy the activity twice. My code is below. Any help is appreciated, and I’d be glad to provide more info. Thanks.
public class WorksheetActivity extends FragmentActivity implements android.support.v4.app.LoaderManager.LoaderCallbacks<Cursor> {
public String user;
private static final int DAY_LOADER = 0;
public int program_index;
public int program_id;
public int item_id;
public int type_key;
ViewPager mViewPager;
TabsAdapter mTabsAdapter;
LoaderManager.LoaderCallbacks<Cursor> mCallbacks;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_worksheet);
mCallbacks = this;
ActionBar bar = getActionBar();
//this sets up to include tabs, and to show the title on each tab
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
//enables navigation up, when clicked calls on options item selected for home button.
bar.setDisplayHomeAsUpEnabled(true);
Bundle extras = getIntent().getExtras();
if (!(extras.isEmpty())){
user = extras.getString("user");
//used for moving up to the Program Activity and restoring the selection.
program_index = extras.getInt(ProgramActivity.PROGRAM_KEY);
program_id = extras.getInt(ProgramActivity.PROGRAM_ID);
item_id = extras.getInt(ProgramActivity.ITEM_ID);
type_key = extras.getInt(ProgramActivity.TYPE_KEY);
}
Bundle tabData = new Bundle();
tabData.putAll((extras.isEmpty()? savedInstanceState : extras));
mViewPager = (ViewPager) findViewById(R.id.pager);
mTabsAdapter = new TabsAdapter(this, mViewPager, null, tabData);
LoaderManager lm = getSupportLoaderManager();
lm.initLoader(0, tabData, mCallbacks);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_worksheet, menu);
return true;
}
@Override
public void onSaveInstanceState(Bundle out){
out.putString("user",user);
//used for moving up to the Program Activity and restoring the selection.
out.putInt(ProgramActivity.PROGRAM_KEY, program_index);
out.putInt(ProgramActivity.ITEM_ID, item_id);
out.putInt(ProgramActivity.PROGRAM_ID, program_id);
out.putInt(ProgramActivity.TYPE_KEY, type_key);
}
@Override
public void onRestoreInstanceState(Bundle in){
user = in.getString("user");
program_index = in.getInt(ProgramActivity.PROGRAM_KEY);
program_id = in.getInt(ProgramActivity.PROGRAM_ID);
item_id = in.getInt(ProgramActivity.ITEM_ID);
type_key = in.getInt(ProgramActivity.TYPE_KEY);
}
@SuppressLint("NewApi")
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent upIntent = new Intent(this, ProgramActivity.class);
upIntent.putExtra("user", user);
upIntent.putExtra(ProgramActivity.PROGRAM_KEY, program_index);
if (NavUtils.shouldUpRecreateTask(this, upIntent)){
TaskStackBuilder.create(this)
.addNextIntent(new Intent(this, Marta_QC.class))
.addNextIntent(upIntent).startActivities();
finish();
}else{
NavUtils.navigateUpTo(this, upIntent);
}
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle arg1) {
CursorLoader cl = null;
switch(id){
case DAY_LOADER:
cl = new CursorLoader(getApplicationContext(), Tasks.CONTENT_URI, new String[]{Tasks.DAY_COL},Tasks.PROGRAM_COL+" = ?",
new String[]{Integer.toString(program_id)}, Tasks.DAY_COL+" ASC");
break;
default:
}
return cl;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
switch(loader.getId()){
case DAY_LOADER:
mTabsAdapter.swapCursor(c);
break;
default:
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
switch(loader.getId()){
case DAY_LOADER:
mTabsAdapter.swapCursor(null);
break;
default:
}
}
public static class TabsAdapter extends FragmentStatePagerAdapter
implements ActionBar.TabListener, ViewPager.OnPageChangeListener {
private final Context mContext;
private final ActionBar mActionBar;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
private Bundle mArgs;
private int currentPosition;
static final class TabInfo{
private final Class<?> clss;
private final Bundle args;
public final int position;
TabInfo(Class<?> _class, Bundle _args, int _position){
clss = _class;
args = _args;
position = _position;
}
}
public TabsAdapter (FragmentActivity activity, ViewPager pager, Cursor days, Bundle args){
super(activity.getSupportFragmentManager());
mContext = activity;
mActionBar = activity.getActionBar();
mViewPager = pager;
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
mArgs = args;
}
public void swapCursor(Cursor c){
mActionBar.removeAllTabs();
mViewPager.removeAllViews();
//add the exceptions tab. Always first.
ActionBar.Tab tab = mActionBar.newTab();
Bundle tabArgs = new Bundle();
tabArgs.putAll(mArgs);
this.addTab(tab, ExceptionsSheet.class, tabArgs);
//add each of the day tabs to the set, which stores unique values only
LinkedHashSet<String> days = new LinkedHashSet<String>();
if(c.moveToFirst()){
do{
days.add(c.getString(c.getColumnIndex(Tasks.DAY_COL)));
}while (c.moveToNext());
}
//iterate through the unique days
for(String day:days){
ActionBar.Tab dayTab = mActionBar.newTab();
Bundle dayArgs = new Bundle();
dayArgs.putAll(mArgs);
dayArgs.putString("day", day);
this.addTab(dayTab, Worksheet.class, dayArgs);
}
//start with the first day
mActionBar.setSelectedNavigationItem(1);
}
public void addTab(ActionBar.Tab tab, Class<?> clss, Bundle args){
//implemented adding a position to the tab info class.
//gets the current tab count prior to adding this tab.
TabInfo info = new TabInfo(clss, args, this.getCount());
tab.setTag(info);
tab.setTabListener(this);
mTabs.add(info);
if (clss.equals(ExceptionsSheet.class)){
//tab.setCustomView(R.layout.exceptions_tab);
tab.setText(mContext.getText(R.string.exceptions_tab_title));
}else{
if(clss.equals(Worksheet.class)){
tab.setText("Day "+args.getString("day"));
}else{
Log.d("unknown class", clss.toString());
}
}
mActionBar.addTab(tab);
notifyDataSetChanged();
}
@Override
public void onPageSelected(int pos) {
// set the tab to match the page, following a swipe action.
currentPosition = pos;
mActionBar.setSelectedNavigationItem(pos);
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
//this will return our tab info
TabInfo minfo = (TabInfo) tab.getTag();
int pos = minfo.position;
this.getItem(currentPosition);
mViewPager.setCurrentItem(pos);
currentPosition = pos;
}
//this is the FragmentPager Adapter main method
//here we will use the arguments bundled into the tab to set the
//fragment for the page.
@Override
public Fragment getItem(int pos) {
TabInfo tabInfo = mTabs.get(pos);
return Fragment.instantiate(mContext, tabInfo.clss.getName(), tabInfo.args);
}
}
//class for exceptions display
public static class ExceptionsSheet extends Fragment{
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
int i = 1;
i = i+1;
//must include false, since we're attaching to the ViewPager sheet, not the root view.
return inflater.inflate(R.layout.exception, container, false);
}
}
//class for standard worksheet display
public static class Worksheet extends Fragment{
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){
return inflater.inflate(R.layout.worksheet, container, false);
}
}
}
I found the problem. When the Activity is destroyed, the onLoaderReset() method is called. Here I swapped in a null cursor to remove references to the cursor (as with non-custom adapters), but my swapCursor method did not check to see if the cursor was null. Once I fixed this the code no longer tries to update the UI after the activity has been destroyed.