I ran into this issue and came up with numerous posts both here and elsewhere related to this issue.
This application targets android 16, but min is set to 8. So I am using the Android Support Library to have access to newer apis.
I have an activity that loads a fragment which is a ListFragment. If you are running on a tablet in landscape mode, the first fragment, when list item is clicked, will load the second fragment in a 2 pane layout. If on a phone, a new activity loads the second fragment.
The second fragment uses a CursorLoader to access a ContentProvider as its backing data store. When this fragment is called from its own activity, there is no issue. However, when it is in 2 pane layout, the CursorLoader would fail with the above exception.
Here is the code:
public class ProgramGroupFragment extends MythtvListFragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String TAG = ProgramGroupFragment.class.getSimpleName();
private ProgramCursorAdapter adapter;
private String programGroup = "*";
public ProgramGroupFragment() { }
@Override
public Loader<Cursor> onCreateLoader( int id, Bundle args ) {
String[] projection = { BaseColumns._ID, ProgramConstants.FIELD_TITLE, ProgramConstants.FIELD_SUB_TITLE };
String[] selectionArgs = { programGroup };
CursorLoader cursorLoader = new CursorLoader( getActivity(), ProgramConstants.CONTENT_URI, projection, ProgramConstants.FIELD_TITLE + "=?", selectionArgs, ProgramConstants.FIELD_SUB_TITLE );
return cursorLoader;
}
@Override
public void onLoadFinished( Loader<Cursor> loader, Cursor cursor ) {
Log.v( TAG, "onLoadFinished : enter" );
adapter.swapCursor( cursor );
}
@Override
public void onLoaderReset( Loader<Cursor> loader ) {
adapter.swapCursor( null );
}
@Override
public void onActivityCreated( Bundle savedInstanceState ) {
super.onActivityCreated( savedInstanceState );
adapter = new ProgramCursorAdapter(
getActivity().getApplicationContext(), R.layout.program_row,
null, new String[] { ProgramConstants.FIELD_SUB_TITLE }, new int[] { R.id.program_sub_title },
CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER );
setListAdapter( adapter );
getLoaderManager().initLoader( 0, null, this );
}
public void loadPrograms( String name ) {
programGroup = name;
try {
getLoaderManager().restartLoader( 0, null, this );
} catch( Exception e ) {
Log.w( TAG, e.getLocalizedMessage(), e );
}
}
@Override
public void onListItemClick( ListView l, View v, int position, long id ) {
super.onListItemClick( l, v, position, id );
Intent i = new Intent( getActivity(), VideoActivity.class );
i.putExtra( VideoActivity.EXTRA_PROGRAM_KEY, id );
startActivity( i );
}
private class ProgramCursorAdapter extends SimpleCursorAdapter {
public ProgramCursorAdapter( Context context, int layout, Cursor c, String[] from, int[] to, int flags ) {
super( context, layout, c, from, to, flags );
}
@Override
public View getView( int position, View convertView, ViewGroup parent ) {
View row = super.getView( position, convertView, parent );
getCursor().moveToPosition( position );
try {
int idIndex = getCursor().getColumnIndexOrThrow( BaseColumns._ID );
int titleIndex = getCursor().getColumnIndexOrThrow( ProgramConstants.FIELD_TITLE );
int subTitleIndex = getCursor().getColumnIndexOrThrow( ProgramConstants.FIELD_SUB_TITLE );
int id = getCursor().getInt( idIndex );
String title = getCursor().getString( titleIndex );
String subTitle = getCursor().getString( subTitleIndex );
if( row == null ) {
LayoutInflater inflater = getActivity().getLayoutInflater();
row = inflater.inflate( R.layout.program_row, parent, false );
}
TextView t = (TextView) row.findViewById( R.id.program_sub_title );
t.setText( !"".equals( subTitle ) ? subTitle : title );
} catch( Exception e ) {
Log.e( TAG, "getView : error", e );
}
return row;
}
}
}
The problem occurred in the method loadPrograms, specifically the line:
getLoaderManager().restartLoader( 0, null, this );
This was throwing the IllegalStateException. After looking through the documentation and numerous examples, I never once saw this issue and having to be caught in order to proceed.
I wrapped the code in a try/catch block just to see what would happen, and to my surprise, the app worked as expected.
EDIT: Here is the Activity code that establishes the 2 pane layout:
public class RecordingsActivity extends AbstractRecordingsActivity implements RecordingsFragment.OnProgramGroupListener {
private static final String TAG = RecordingsActivity.class.getSimpleName();
private static final int REFRESH_ID = Menu.FIRST + 2;
private long requestId;
private BroadcastReceiver requestReceiver;
private DvrServiceHelper mDvrServiceHelper;
@Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
setContentView( R.layout.activity_dvr_recordings );
RecordingsFragment recordingsFragment = (RecordingsFragment) getSupportFragmentManager().findFragmentById( R.id.fragment_dvr_program_groups );
recordingsFragment.setOnProgramGroupListener( this );
}
@TargetApi( 11 )
@Override
public boolean onCreateOptionsMenu( Menu menu ) {
Log.v( TAG, "onCreateOptionsMenu : enter" );
MenuItem refresh = menu.add( Menu.NONE, REFRESH_ID, Menu.NONE, "Refresh" );
if( Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ) {
refresh.setShowAsAction( MenuItem.SHOW_AS_ACTION_IF_ROOM );
}
return true;
}
/* (non-Javadoc)
* @see org.mythtv.client.ui.dvr.AbstractRecordingsActivity#onOptionsItemSelected(android.view.MenuItem)
*/
@Override
public boolean onOptionsItemSelected( MenuItem item ) {
switch( item.getItemId() ) {
case REFRESH_ID:
requestId = mDvrServiceHelper.getRecordingedList();
return true;
}
return super.onOptionsItemSelected( item );
}
@Override
protected void onResume() {
super.onResume();
IntentFilter filter = new IntentFilter( DvrServiceHelper.ACTION_REQUEST_RESULT );
requestReceiver = new BroadcastReceiver() {
@Override
public void onReceive( Context context, Intent intent ) {
long resultRequestId = intent.getLongExtra( DvrServiceHelper.EXTRA_REQUEST_ID, 0 );
if( resultRequestId == requestId ) {
int resultCode = intent.getIntExtra( DvrServiceHelper.EXTRA_RESULT_CODE, 0 );
if( resultCode == 200 ) {
Log.d( TAG, "Updating UI with new data" );
} else {
Log.d( TAG, "error occurred" );
}
} else {
Log.d( TAG, "Result is NOT for our request ID" );
}
}
};
mDvrServiceHelper = DvrServiceHelper.getInstance( this );
registerReceiver( requestReceiver, filter );
}
/* (non-Javadoc)
* @see android.support.v4.app.FragmentActivity#onPause()
*/
@Override
public void onPause() {
super.onPause();
// Unregister for broadcast
if( null != requestReceiver ) {
try {
unregisterReceiver( requestReceiver );
requestReceiver = null;
} catch( IllegalArgumentException e ) {
Log.e( TAG, e.getLocalizedMessage(), e );
}
}
}
public void onProgramGroupSelected( String programGroup ) {
Log.d( TAG, "onProgramGroupSelected : enter" );
if( null != findViewById( R.id.fragment_dvr_program_group ) ) {
FragmentManager manager = getSupportFragmentManager();
ProgramGroupFragment programGroupFragment = (ProgramGroupFragment) manager.findFragmentById( R.id.fragment_dvr_program_group );
FragmentTransaction transaction = manager.beginTransaction();
if( null == programGroupFragment ) {
Log.v( TAG, "onProgramGroupSelected : creating new programGroupFragment" );
programGroupFragment = new ProgramGroupFragment();
transaction
.add( R.id.fragment_dvr_program_group, programGroupFragment )
.setTransition( FragmentTransaction.TRANSIT_FRAGMENT_OPEN )
.addToBackStack( null )
.commit();
}
programGroupFragment.loadPrograms( programGroup );
} else {
Intent i = new Intent( this, ProgramGroupActivity.class );
i.putExtra( ProgramGroupActivity.EXTRA_PROGRAM_GROUP_KEY, programGroup );
startActivity( i );
}
}
}
The Listener method onProgramGroupSelected is where the single vs 2 pane layout is determined. The listener is called when the list item in the left pain is clicked.
Can anyone explain why this exception is occurring?
The full code for the project is located at https://github.com/MythTV-Android/mythtv-for-android
I experienced something similar. For me it seemed to help to avoid using the ApplicationContext
when using a context. Try create your ProgramCursorAdapter
without using the getApplicationContext()
.