Android:屏幕旋转后Activity方向未始终切换问题求助
Hey there! Let's sort out that screen rotation issue with your Android drawing app. Looking at your code and the problem description, the main culprits are Android's default behavior of recreating the Activity during rotation, plus some missing handling for thread lifecycle and state persistence. Here's how to fix this step by step:
1. Quick Fix: Prevent Activity Recreation During Rotation
If your app doesn't need to load different resources for portrait/landscape, the fastest solution is to tell Android not to recreate your Activity when the screen rotates. Add this to your AndroidManifest.xml for your MainActivity:
<activity android:name=".MainActivity" android:configChanges="orientation|screenSize|smallestScreenSize"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
Then override onConfigurationChanged in MainActivity to manually update the SurfaceView size when the orientation changes:
@Override public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); SurfaceView mainView = findViewById(R.id.mainView); if (mGameApp != null) { mGameApp.setSurfaceSize(mainView.getWidth(), mainView.getHeight()); } }
This skips the Activity recreation entirely, so your thread and drawing state stay intact.
2. Standard Fix: Save and Restore Activity State
If you do need the Activity to recreate (e.g., to load orientation-specific resources), you need to properly save and restore your app's state:
Step 2.1: Save Running State in onSaveInstanceState
Add this to MainActivity to save whether your game thread was running before rotation:
@Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean("isThreadRunning", mGameApp != null && mGameApp.isRunning()); }
Note: Add a getter for mRun in GameApp to access the running state:
public boolean isRunning() { return mRun; }
Step 2.2: Restore State in onCreate
Update your onCreate method to resume the thread if it was running before the rotation:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); SurfaceView mainView = findViewById(R.id.mainView); SurfaceHolder holder = mainView.getHolder(); holder.addCallback(this); mGameApp = new GameApp(getResources(), holder); // Restore state if Activity is recreating if (savedInstanceState != null) { boolean wasRunning = savedInstanceState.getBoolean("isThreadRunning"); mGameApp.setRunning(wasRunning); } }
Step 2.3: Clean Up Thread Properly in surfaceDestroyed
Make sure you clear the thread reference when the Surface is destroyed to avoid leftover threads after recreation:
@Override public void surfaceDestroyed(SurfaceHolder holder) { boolean retry = true; mGameApp.setRunning(false); while (retry) { try { if (mGameThread != null) { mGameThread.join(); mGameThread = null; // Clear reference to avoid reuse } retry = false; } catch (InterruptedException e) { e.printStackTrace(); // Don't swallow exceptions—helps with debugging! } } }
3. Optimize Drawing Logic for Consistency
Your current draw method reloads the cell Drawable every time it runs, which can cause performance hits and potential glitches during rotation. Initialize the Drawable once in GameApp's constructor instead:
public class GameApp { private Resources mResources; private SurfaceHolder mSurfaceHolder; private int mCanvasHeight = 1; private int mCanvasWidth = 1; private volatile boolean mRun = false; private Drawable cellImage; // Reusable Drawable instance public GameApp(Resources resources, SurfaceHolder surfaceHolder) { mResources = resources; mSurfaceHolder = surfaceHolder; cellImage = mResources.getDrawable(R.drawable.cell); // Initialize once } // ... rest of your methods ... private void draw(Canvas canvas) { canvas.drawColor(Color.GREEN); final float cellWidth = (float) mCanvasWidth / 6; final float cellHeight = (float) mCanvasHeight / 6; for (int i = 0; i < 6; i++) { for (int j = 0; j < 6; j++) { float x = i * cellWidth; float y = j * cellHeight; cellImage.setBounds(Math.round(x), Math.round(y), Math.round(x + cellWidth), Math.round(y + cellHeight)); cellImage.draw(canvas); } } } }
Why This Happened
- Activity Recreation: By default, Android destroys and recreates your Activity when the screen rotates. This meant your old thread and
GameAppinstance were discarded, and the new instance didn't know to resume the drawing loop. - Surface Size Mismatch: Sometimes the
surfaceChangedcallback wasn't triggering correctly after recreation, leading to wrong canvas dimensions for drawing. - Drawable Reloads: Reinitializing the Drawable every frame wasted resources and could cause inconsistencies during configuration changes.
内容的提问来源于stack exchange,提问作者Denis

