java - Android APIs frequently return incorrect screen resolutions after device rotation - Stack Overflow

admin2025-04-17  3

I need to know the dimensions of an app in pixels for screen capture purposes. Previously I used methods like getRealMetrics to get the resolution and this works flawlessly under normal circumstances but have recently discovered (seemingly on Android versions 30+) that if the device is rotated the width and height values returned by the APIs will sometimes update to reflect the view being rotated but other times will not.

EDIT: I'm starting to suspect this may be due to behavior changes in WindowManager and it not being updated properly?

This all seems to work correctly on Android 29 and below but for some reason newer versions of android don't seem to update values derived from the WindowManager properly.

For example, if my app is 200px high and 100px wide in portrait mode, rotating into landscape will sometimes update to 100px high, 200px wide, but just as often it will continue to return 200px high, 100px wide indefinitely. In my experience so far there is no clear reason for why it sometimes updates correctly but other times does not.

My code is very simple and looks roughly like this. I have tried using getRealMetrics as well as the more recent getCurrentWindowMetrics where appropriate and even very deprecated methods like getWidth for the sake of testing but each one returns the same correct or incorrect value in sync with one another. (In other words, after each rotation they all are either correct or incorrect, so they are seemingly all getting data from the same ground "truth"). Considering this seems to be more common if not exclusive to newer devices I was really hoping that using the newer getCurrentWindowMetrics would work but no such luck.

EDIT Tried playing around with display.getRotation() and noticed that it always returned 0 no matter what I did so something fundamental isn't being set correctly.

Also note this isn't outwardly a race condition since once the rotation occurs the correct or incorrect values continue to be returned by the APIs until another rotation, at which point it's roughly 50/50 on whether on this rotation the values will be updated correctly on not.

import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;
import android.view.WindowMetrics;
import androidx.annotation.NonNull;

public class DisplayUtil {

  private static WindowManager windowManagerInstance;


  public static void getScreenDimensions() {
    Display getOrient = windowManagerInstance.getDefaultDisplay();

    if (VERSION.SDK_INT >= VERSION_CODES.R) {
      WindowMetrics metrics = windowManagerInstance.getCurrentWindowMetrics();
    } else {
      DisplayMetrics metrics = new DisplayMetrics();
      windowManagerInstance.getDefaultDisplay().getRealMetrics(metrics);
    }
  }

  public static synchronized void setWindowManager(WindowManager windowManager) {
    windowManagerInstance = windowManager;
  }
}

Has anyone else experienced this behavior before? Is this expected behavior or a known bug somehow? The inconsistency really throws me off. Is there some way something in my app is to blame here?

Example screenshot here, where "old" and "new" methods return (approximately) the same incorrect values.

I need to know the dimensions of an app in pixels for screen capture purposes. Previously I used methods like getRealMetrics to get the resolution and this works flawlessly under normal circumstances but have recently discovered (seemingly on Android versions 30+) that if the device is rotated the width and height values returned by the APIs will sometimes update to reflect the view being rotated but other times will not.

EDIT: I'm starting to suspect this may be due to behavior changes in WindowManager and it not being updated properly?

This all seems to work correctly on Android 29 and below but for some reason newer versions of android don't seem to update values derived from the WindowManager properly.

For example, if my app is 200px high and 100px wide in portrait mode, rotating into landscape will sometimes update to 100px high, 200px wide, but just as often it will continue to return 200px high, 100px wide indefinitely. In my experience so far there is no clear reason for why it sometimes updates correctly but other times does not.

My code is very simple and looks roughly like this. I have tried using getRealMetrics as well as the more recent getCurrentWindowMetrics where appropriate and even very deprecated methods like getWidth for the sake of testing but each one returns the same correct or incorrect value in sync with one another. (In other words, after each rotation they all are either correct or incorrect, so they are seemingly all getting data from the same ground "truth"). Considering this seems to be more common if not exclusive to newer devices I was really hoping that using the newer getCurrentWindowMetrics would work but no such luck.

EDIT Tried playing around with display.getRotation() and noticed that it always returned 0 no matter what I did so something fundamental isn't being set correctly.

Also note this isn't outwardly a race condition since once the rotation occurs the correct or incorrect values continue to be returned by the APIs until another rotation, at which point it's roughly 50/50 on whether on this rotation the values will be updated correctly on not.

import android.os.Build.VERSION;
import android.os.Build.VERSION_CODES;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.WindowManager;
import android.view.WindowMetrics;
import androidx.annotation.NonNull;

public class DisplayUtil {

  private static WindowManager windowManagerInstance;


  public static void getScreenDimensions() {
    Display getOrient = windowManagerInstance.getDefaultDisplay();

    if (VERSION.SDK_INT >= VERSION_CODES.R) {
      WindowMetrics metrics = windowManagerInstance.getCurrentWindowMetrics();
    } else {
      DisplayMetrics metrics = new DisplayMetrics();
      windowManagerInstance.getDefaultDisplay().getRealMetrics(metrics);
    }
  }

  public static synchronized void setWindowManager(WindowManager windowManager) {
    windowManagerInstance = windowManager;
  }
}

Has anyone else experienced this behavior before? Is this expected behavior or a known bug somehow? The inconsistency really throws me off. Is there some way something in my app is to blame here?

Example screenshot here, where "old" and "new" methods return (approximately) the same incorrect values.

Share Improve this question edited Jan 31 at 14:51 Blasterdude8 asked Jan 30 at 19:58 Blasterdude8Blasterdude8 331 silver badge9 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1

The behavior you're describing is likely related to changes in how Android handles window metrics and display orientation starting with Android 11 (API level 30). These changes were introduced to better support new features like multi-window mode, foldable devices and etc.

The Problems:

  1. Android has moved away from some older methods like getRealMetrics and toward WindowMetrics. However, even with WindowMetrics, the values ​​you get are tied to the window itself, not necessarily the display. This is important. The window may not take up the entire display, especially in multi-window mode, or in some cases the system may not immediately update the window's metrics after a rotation.
  2. The WindowMetrics you get from getCurrentWindowMetrics() reflect the current state of the window. While they should update when you rotate, the timing of these updates can be unpredictable. The system may delay updating, or in some extreme cases, the update may not happen right away, and as a result you see outdated measurements.
  3. The fact that display.getRotation() is not updating is a strong indicator that the system is reporting incorrect rotation changes to your app's window. This, combined with inconsistent WindowMetrics, points to an underlying problem.

Solution Approaches:

  1. Instead of relying on WindowMetrics or getRealMetrics only, you should listen for configuration changes. This is the most reliable way to get the correct dimensions after rotation:

  2. Make sure you are using an Activity context when you call getWindowManager(). This is essential for the onConfigurationChanged() callback to work correctly.

  3. The getScreenDimensions() method is now called from onConfigurationChanged(). This ensures that you get updated dimensions after the rotation has actually occurred and the window metrics have been adjusted.

  4. In your AndroidManifest.xml:

 <activity
    android:name=".YourActivity"
    android:configChanges="orientation|screenSize|screenLayout" />
public class MainActivity extends AppCompatActivity { 

    private static final String TAG = "ScreenDimensions";
    private WindowManager windowManagerInstance;
    private int currentWidth;
    private int currentHeight;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        windowManagerInstance = getWindowManager();
        getScreenDimensions(); // Get initial dimensions
    }


    @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.d(TAG, "onConfigurationChanged called");
        getScreenDimensions(); // Update dimensions on config change
    }

    private void getScreenDimensions() {

        if (VERSION.SDK_INT >= VERSION_CODES.R) {
            WindowMetrics metrics = windowManagerInstance.getCurrentWindowMetrics();
            currentWidth = metrics.getBounds().width();
            currentHeight = metrics.getBounds().height();

        } else {
            DisplayMetrics metrics = new DisplayMetrics();
            windowManagerInstance.getDefaultDisplay().getRealMetrics(metrics);
            currentWidth = metrics.widthPixels;
            currentHeight = metrics.heightPixels;
        }

        Log.d(TAG, "Width: " + currentWidth + ", Height: " + currentHeight);
        // Use currentWidth and currentHeight for your screen capture
    }
}
  1. Test on real devices. The delay may be due to the emulator not working correctly.

Turns out my suspicions were correct and all I had to do was update the WindowManager when appropriate! In this case I had a WindowCallback class that implemented Window.Callback and I could perform the update to the window manager there. The callback was called anytime the device orientation changed so the WindowManager object was always updated when the height and width flipped. A little annoying this is only an issue on newer versions of android (I think 30+) but glad it's resolved.

转载请注明原文地址:http://anycun.com/QandA/1744893049a89114.html