Activity restart on rotation Android

前端 未结 30 3836
花落未央
花落未央 2020-11-21 04:32

In my Android application, when I rotate the device (slide out the keyboard) then my Activity is restarted (onCreate is called). Now, this is proba

相关标签:
30条回答
  • 2020-11-21 05:13

    Using the Application Class

    Depending on what you're doing in your initialization you could consider creating a new class that extends Application and moving your initialization code into an overridden onCreate method within that class.

    public class MyApplicationClass extends Application {
      @Override
      public void onCreate() {
        super.onCreate();
        // TODO Put your application initialization code here.
      }
    }
    

    The onCreate in the application class is only called when the entire application is created, so the Activity restarts on orientation or keyboard visibility changes won't trigger it.

    It's good practice to expose the instance of this class as a singleton and exposing the application variables you're initializing using getters and setters.

    NOTE: You'll need to specify the name of your new Application class in the manifest for it to be registered and used:

    <application
        android:name="com.you.yourapp.MyApplicationClass"
    

    Reacting to Configuration Changes [UPDATE: this is deprecated since API 13; see the recommended alternative]

    As a further alternative, you can have your application listen for events that would cause a restart – like orientation and keyboard visibility changes – and handle them within your Activity.

    Start by adding the android:configChanges node to your Activity's manifest node

     <activity android:name=".MyActivity"
          android:configChanges="orientation|keyboardHidden"
          android:label="@string/app_name">
    

    or for Android 3.2 (API level 13) and newer:

    <activity android:name=".MyActivity"
          android:configChanges="keyboardHidden|orientation|screenSize"
          android:label="@string/app_name">
    

    Then within the Activity override the onConfigurationChanged method and call setContentView to force the GUI layout to be re-done in the new orientation.

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
      super.onConfigurationChanged(newConfig);
      setContentView(R.layout.myLayout);
    }
    
    0 讨论(0)
  • 2020-11-21 05:15

    The approach is useful but is incomplete when using Fragments.

    Fragments usually get recreated on configuration change. If you don't wish this to happen, use

    setRetainInstance(true); in the Fragment's constructor(s)

    This will cause fragments to be retained during configuration change.

    http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)

    0 讨论(0)
  • 2020-11-21 05:15

    You may use ViewModel object in your activity.

    ViewModel objects are automatically retained during configuration changes so that data they hold is immediately available to the next activity or fragment instance. Read more:

    https://developer.android.com/topic/libraries/architecture/viewmodel

    0 讨论(0)
  • 2020-11-21 05:16

    Add this line in manifest : android:configChanges="orientation|screenSize"

    0 讨论(0)
  • 2020-11-21 05:17

    Update for Android 3.2 and higher:

    Caution: Beginning with Android 3.2 (API level 13), the "screen size" also changes when the device switches between portrait and landscape orientation. Thus, if you want to prevent runtime restarts due to orientation change when developing for API level 13 or higher (as declared by the minSdkVersion and targetSdkVersion attributes), you must include the "screenSize" value in addition to the "orientation" value. That is, you must declare android:configChanges="orientation|screenSize". However, if your application targets API level 12 or lower, then your activity always handles this configuration change itself (this configuration change does not restart your activity, even when running on an Android 3.2 or higher device).

    0 讨论(0)
  • 2020-11-21 05:18

    After a while of trial and error, I found a solution which fits my needs in the most situations. Here is the Code:

    Manifest configuration:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
              package="com.pepperonas.myapplication">
    
        <application
            android:name=".App"
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity
                android:name=".MainActivity"
                android:configChanges="orientation|keyboardHidden|screenSize">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN"/>
    
                    <category android:name="android.intent.category.LAUNCHER"/>
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    

    MainActivity:

    import android.content.res.Configuration;
    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.support.v4.app.FragmentManager;
    import android.support.v4.app.FragmentTransaction;
    import android.support.v7.app.AppCompatActivity;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        private static final String TAG = "MainActivity";
    
        private Fragment mFragment;
    
        private int mSelected = -1;
    
    
        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            Log.d(TAG, "onCreate  " + "");
    
            // null check not realy needed - but just in case...
            if (savedInstanceState == null) {
    
                initUi();
    
                // get an instance of FragmentTransaction from your Activity
                FragmentManager fragmentManager = getSupportFragmentManager();
                FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
    
                /*IMPORTANT: Do the INITIAL(!) transaction only once!
                * If we call this everytime the layout changes orientation,
                * we will end with a messy, half-working UI.
                * */
                mFragment = FragmentOne.newInstance(mSelected = 0);
                fragmentTransaction.add(R.id.frame, mFragment);
                fragmentTransaction.commit();
            }
        }
    
    
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            super.onConfigurationChanged(newConfig);
            Log.d(TAG, "onConfigurationChanged  " +
                       (newConfig.orientation
                        == Configuration.ORIENTATION_LANDSCAPE
                        ? "landscape" : "portrait"));
    
            initUi();
    
            Log.i(TAG, "onConfigurationChanged - last selected: " + mSelected);
            makeFragmentTransaction(mSelected);
        }
    
    
        /**
         * Called from {@link #onCreate} and {@link #onConfigurationChanged}
         */
        private void initUi() {
            setContentView(R.layout.activity_main);
            Log.d(TAG, "onCreate  instanceState == null / reinitializing..." + "");
            Button btnFragmentOne = (Button) findViewById(R.id.btn_fragment_one);
            Button btnFragmentTwo = (Button) findViewById(R.id.btn_fragment_two);
            btnFragmentOne.setOnClickListener(this);
            btnFragmentTwo.setOnClickListener(this);
        }
    
    
        /**
         * Not invoked (just for testing)...
         */
        @Override
        protected void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            Log.d(TAG, "onSaveInstanceState  " + "YOU WON'T SEE ME!!!");
        }
    
    
        /**
         * Not invoked (just for testing)...
         */
        @Override
        protected void onRestoreInstanceState(Bundle savedInstanceState) {
            super.onRestoreInstanceState(savedInstanceState);
            Log.d(TAG, "onSaveInstanceState  " + "YOU WON'T SEE ME, AS WELL!!!");
        }
    
    
        @Override
        protected void onResume() {
            super.onResume();
            Log.d(TAG, "onResume  " + "");
        }
    
    
        @Override
        protected void onPause() {
            super.onPause();
            Log.d(TAG, "onPause  " + "");
        }
    
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            Log.d(TAG, "onDestroy  " + "");
        }
    
    
        @Override
        public void onClick(View v) {
    
            switch (v.getId()) {
                case R.id.btn_fragment_one:
                    Log.d(TAG, "onClick btn_fragment_one " + "");
                    makeFragmentTransaction(0);
                    break;
    
                case R.id.btn_fragment_two:
                    Log.d(TAG, "onClick btn_fragment_two " + "");
                    makeFragmentTransaction(1);
                    break;
    
                default:
                    Log.d(TAG, "onClick  null - wtf?!" + "");
            }
        }
    
    
        /**
         * We replace the current Fragment with the selected one.
         * Note: It's called from {@link #onConfigurationChanged} as well.
         */
        private void makeFragmentTransaction(int selection) {
    
            switch (selection) {
                case 0:
                    mFragment = FragmentOne.newInstance(mSelected = 0);
                    break;
                case 1:
                    mFragment = FragmentTwo.newInstance(mSelected = 1);
                    break;
            }
    
            // Create new transaction
            FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    
            // Replace whatever is in the fragment_container view with this fragment,
            // and add the transaction to the back stack
            transaction.replace(R.id.frame, mFragment);
    
            /*This would add the Fragment to the backstack...
            * But right now we comment it out.*/
            //        transaction.addToBackStack(null);
    
            // Commit the transaction
            transaction.commit();
        }
    
    }
    

    And sample Fragment:

    import android.os.Bundle;
    import android.support.v4.app.Fragment;
    import android.util.Log;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    /**
     * @author Martin Pfeffer (pepperonas)
     */
    public class FragmentOne extends Fragment {
    
        private static final String TAG = "FragmentOne";
    
    
        public static Fragment newInstance(int i) {
            Fragment fragment = new FragmentOne();
            Bundle args = new Bundle();
            args.putInt("the_id", i);
            fragment.setArguments(args);
            return fragment;
        }
    
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            Log.d(TAG, "onCreateView  " + "");
            return inflater.inflate(R.layout.fragment_one, container, false);
        }
    
    }
    

    Can be found on github.

    0 讨论(0)
提交回复
热议问题