How to prevent Screen Orientation change when Acti

2020-08-13 05:49发布

问题:

I'm working on an app and encountered a strange behavior on 8.1 Devices (Pixel2, Nexus5x), so I wrote a small App to verify this behavior.

MainActivity is locked to portrait mode while LandscapeActivity is locked to landscape. MainActivity starts the LandscapeActivity for Result. Screen Switches from portrait to landscape as expected. When LandscapeActivity is finished it propagates the Result to MainActivity, while switching back to portrait (as expected).

But Sometimes I encounter kind of a bug here. After onActivityResult in MainActivity it switches from portrait to landscape to switch back to portrait immediately. While I can handle the state, it still looks nasty.

For traceability I will post everything to rebuild it.

What can I do to prevent it? Also note that it does not happen every time, and as far as I can test on 8.1 devices ONLY. It seems fine on android devices below 8.1

EDIT: Added android:configChanges="orientation|screenSize" to my manifest as @Floern suggested in his answer without success. A confirmation on a platform bug will be an acceptable answer, too

Manifest

<application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity"
            android:screenOrientation="portrait"
            android:configChanges="orientation|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".LandscapeActivity"
            android:screenOrientation="landscape"/>

    </application>

MainActivity

public class MainActivity extends AppCompatActivity {

    public static final String TAG = MainActivity.class.getSimpleName();

    private static final int REQ_CODE = 878;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate: savedInstanceState=" + savedInstanceState + ", orientation=" + orientation());
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(MainActivity.this, LandscapeActivity.class);
                startActivityForResult(intent, REQ_CODE);

            }
        });
    }

    @Override
    protected void onResume() {
        Log.d(TAG, "onResume: orientation=" + orientation());
        super.onResume();
    }

    @Override
    protected void onPause() {
        Log.d(TAG, "onPause: orientation=" + orientation());
        super.onPause();
    }

    @Override
    protected void onStart() {
        Log.d(TAG, "onStart: orientation=" + orientation());
        super.onStart();
    }

    @Override
    protected void onStop() {
        Log.d(TAG, "onStop: orientation=" + orientation());
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        Log.d(TAG, "onDestroy: orientation=" + orientation());
        super.onDestroy();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "onActivityResult: orientation=" + orientation());
        if (requestCode == REQ_CODE){
            Toast.makeText(this, "Result=" + data.getStringExtra(LandscapeActivity.KEY_DATA), Toast.LENGTH_LONG).show();
        }
    }

    String orientation(){
        return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ?  "Landscape" : "Portrait";
    }
}

LandscapeActivity

public class LandscapeActivity extends AppCompatActivity{

    public static final String TAG = LandscapeActivity.class.getSimpleName();

    public static final String KEY_DATA = "DATA";
    static int COUNTER = 0;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "onCreate: savedInstanceState=" + savedInstanceState + "orientation=" + (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ?  "Landscape" : "Portrait"));
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent data = new Intent();
                COUNTER++;
                data.putExtra(KEY_DATA, "Runned " + COUNTER + " times");
                setResult(RESULT_OK, data);
                finish();
            }
        });
    }

    @Override
    protected void onResume() {
        Log.d(TAG, "onResume: orientation=" + orientation());
        super.onResume();
    }

    @Override
    protected void onPause() {
        Log.d(TAG, "onPause: orientation=" + orientation());
        super.onPause();
    }

    @Override
    protected void onStart() {
        Log.d(TAG, "onStart: orientation=" + orientation());
        super.onStart();
    }

    @Override
    protected void onStop() {
        Log.d(TAG, "onStop: orientation=" + orientation());
        super.onStop();
    }

    @Override
    protected void onDestroy() {
        Log.d(TAG, "onDestroy: orientation=" + orientation());
        super.onDestroy();
    }

    String orientation(){
        return getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ?  "Landscape" : "Portrait";
    }
}

build.gradle

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "android.example.com.orientationtest8_1"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

layout activity_main File:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="android.dermalog.com.orientationtest8_1.MainActivity">

    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

And last a Log from clicking on Buttons repeatadly:

01-04 15:18:06.744  D/MainActivity: onCreate: savedInstanceState=null
01-04 15:18:06.847  D/MainActivity: onStart: orientation=Portrait
01-04 15:18:06.850  D/MainActivity: onResume: orientation=Portrait
01-04 15:18:14.036  D/MainActivity: onPause: orientation=Portrait
01-04 15:18:14.108  D/LandscapeActivity: onCreate: savedInstanceState=nullorientation=Landscape
01-04 15:18:14.139  D/LandscapeActivity: onStart: orientation=Landscape
01-04 15:18:14.141  D/LandscapeActivity: onResume: orientation=Landscape
01-04 15:18:14.217  D/MainActivity: onStop: orientation=Portrait
01-04 15:18:15.643  D/LandscapeActivity: onPause: orientation=Landscape
01-04 15:18:15.711  D/MainActivity: onActivityResult: orientation=Portrait
01-04 15:18:15.719  D/MainActivity: onStart: orientation=Portrait
01-04 15:18:15.720  D/MainActivity: onResume: orientation=Portrait
01-04 15:18:15.786  D/LandscapeActivity: onStop: orientation=Landscape
01-04 15:18:15.786  D/LandscapeActivity: onDestroy: orientation=Landscape
01-04 15:18:18.036  D/MainActivity: onPause: orientation=Portrait
01-04 15:18:18.097  D/LandscapeActivity: onCreate: savedInstanceState=nullorientation=Landscape
01-04 15:18:18.121  D/LandscapeActivity: onStart: orientation=Landscape
01-04 15:18:18.123  D/LandscapeActivity: onResume: orientation=Landscape
01-04 15:18:18.213  D/MainActivity: onStop: orientation=Portrait
01-04 15:18:19.505  D/LandscapeActivity: onPause: orientation=Landscape
01-04 15:18:19.564  D/MainActivity: onActivityResult: orientation=Portrait
01-04 15:18:19.569  D/MainActivity: onStart: orientation=Portrait
01-04 15:18:19.569  D/MainActivity: onResume: orientation=Portrait
01-04 15:18:19.639  D/LandscapeActivity: onStop: orientation=Landscape
01-04 15:18:19.640  D/LandscapeActivity: onDestroy: orientation=Landscape
01-04 15:18:20.102  D/MainActivity: onPause: orientation=Portrait
01-04 15:18:20.103  D/MainActivity: onStop: orientation=Portrait
01-04 15:18:20.104  D/MainActivity: onDestroy: orientation=Portrait
01-04 15:18:20.123  D/MainActivity: onCreate: savedInstanceState=Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1@7ceec3, 2131165191=android.support.v7.widget.Toolbar$SavedState@3c34340, 2131165193=android.view.AbsSavedState$1@7ceec3, 2131165199=android.view.AbsSavedState$1@7ceec3, 2131165219=android.view.AbsSavedState$1@7ceec3, 2131165229=android.view.AbsSavedState$1@7ceec3}}], android:lastAutofillId=1073741823, android:fragments=android.app.FragmentManagerState@c215279}]
01-04 15:18:20.149  D/MainActivity: onStart: orientation=Landscape
01-04 15:18:20.152  D/MainActivity: onResume: orientation=Landscape
01-04 15:18:20.699  D/MainActivity: onPause: orientation=Landscape
01-04 15:18:20.701  D/MainActivity: onStop: orientation=Landscape
01-04 15:18:20.702  D/MainActivity: onDestroy: orientation=Landscape
01-04 15:18:20.718  D/MainActivity: onCreate: savedInstanceState=Bundle[{android:viewHierarchyState=Bundle[{android:views={16908290=android.view.AbsSavedState$1@7ceec3, 2131165191=android.support.v7.widget.Toolbar$SavedState@807af46, 2131165193=android.view.AbsSavedState$1@7ceec3, 2131165199=android.view.AbsSavedState$1@7ceec3, 2131165219=android.view.AbsSavedState$1@7ceec3, 2131165229=android.view.AbsSavedState$1@7ceec3}}], android:lastAutofillId=1073741823, android:fragments=android.app.FragmentManagerState@8d12507}]
01-04 15:18:20.748  D/MainActivity: onStart: orientation=Portrait
01-04 15:18:20.751  D/MainActivity: onResume: orientation=Portrait

回答1:

This bug was fixed. So waiting for next release/patch.



回答2:

Yes, This is an issue in Android 8.1 OS version. As this was made obsolete. we can have a work around for these kind of issues. In this particular API level , Android OS might be storing the latest orientation value and is being applied to all the screens until the previous screen or that particular screen is destroyed. So, the work around for this type of issue is to change the orientation before going back to the screen .
for ex:

If ScreenA is in portrait mode and screenB is in landscape mode and if screen is moved to ScreenB which is Landscape and if you come back to ScreenA without killing ScreenB (restoring ScreenA), screenA also be visible in Landscape.
To fix this issue, force change the orientation of ScreenA to Portrait in onPause() or OnStop() of ScreenB(to restore ScreenA).

 if (android.os.Build.VERSION.SDK_INT >= 27) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

after this call, ScreenA will be in portrait. Also, in onResume() of ScreenB,

if (android.os.Build.VERSION.SDK_INT >= 27) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
}


回答3:

This might indeed be a framework bug. I noticed similar behavior on Samsung devices while using the camera app (via Intent) in landscape from a portrait-only app. I couldn't figure out what exactly the cause is, but I found a work-around that minimizes the effect.

If you tell the system you want to handle the orientation change yourself, by adding android:configChanges="orientation|screenSize" to your Activity, it won't be recreated on orientation change. Thus you can avoid the overhead of recreating the Activity (twice), which may improve the performance up to the point that you don't notice the orientation change at all.

<activity android:name=".MainActivity"
    android:screenOrientation="portrait"
    android:configChanges="orientation|screenSize">