如何在Android 8.1版本设备上防止Activity完成时屏幕方向改变?

12

我正在开发一个应用程序,但在8.1设备(Pixel2,Nexus5x)上遇到了奇怪的行为,所以我编写了一个小应用程序来验证这种行为。

MainActivity 锁定为 portrait 模式,而 LandscapeActivity 锁定为 landscapeMainActivity 启动 LandscapeActivity 以获取结果。屏幕按预期从 portrait 切换到 landscape。当完成 LandscapeActivity 后,它将结果传播到 MainActivity,同时切换回 portrait (如预期)。

但有时候我会遇到一些 bug。在 MainActivityonActivityResult 方法执行后,它会立即从 portrait 切换到 landscape 再切换回 portrait。虽然我可以处理状态,但它看起来仍然很糟糕。

为了追踪问题,我将发布一切来重建它。

我该怎么做才能防止这种情况?请注意,它并不总是发生,并且据我测试仅在8.1设备上出现问题。在Android 8.1以下的设备上似乎没问题。

编辑: 已根据@Floern的建议将 android:configChanges="orientation|screenSize" 添加到我的清单中,但没有成功。 如果确认是平台 bug,则也可以接受答案。

清单

<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>

主活动

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";
    }
}

景观活动

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'
        }
    }
}

布局 activity_main 文件:

<?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>

最后,点击按钮反复出现的日志:

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

那么,它没有避免的方法吗?我也有同样的问题。 - Wun
3个回答

4
是的,这是在Android 8.1版本中的一个问题。由于此已过时,我们可以解决这类问题。在此特定的API级别中,Android操作系统可能会存储最新的方向值,并应用于所有屏幕,直到前一个屏幕或该特定屏幕被销毁为止。因此,解决此类问题的方法是在返回到屏幕之前改变方向。 例如: 如果ScreenA处于竖屏模式,而screenB处于横屏模式,并且如果将屏幕移动到横屏的ScreenB,并且不杀死ScreenB(还原ScreenA),则ScreenA也会以横屏模式显示。 为了解决这个问题,在ScreenB的onPause()或OnStop()中强制更改ScreenA的方向为Portrait。
 if (android.os.Build.VERSION.SDK_INT >= 27) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}

这个调用之后,ScreenA将会处于竖屏模式。同时,在ScreenB的onResume()方法中,

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

在屏幕上,我们无法控制的情况怎么办?比如去发邮件或者在社交媒体上分享文本。我们无法控制那些应用程序的代码。 - Abu Saad Papa
@Pops,这个问题只会在ScreenA的方向与ScreenB不同且两者都应该被限制为单一类型(即纵向或横向)时才会出现。在你的问题中,如果屏幕移出了你的应用程序,可能什么也不会发生。 - Rushi Ayyappa
想象一下,我们所有的屏幕都是横向的,并且我们在应用程序中有一个分享消息的按钮。当我们点击该按钮以进入消息应用程序/WhatsApp并将手机切换为纵向模式时,现在消息应用程序将处于纵向模式。当我们回到我们的应用程序时,横向屏幕将显示为纵向,并且我们无法通过上述解决方案来解决这个问题。 - Abu Saad Papa
是的,我遇到了同样的问题。但这是我们可以得到的解决方案。这对我有用。你能发一下你的代码吗?我会检查的。 - Rushi Ayyappa

4

这个漏洞已经被修复。请等待下一个版本/修补程序。


请问您能告诉我们这个问题完全修复的发布版本号吗? - Ahmad Arslan
这个问题修复了吗? - Ahmad Arslan
不予修复(已过时):由于产品的更改,该问题已不再相关。 - CeH9
@Rafael T 请检查我的答案。 - Rushi Ayyappa
他们说它已经在2019年1月30日修复了。 - Freddie

2

这可能确实是一个框架的 bug。我在使用相机应用程序(通过 Intent)从一个仅支持纵向的应用程序中横向使用时,也注意到了三星设备上类似的行为。我没能找出具体原因,但我找到了一个可以将影响最小化的解决方法。

如果您告诉系统您要自己处理方向变化,只需在您的 Activity 中添加 android:configChanges="orientation|screenSize" ,它就不会在方向发生改变时重新创建。因此,您可以避免重新创建 Activity 的开销(两次),这可能会提高性能,甚至表现得您根本没有注意到方向的变化。

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

嘿,在我的原始应用程序中,我也在横向使用相机,这就是为什么我编写了上面的示例以确保它与此无关的原因。关于您的“configChanges”:我在我的示例中尝试过它,但仍然得到相同的行为。我将在我的原始问题中进行编辑,以防止其他人尝试它。 - Rafael T
1
活动在屏幕方向改变时是否仍会重新创建?如果是,则可能是同一Activity类的另一个实例。注意,我的Activity具有android:launchMode="singleTask"属性,但我认为这对此没有影响。 - Floern
没错!我也尝试了singleTasksingleTopsingleInstance启动模式,但没有任何改变。请注意,这只会在8.1设备上发生。如果这是一个框架错误,如何确认? - Rafael T
你们有得到任何帮助吗? - Ahmad Arslan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接