感谢@Sheldon指导我关于“无界面”片段中setRetainInstance(true)
的正确使用。这就是我解决此问题所采取的方法,现在我想在此贴出我的代码,希望对他人有所帮助。但首先:
口头解释
如问题中所述,设备方向变化会摧毁MainActivity extends BaseGameActivity
,以及您的游戏状态(即您与Google Play服务的连接)。然而,我们可以将所有GameHelper代码放入“无界面”片段(一个没有UI的片段),并声明setRetainInstance(true)
。 现在,当我们的MainActivity extends FragmentActivity
在方向更改时被销毁时,“无界面”片段会停止,甚至分离,但不被销毁!(onDestroy()
不被调用)当Android重新创建MainActivity
时,我们的“无界面”片段会自动重新附加到它上面。 此时,在我们的“无界面”片段中,onCreate()
未被调用。 因此,onCreate()
是我们连接GameHelper的地方。 我们可以在onDestroy()
中断开与GameHelper的连接,因为除了应用程序结束时(此时,杀死我们的连接都没有问题)之外,这将永远不会被调用。
注意:我认为GameHeaderFragment.java
可能应该分解为一个抽象类和一个从中继承的特定于游戏的类(但我在这里没有这样做)。
这就是我想出来的(请原谅我的特定于游戏的代码交织的地方):
GameHeaderFragment.java
public class GameHelperFragment extends Fragment implements GameHelperListener, OnInvitationReceivedListener, RoomUpdateListener, RoomStatusUpdateListener, RealTimeMessageReceivedListener {
protected MainActivity mActivity = null;
protected GameHelper mHelper;
final static int MAX_NUM_PLAYERS = 4;
final static int RC_SELECT_PLAYERS = 10000;
final static int RC_INVITATION_INBOX = 10001;
final static int RC_WAITING_ROOM = 10002;
public static final int CLIENT_GAMES = GameHelper.CLIENT_GAMES;
public static final int CLIENT_APPSTATE = GameHelper.CLIENT_APPSTATE;
public static final int CLIENT_PLUS = GameHelper.CLIENT_PLUS;
public static final int CLIENT_ALL = GameHelper.CLIENT_ALL;
protected int mRequestedClients = CLIENT_GAMES;
protected String mSigningInMessage = "Signing in with Google";
protected String mSigningOutMessage = "Signing out";
String mMyId = "";
String mRoomId = "";
ArrayList<Participant> mParticipants = null;
int mCurrentlyPlayingIdx = 0;
boolean mIsMultiplayer = false;
boolean mWaitRoomDismissedFromCode = false;
public interface GameHelperFragmentListener {
void onSignInFailed();
void onSignInSucceeded();
void onInvitationReceived(Invitation invitation);
void showMainMenu();
void showWaitScreen();
void startGame();
void participantLeftAtIdx(int idx);
void handleRealTimeMessage(RealTimeMessage rtm);
}
GameHelperFragmentListener mListener;
public GameHelperFragment() {
super();
Log.d("mab", "GHFrag.Constructor()");
}
protected void setRequestedClients(int requestedClients) {
mRequestedClients = requestedClients;
}
@Override
public void onAttach(Activity activity) {
Log.d("mab", this + ": onAttach(" + activity + ")");
super.onAttach(activity);
mActivity = (MainActivity) activity;
mListener = (GameHelperFragmentListener) activity;
}
@Override
public void onCreate(Bundle b) {
Log.d("mab", this + ": onCreate()");
super.onCreate(b);
setRetainInstance(true);
mHelper = new GameHelper(mActivity);
mHelper.setup(this, mRequestedClients);
mHelper.setSigningInMessage(mSigningInMessage);
mHelper.setSigningOutMessage(mSigningOutMessage);
mHelper.onStart(mActivity);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return null;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.d("mab", this + ": onActivityCreated()");
super.onActivityCreated(savedInstanceState);
}
@Override
public void onDestroy() {
Log.d("mab", this + ": onDestroy()");
super.onDestroy();
mHelper.onStop();
}
@Override
public void onActivityResult(int requestCode, int responseCode, Intent data) {
Log.d("mab", this + ": onActivityResult(" + requestCode + ")");
super.onActivityResult(requestCode, responseCode, data);
mHelper.onActivityResult(requestCode, responseCode, data);
switch (requestCode) {
case RC_SELECT_PLAYERS:
handleSelectPlayersResult(responseCode, data);
break;
case RC_INVITATION_INBOX:
handleInvitationInboxResult(responseCode, data);
break;
case RC_WAITING_ROOM:
if (mWaitRoomDismissedFromCode) break;
if (responseCode == Activity.RESULT_OK) {
} else if (responseCode == GamesActivityResultCodes.RESULT_LEFT_ROOM) {
leaveRoom();
} else if (responseCode == Activity.RESULT_CANCELED) {
leaveRoom();
}
break;
}
}
private void handleSelectPlayersResult(int responseCode, Intent data) {
if (responseCode != Activity.RESULT_OK) {
Log.w("mab", "*** select players UI cancelled, " + responseCode);
showMainMenu();
return;
}
Log.d("mab", "Select players UI succeeded.");
final ArrayList<String> invitees = data.getStringArrayListExtra(GamesClient.EXTRA_PLAYERS);
Log.d("mab", "Invitee count: " + invitees.size());
Bundle autoMatchCriteria = null;
int minAutoMatchPlayers = data.getIntExtra(GamesClient.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
int maxAutoMatchPlayers = data.getIntExtra(GamesClient.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);
if (minAutoMatchPlayers > 0 || maxAutoMatchPlayers > 0) {
autoMatchCriteria = RoomConfig.createAutoMatchCriteria(
minAutoMatchPlayers, maxAutoMatchPlayers, 0);
Log.d("mab", "Automatch criteria: " + autoMatchCriteria);
}
Log.d("mab", "Creating room...");
RoomConfig.Builder rtmConfigBuilder = RoomConfig.builder(this);
rtmConfigBuilder.addPlayersToInvite(invitees);
rtmConfigBuilder.setMessageReceivedListener(this);
rtmConfigBuilder.setRoomStatusUpdateListener(this);
if (autoMatchCriteria != null) {
rtmConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);
}
showWaitScreen();
keepScreenOn();
getGamesClient().createRoom(rtmConfigBuilder.build());
Log.d("mab", "Room configured, waiting for it to be created...");
}
private void handleInvitationInboxResult(int response, Intent data) {
if (response != Activity.RESULT_OK) {
Log.d("mab", "*** invitation inbox UI cancelled, " + response);
showMainMenu();
return;
}
Log.d("mab", "Invitation inbox UI succeeded.");
Invitation inv = data.getExtras().getParcelable(GamesClient.EXTRA_INVITATION);
acceptInviteToRoom(inv.getInvitationId());
}
protected GamesClient getGamesClient() {
return mHelper.getGamesClient();
}
protected AppStateClient getAppStateClient() {
return mHelper.getAppStateClient();
}
protected PlusClient getPlusClient() {
return mHelper.getPlusClient();
}
protected boolean isSignedIn() {
return mHelper.isSignedIn();
}
protected void beginUserInitiatedSignIn() {
mHelper.beginUserInitiatedSignIn();
}
protected void signOut() {
mHelper.signOut();
}
protected void showAlert(String title, String message) {
mHelper.showAlert(title, message);
}
protected void showAlert(String message) {
mHelper.showAlert(message);
}
protected void enableDebugLog(boolean enabled, String tag) {
mHelper.enableDebugLog(enabled, tag);
}
protected String getInvitationId() {
return mHelper.getInvitationId();
}
protected void reconnectClients(int whichClients) {
mHelper.reconnectClients(whichClients);
}
protected String getScopes() {
return mHelper.getScopes();
}
protected boolean hasSignInError() {
return mHelper.hasSignInError();
}
protected ConnectionResult getSignInError() {
return mHelper.getSignInError();
}
protected void setSignInMessages(String signingInMessage, String signingOutMessage) {
mSigningInMessage = signingInMessage;
mSigningOutMessage = signingOutMessage;
}
public void setRoomId(String rid) {
mRoomId = rid;
}
public String getRoomId() {
return mRoomId;
}
@Override
public void onRealTimeMessageReceived(RealTimeMessage rtm) {
mListener.handleRealTimeMessage(rtm);
}
@Override
public void onConnectedToRoom(Room room) {
Log.d("mab", "onConnectedToRoom.");
mRoomId = room.getRoomId();
mParticipants = room.getParticipants();
mMyId = room.getParticipantId(getGamesClient().getCurrentPlayerId());
Log.d("mab", "Room ID: " + mRoomId);
Log.d("mab", "My ID " + mMyId);
Log.d("mab", "<< CONNECTED TO ROOM>>");
Log.d("mab", " Number of Joined Participants: " + getNumJoinedParticipants());
}
@Override
public void onDisconnectedFromRoom(Room room) {
mIsMultiplayer = false;
mRoomId = null;
showGameError("Disconnected from room");
}
@Override
public void onJoinedRoom(int statusCode, Room room) {
Log.d("mab", "onJoinedRoom(" + statusCode + ")");
if (room != null) { Log.d("mab", " roomId: " + room.getRoomId()); }
if (statusCode != GamesClient.STATUS_OK) {
mIsMultiplayer = false;
Log.e("mab", "*** Error: onJoinedRoom, status " + statusCode);
showGameError("Joined room unsuccessfully: " + statusCode);
return;
}
mRoomId = room.getRoomId();
showWaitingRoom(room);
}
@Override
public void onLeftRoom(int statusCode, String roomId) {
Log.d("mab", "onLeftRoom, code " + statusCode);
mRoomId = null;
showMainMenu();
}
@Override
public void onRoomConnected(int statusCode, Room room) {
Log.d("mab", "onRoomConnected(" + statusCode + ")");
if (room != null) { Log.d("mab", " roomId: " + room.getRoomId()); }
if (statusCode != GamesClient.STATUS_OK) {
mIsMultiplayer = false;
Log.d("mab", "*** Error: onRoomConnected, status " + statusCode);
showGameError("Roon connected unsuccessfully: " + statusCode);
return;
}
mRoomId = room.getRoomId();
mParticipants = room.getParticipants();
mIsMultiplayer = true;
mCurrentlyPlayingIdx = 0;
mListener.startGame();
}
@Override
public void onRoomCreated(int statusCode, Room room) {
Log.d("mab", "onRoomCreated(" + statusCode + ")");
if (room != null) { Log.d("mab", " roomId: " + room.getRoomId()); }
if (statusCode != GamesClient.STATUS_OK) {
mIsMultiplayer = false;
Log.e("mab", "*** Error: onRoomCreated, status " + statusCode);
showGameError("Room not created successfully: " + statusCode);
return;
}
mRoomId = room.getRoomId();
showWaitingRoom(room);
}
@Override
public void onInvitationReceived(Invitation invitation) {
Log.d("mab", "ghFrag.onInvitationReceived()");
mListener.onInvitationReceived(invitation);
}
@Override
public void onSignInFailed() {
mListener.onSignInFailed();
}
@Override
public void onSignInSucceeded() {
getGamesClient().registerInvitationListener(this);
if (getInvitationId() != null) {
acceptInviteToRoom(getInvitationId());
return;
}
mListener.onSignInSucceeded();
}
void acceptInviteToRoom(String invId) {
Log.d("mab", "Accepting invitation: " + invId);
keepScreenOn();
RoomConfig.Builder roomConfigBuilder = RoomConfig.builder(this);
roomConfigBuilder.setInvitationIdToAccept(invId)
.setMessageReceivedListener(this)
.setRoomStatusUpdateListener(this);
showWaitScreen();
getGamesClient().joinRoom(roomConfigBuilder.build());
}
void keepScreenOn() {
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
void stopKeepingScreenOn() {
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
public void inviteFriends() {
Intent intent = getGamesClient().getSelectPlayersIntent(1, 3);
showWaitScreen();
startActivityForResult(intent, RC_SELECT_PLAYERS);
}
void leaveRoom() {
Log.d("mab", "Leaving room.");
mIsMultiplayer = false;
stopKeepingScreenOn();
if (mRoomId != null) {
getGamesClient().leaveRoom(this, mRoomId);
mRoomId = null;
showWaitScreen();
} else {
showMainMenu();
}
}
void showWaitingRoom(Room room) {
Log.d("mab", "GHFrag.showWaitingRoom()");
mWaitRoomDismissedFromCode = false;
int minPlayers = MAX_NUM_PLAYERS;
Intent i = getGamesClient().getRealTimeWaitingRoomIntent(room, minPlayers);
getActivity().startActivityForResult(i, RC_WAITING_ROOM);
}
void dismissWaitingRoom() {
mWaitRoomDismissedFromCode = true;
getActivity().finishActivity(RC_WAITING_ROOM);
}
void showGameError(String msg) {
showAlert("Error", "Game Error: " + msg);
showMainMenu();
}
private void showMainMenu() {
mListener.showMainMenu();
}
private void showWaitScreen() {
mListener.showWaitScreen();
}
}
MainActivity.java
public class MainActivity extends FragmentActivity implements MainMenuFragment.Listener, PlayFragment.Listener, GameHelperFragmentListener, AlertDialogFragmentListener {
public static final String MAIN_MENU_FRAGMENT = "MainMenuFragment";
public static final String PLAY_FRAGMENT = "PlayFragment";
public static final String WAIT_FRAGMENT = "WaitFragment";
MainMenuFragment mMainMenuFragment;
PlayFragment mPlayFragment;
WaitFragment mWaitFragment;
GameHelperFragment gameHelperFragment = null;
String mIncomingInvitationId = null;
@SuppressLint("NewApi")
@Override
public void onCreate(Bundle savedInstanceState) {
Log.d("mab", "MainActivity.onCreate()");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
gameHelperFragment = (GameHelperFragment) getSupportFragmentManager().findFragmentByTag("GameHelperFragment");
if (gameHelperFragment == null) {
Log.d("mab", this + ": Existing fragment not found.!!!");
gameHelperFragment = new GameHelperFragment();
gameHelperFragment.setSignInMessages("Signing in with Google", "Signing out");
getSupportFragmentManager().beginTransaction().add(gameHelperFragment, "GameHelperFragment").commit();
} else {
Log.d("mab", this + ": Existing fragment found.!!!");
}
}
@Override
public void onSignInFailed() {
Log.d("mab", "MainActivity.onSignInFailed()");
if (mMainMenuFragment != null) {
mMainMenuFragment.updateUi();
}
}
@Override
public void onSignInSucceeded() {
Log.d("mab", "MainActivity.onSignInSuccedded()");
if (mMainMenuFragment != null) {
mMainMenuFragment.updateUi();
}
}
@Override
public void onSignInButtonClicked() {
Log.d("mab", "MainActivity.onSignInButtonClicked()");
beginUserInitiatedSignIn();
}
@Override
public void onSignOutButtonClicked() {
Log.d("mab", "MainActivity.onSignOutButtonClicked()");
signOut();
if (mMainMenuFragment != null) {
mMainMenuFragment.updateUi();
}
}
@Override
public void onInvitationReceived(Invitation invitation) {
mIncomingInvitationId = invitation.getInvitationId();
String dispName = invitation.getInviter().getDisplayName();
DialogFragment alertInvitationReceived = AlertDialogFragment.newInstance("Invitation Received", dispName +
" is inviting you to play Yahtzee Blast.", "Accept", "Decline", null);
alertInvitationReceived.show(getSupportFragmentManager(), DLG_INVITATION_RECVD);
}
@Override
protected void onPause() {
Log.d("mab", "MainActivity.onPause()");
super.onPause();
}
@Override
protected void onStop() {
Log.d("mab", "MainActivity.onStop()");
super.onStop();
}
@Override
protected void onStart() {
Log.d("mab", "MainActivity.onStart()");
super.onStart();
}
@Override
protected void onResume() {
Log.d("mab", "MainActivity.onResume()");
super.onResume();
}
@Override
protected void onDestroy() {
Log.d("mab", "MainActivity.onDestroy()");
super.onDestroy();
mHelper = null;
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("mIncomingInvitationId", mIncomingInvitationId);
}
@Override
public void onInviteFriendsClicked() {
Log.d("mab", "MainActivity.onInviteFriendsClicked()");
gameHelperFragment.inviteFriends();
}
@Override
public void onSeeAllInvitationsClicked() {
Log.d("mab", "MainActivity.onSeeAllInvitationsClicked()");
gameHelperFragment.seeAllInvitations();
}
@Override
public void onActivityResult(int requestCode, int responseCode, Intent intent) {
Log.d("mab", this + ": onActivityResult(requestCode: " + requestCode + ", responseCode: " + responseCode + ")");
super.onActivityResult(requestCode, responseCode, intent);
gameHelperFragment.onActivityResult(requestCode, responseCode, intent);
}
public void onAlertDialogFragmentPositiveClicked(String tag) {
Log.d("mab", "MainActivity.onAlertDialogFragmentPositiveClicked(" + tag + ")");
if (tag == DLG_INVITATION_RECVD) {
gameHelperFragment.acceptInviteToRoom(mIncomingInvitationId);
}
}
public void handleRealTimeMessage(RealTimeMessage rtm) {
Log.d(TAG, "MainActivity.onRealTimeMessageReceived()");
}
private void setSignInMessages(String signingInMessage, String signingOutMessage) {
gameHelperFragment.setSignInMessages(signingInMessage, signingOutMessage);
}
private GamesClient getGamesClient() {
return gameHelperFragment.getGamesClient();
}
private String getInvitationId() {
return gameHelperFragment.getInvitationId();
}
private void beginUserInitiatedSignIn() {
gameHelperFragment.beginUserInitiatedSignIn();
}
private void signOut() {
gameHelperFragment.signOut();
}
private void showAlert(String message) {
gameHelperFragment.showAlert(message);
}
private void showAlert(String title, String message) {
gameHelperFragment.showAlert(title, message);
}
public GameHelperFragment getGameHelperFragment() {
return gameHelperFragment;
}
@Override
public void showMainMenu() {
switchToFragment(MAIN_MENU_FRAGMENT, false);
}
@Override
public void showWaitScreen() {
switchToFragment(WAIT_FRAGMENT, false);
}
@Override
public void participantLeftAtIdx(int idx) {
}
}
sigleTop=true
。本质上,您需要使您的应用程序成为单例并始终使用一个实例。尝试一下。 - Shark