1

Why is onCreate method inside ResourceListFragment called twice on orientation change? After first call app crashes because getActivity() inside onPostExecute returns null, so I tried changing:

new HttpTask(this).execute("");

to

if(savedInstanceStata == null)
     new HttpTask(this).execute("");

Then on second call savedInstanceState is null, so mActivatedPosition is always ListView.INVALID_POSITION. I'm using layout aliases so activity_list get replaced by activity_twopane.

MainActivity

public class MainActivity extends Activity implements
    TabListener, OnNavigationListener {

/**
 * Whether or not the activity is in two-pane mode, i.e. running on a tablet
 * device.
 */
private boolean mTwoPane = false;
ResourceReservationApp app = (ResourceReservationApp)getApplication();

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_list);
    mTwoPane = getResources().getBoolean(R.bool.has_two_panes);

    if(mTwoPane){
     // Set up the action bar to show tabs.
    final ActionBar actionBar = getActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
    final String[] dropdownValues = getResources().getStringArray(R.array.dropdown);
    for (String item : dropdownValues) 
        actionBar.addTab(actionBar.newTab().setText(item).setTabListener(this));    

    }else{
        ActionBar actionBar = getActionBar();
         actionBar.setDisplayShowTitleEnabled(false);
            actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);

            final String[] dropdownValues = getResources().getStringArray(R.array.dropdown);

            ArrayAdapter<String> adapter = new ArrayAdapter<String>(actionBar.getThemedContext(),
                android.R.layout.simple_spinner_item, android.R.id.text1,
                dropdownValues);

            adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
            actionBar.setListNavigationCallbacks(adapter, this);                            
    }


}

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu items for use in the action bar
    MenuInflater inflater = getMenuInflater();
    inflater.inflate(R.menu.activity_item_list, menu);
    return super.onCreateOptionsMenu(menu);
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {

    if(tab.getText().equals("Resources")){
         ResourceListFragment fragment = new ResourceListFragment();            
                getFragmentManager().beginTransaction()
                .replace(R.id.fragment_list, fragment).commit();             
    }
    if(tab.getText().equals("Reservations")){
         ReservationListFragment fragment = new ReservationListFragment();                          
                getFragmentManager().beginTransaction()
                .replace(R.id.fragment_list, fragment).commit();                    
    }
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
    // TODO Auto-generated method stub

}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
    // TODO Auto-generated method stub

}
@Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
     final String[] dropdownValues = getResources().getStringArray(R.array.dropdown);
     if(dropdownValues[itemPosition].equals("Resources")){
         ResourceListFragment fragment = new ResourceListFragment();
            getFragmentManager().beginTransaction()
                    .replace(R.id.activity_list, fragment).commit();
     }
     if(dropdownValues[itemPosition].equals("Reservations")){
         ReservationListFragment fragment = new ReservationListFragment();                                  
            getFragmentManager().beginTransaction()
                    .replace(R.id.activity_list, fragment).commit();
     }
    return true;
}
    }

activity_list.xml

 <?xml version="1.0" encoding="utf-8"?>
 <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
     />

ResourceListFragment

public class ResourceListFragment extends ListFragment {
private boolean mTwoPane =false;
/**
 * The serialization (saved instance state) Bundle key representing the
 * activated item position. Only used on tablets.
 */
private static final String STATE_ACTIVATED_POSITION = "activated_position";
/**
 * The current activated item position. Only used on tablets.
 */
private int mActivatedPosition = ListView.INVALID_POSITION;

public ResourceListFragment() {
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);         
    mTwoPane = getResources().getBoolean(R.bool.has_two_panes);
    new HttpTask(this).execute("");
}

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    if(mTwoPane){
        getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
        if (savedInstanceState != null
                && savedInstanceState.containsKey(STATE_ACTIVATED_POSITION)) {
            setActivatedPosition(savedInstanceState
                    .getInt(STATE_ACTIVATED_POSITION));
        }
    }       
}


@Override
public void onListItemClick(ListView listView, View view, int position,
        long id) {
    super.onListItemClick(listView, view, position, id);

    // Notify the active callbacks interface (the activity, if the
    // fragment is attached to one) that an item has been selected.

    ((ResourceReservationApp)getActivity().getApplication()).setSelectedResource((Resource)(listView.getAdapter().getItem(position)));  
    if (mTwoPane) {
        // In two-pane mode, show the detail view in this activity by
        // adding or replacing the detail fragment using a
        // fragment transaction.

        setActivatedPosition(position);

        Bundle arguments = new Bundle();
        arguments.putString(ResourceDetailFragment.ARG_ITEM_ID, id+"");
        ResourceDetailFragment fragment = new ResourceDetailFragment();
        fragment.setArguments(arguments);
        getActivity().getFragmentManager().beginTransaction()
                .replace(R.id.detail_container, fragment).commit();

    } else {

        // In single-pane mode, simply start the detail activity
        // for the selected item ID.
        Intent detailIntent = new Intent(getActivity(), ResourceDetailActivity.class);
        detailIntent.putExtra(ResourceDetailFragment.ARG_ITEM_ID, id);          
        startActivity(detailIntent);
    }

}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    if(mTwoPane) {
        // Serialize and persist the activated item position.
        outState.putInt(STATE_ACTIVATED_POSITION, mActivatedPosition);
        outState.putParcelable("AS",(Parcelable) getActivity());
    }
}

/**
 * Turns on activate-on-click mode. When this mode is on, list items will be
 * given the 'activated' state when touched.
 */


private void setActivatedPosition(int position) {
    if(mTwoPane)
    if (position == ListView.INVALID_POSITION) {
        getListView().setItemChecked(mActivatedPosition, false);
    } else {
        getListView().setItemChecked(position, true);
    }       
    mActivatedPosition = position;
}
 private class HttpTask extends AsyncTask<String, Void, List<Resource>> {
        String message="Received";
        ResourceListFragment fragment;
        ProgressDialog dialog;


        public HttpTask(ResourceListFragment fragment){
            this.fragment=fragment;

        }


        @Override
        protected void onPreExecute() {
             dialog = ProgressDialog.show(getActivity(), "Loading...", "Please wait...", true);
        }



        @Override
        protected List<Resource> doInBackground(String... credentials) {
            List<Resource>resources=null;
            try {
                resources =ResourceDAO.getResourcesFromService("gataric","gataric");
                if(resources== null){
                    message  = "Error occured!";
                    return resources;
                }                   
            } catch (HttpException e) { 
                message = e.getMessage();

            }
            return resources;
        }      
        @Override
        protected void onPostExecute(List<Resource> result) {
            dialog.dismiss();               
            if(result!=null)
            fragment.setListAdapter(new ArrayAdapter<Resource>(getActivity(),
                    android.R.layout.simple_list_item_activated_1,
                    android.R.id.text1, result));
            fragment.setActivatedPosition(fragment.mActivatedPosition);
            //Toast.makeText(getActivity().getBaseContext(), message, Toast.LENGTH_LONG).show();
       }
    }
        }

activity_twopane.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:baselineAligned="false"
android:divider="?android:attr/dividerHorizontal"
android:orientation="horizontal"
android:showDividers="middle"
tools:context="com.example.resourcereservation.ResourceListActivity" >

<FrameLayout
    android:id="@+id/fragment_list"        
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="1"
    />

<FrameLayout
    android:id="@+id/detail_container"
    android:layout_width="0dp"
    android:layout_height="match_parent"
    android:layout_weight="3" />

Siddharth_Vyas
  • 9,972
  • 10
  • 39
  • 69
user1610362
  • 637
  • 2
  • 9
  • 26

2 Answers2

1

When your orientation changes activity is destroyed and re-created but before re-creation onSaveInstanceState() is called. So save your data in onSaveInstanceState() method. You can then restore the state during onCreate() or onRestoreInstanceState(). see this

Kaustubh
  • 653
  • 6
  • 21
  • 1
    You're describing the behavior right but you're missing his problem. He has an AsyncTask. Changing orientations destroys the activity but does not cancel any Threads or AsyncTasks running at that time because doing so could put the entire system in a corrupt state (for example a file could be half written). Code using multiple threads needs to either code very carefully so as not to use ANY class level variables (especially ui ones), or it needs to turn off that destroy on rotate behavior. Usually the second. Its only useful if you have separate landscape and portrait layouts anyway. – Gabe Sechan Jun 27 '14 at 09:19
0

Your original question:

Why is onCreate method inside ResourceListFragment called twice on orientation change?

Yes, I've seen this on occasions, and wondered why. See Ted Hopp's answer here (not the accepted one):

onCreate(...) is called twice after the device is rotated

Your problem:

Disabling orientation changes is not considered good practice:

How do I disable orientation change on Android?

As I'm sure you realise, you need something like this in inPostExecute

if (getActivity() != null){
    ...
}
Community
  • 1
  • 1
IanB
  • 3,489
  • 1
  • 20
  • 24