Android:尝试使用 Google Play 服务启动默认播放器选择器大厅,遇到 NullPointerException

Posted

技术标签:

【中文标题】Android:尝试使用 Google Play 服务启动默认播放器选择器大厅,遇到 NullPointerException【英文标题】:Android: Trying to Launch Default Player Picker Lobby using Google Play Services, running into NullPointerException 【发布时间】:2014-05-15 18:30:05 【问题描述】:

在过去的四天里,我一直被这个关于我的 android 应用程序的问题所困扰。我正在尝试从 Google Play 服务启动默认的 Player Picker UI 屏幕,但无论何时我都会遇到 NullPointerException:

04-03 13:12:22.045: E/AndroidRuntime(13042): FATAL EXCEPTION: main
04-03 13:12:22.045: E/AndroidRuntime(13042): java.lang.NullPointerException: Appropriate Api was not requested.
04-03 13:12:22.045: E/AndroidRuntime(13042):    at com.google.android.gms.internal.er.b(Unknown Source)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at com.google.android.gms.common.api.b.a(Unknown Source)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at com.google.android.gms.games.Games.c(Unknown Source)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at com.google.android.gms.internal.gn.getSelectOpponentsIntent(Unknown Source)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at com.geti.geti.graphics.MultiplayerLobbyView.startInviteGame(MultiplayerLobbyView.java:102)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at com.geti.geti.graphics.MultiplayerLobbyView.onClick(MultiplayerLobbyView.java:302)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at android.view.View.performClick(View.java:4475)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at android.view.View$PerformClick.run(View.java:18786)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at android.os.Handler.handleCallback(Handler.java:730)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at android.os.Handler.dispatchMessage(Handler.java:92)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at android.os.Looper.loop(Looper.java:137)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at android.app.ActivityThread.main(ActivityThread.java:5419)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at java.lang.reflect.Method.invokeNative(Native Method)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at java.lang.reflect.Method.invoke(Method.java:525)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1187)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
04-03 13:12:22.045: E/AndroidRuntime(13042):    at dalvik.system.NativeStart.main(Native Method)

我正在关注这个:https://developers.google.com/games/services/android/realtimeMultiplayer。这是我的相关代码(我删除了导入):

public class MainActivity extends Activity implements ConnectionCallbacks, OnConnectionFailedListener, OnClickListener 

private static final int RC_SIGN_IN = 0;
private GoogleApiClient mGoogleApiClient;   
private boolean mIntentInProgress;

private boolean mSignInClicked;
private ConnectionResult mConnectionResult;

public void onCreate(Bundle savedInstanceState)

    requestWindowFeature(Window.FEATURE_NO_TITLE);
    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(this)
        .addOnConnectionFailedListener(this)
        .addApi(Plus.API, null)
        .addScope(Plus.SCOPE_PLUS_LOGIN)
        .build();

    findViewById(R.id.sign_in_button).setOnClickListener(this);
    findViewById(R.id.sign_out_button).setOnClickListener(this);
    findViewById(R.id.goButton).setOnClickListener(this);


public GoogleApiClient getApiClient()

    return mGoogleApiClient;


private void resolveSignInError()

    if (mConnectionResult.hasResolution())
    
        try
        
            mIntentInProgress = true;
            mConnectionResult.startResolutionForResult(this, RC_SIGN_IN);
        
        catch (SendIntentException e)
        
            mIntentInProgress = false;
            mGoogleApiClient.connect();
        
    


protected void onStart()

    super.onStart(); 
    mGoogleApiClient.connect();


protected void onStop()

    super.onStop();

    if (mGoogleApiClient.isConnected())
    
        mGoogleApiClient.disconnect();
    


@Override
public void onConnectionFailed(ConnectionResult result) 

    if (!mIntentInProgress)
    
        mConnectionResult = result;

        if (mSignInClicked)
        
            resolveSignInError();
        
       


protected void onActivityResult(int requestCode, int responseCode, Intent intent)

    if (requestCode == RC_SIGN_IN)
    
        if (requestCode == RC_SIGN_IN)
        
            if (responseCode != RESULT_OK)
            
                mSignInClicked = false;
            
        

        mIntentInProgress = false;

        if (!mGoogleApiClient.isConnecting())
        
            mGoogleApiClient.connect();
        
    


@Override
public void onConnected(Bundle connectionHint)

    mSignInClicked = false;

    if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null)
    
        Person currentPerson = Plus.PeopleApi.getCurrentPerson(mGoogleApiClient);
        String personName = currentPerson.getDisplayName();
        Toast.makeText(this, "User if connected! Welcome " + personName +"!", Toast.LENGTH_LONG).show();
    


@Override
public void onConnectionSuspended(int cause) 

    mGoogleApiClient.connect();


@Override
public void onClick(View v) 

    if (v.getId() == R.id.sign_in_button)
    
        findViewById(R.id.sign_in_button).setVisibility(View.GONE);
        findViewById(R.id.sign_out_button).setVisibility(View.VISIBLE);
    

    if (v.getId() == R.id.sign_in_button
            && !mGoogleApiClient.isConnecting())
    
        mSignInClicked = true;
        resolveSignInError();
    

    if (v.getId() == R.id.sign_out_button)
    
        if (mGoogleApiClient.isConnected())
        
            Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
            mGoogleApiClient.disconnect();
            mGoogleApiClient.connect();
            findViewById(R.id.sign_in_button).setVisibility(View.VISIBLE);
            findViewById(R.id.sign_out_button).setVisibility(View.GONE);
        
    

    if (v.getId() == R.id.goButton)
    
        goToTitleScreen();
    



@Override
protected void onResume() 
    super.onResume();


@Override
protected void onPause() 
    super.onPause();


public void goToTitleScreen() 
    Intent intent = new Intent(this, TitleScreen.class);
    startActivity(intent);
    finish();

    

还有我的多人游戏大厅:

public class MultiplayerLobbyView extends MainActivity implements RoomUpdateListener, RoomStatusUpdateListener, RealTimeMessageReceivedListener

        final static int RC_SELECT_PLAYERS = 10000;
        final static int RC_WAITING_ROOM = 10002;
        boolean mPlaying = false;
        final static int MIN_PLAYERS = 2;

@Override
public void onCreate(Bundle savedInstanceState)

     requestWindowFeature(Window.FEATURE_NO_TITLE);
     setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

    super.onCreate(savedInstanceState);
    setContentView(R.layout.multiplayer_lobby);

    findViewById(R.id.quickGame).setOnClickListener(this);
    findViewById(R.id.invitePlayers).setOnClickListener(this);


private RoomConfig.Builder makeBasicRoomConfigBuilder()

    RoomConfig.Builder builder = RoomConfig.builder(this);
    builder.setRoomStatusUpdateListener(this);
    builder.setMessageReceivedListener(this);
    return builder;


boolean shouldStartGame(Room room)

    int connectedPlayers = 0;

    for (Participant p : room.getParticipants())
    
        if (p.isConnectedToRoom())
        
            ++connectedPlayers;
        
    

    return (connectedPlayers >= MIN_PLAYERS);


private void startQuickGame() 

    // auto-match criteria to invite one random automatch opponent.  
    // You can also specify more opponents (up to 3). 
    Bundle am = RoomConfig.createAutoMatchCriteria(1, 1, 0);

    // build the room config:
    RoomConfig.Builder roomConfigBuilder = makeBasicRoomConfigBuilder();
    roomConfigBuilder.setAutoMatchCriteria(am);
    RoomConfig roomConfig = roomConfigBuilder.build();

    // create room:
    Games.RealTimeMultiplayer.create(getApiClient(), roomConfig);

    // prevent screen from sleeping during handshake
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    // go to game screen


private void startInviteGame()


    GoogleApiClient mClient = getApiClient();


    Intent intent = Games.RealTimeMultiplayer.getSelectOpponentsIntent(mClient, 1, 4, true);

    try
    
        startActivityForResult(intent, RC_SELECT_PLAYERS);
    catch (NullPointerException e)
    
        System.out.println("Error.");
    


public void onActivityResult(int request, int response, Intent data) 

    if (request == RC_SELECT_PLAYERS) 
    
        if (response != Activity.RESULT_OK) 
        
            // user canceled
            return;
        

        // get the invitee list
        Bundle extras = data.getExtras();
        final ArrayList<String> invitees =
            data.getStringArrayListExtra(Multiplayer.EXTRA_INVITATION);

        // get auto-match criteria
        Bundle autoMatchCriteria = null;
        int minAutoMatchPlayers =
            data.getIntExtra(Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
        int maxAutoMatchPlayers =
            data.getIntExtra(Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);

        if (minAutoMatchPlayers > 0) 
        
            autoMatchCriteria =
                RoomConfig.createAutoMatchCriteria(
                    minAutoMatchPlayers, maxAutoMatchPlayers, 0);
         else 
        
            autoMatchCriteria = null;
        

        startQuickGame();
    


@Override
public void onConnectedToRoom(Room room) 
    // TODO Auto-generated method stub



@Override
public void onDisconnectedFromRoom(Room room) 





@Override
public void onP2PConnected(String participantId) 
    // TODO Auto-generated method stub



@Override
public void onP2PDisconnected(String participantId) 
    // TODO Auto-generated method stub



@Override
public void onPeerDeclined(Room room, List<String> peers) 

    if (!mPlaying && shouldCancelGame(room))
    
        Games.RealTimeMultiplayer.leave(getApiClient(), null, room.getRoomId());
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    



private boolean shouldCancelGame(Room room) 
    // TODO Auto-generated method stub
    return false;


@Override
public void onPeerInvitedToRoom(Room arg0, List<String> arg1) 
    // TODO Auto-generated method stub



@Override
public void onPeerJoined(Room arg0, List<String> arg1) 
    // TODO Auto-generated method stub



@Override
public void onPeerLeft(Room room, List<String> peers) 

    if (!mPlaying && shouldCancelGame(room))
    
        Games.RealTimeMultiplayer.leave(getApiClient(),null, room.getRoomId());
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    



@Override
public void onPeersConnected(Room room, List<String> peers) 

    if (mPlaying)
    
        String mNewParticipant = new String();
        room.getParticipantIds().add(mNewParticipant);
    
    else if (shouldStartGame(room))
    
        //start
    



@Override
public void onPeersDisconnected(Room arg0, List<String> arg1) 
    // TODO Auto-generated method stub



@Override
public void onRoomAutoMatching(Room room) 
    // TODO Auto-generated method stub



@Override
public void onRoomConnecting(Room room) 
    // TODO Auto-generated method stub



@Override
public void onJoinedRoom(int statusCode, Room room) 

    if (statusCode != GamesStatusCodes.STATUS_OK)
    
        Toast.makeText(this, "Error!", Toast.LENGTH_LONG).show();
        return;
    

    Intent i = Games.RealTimeMultiplayer.getWaitingRoomIntent(getApiClient(), room, Integer.MAX_VALUE);
    startActivityForResult(i, RC_WAITING_ROOM);


@Override
public void onLeftRoom(int statusCode, String roomId) 
    // TODO Auto-generated method stub



@Override
public void onRoomConnected(int statusCode, Room room) 

     if (statusCode != GamesStatusCodes.STATUS_OK) 
     
            // let screen go to sleep
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

            // show error message, return to main screen.
            Toast.makeText(this, "Error!", Toast.LENGTH_LONG).show();
            goToTitleScreen();
     


@Override
public void onRoomCreated(int statusCode, Room room) 

    if (statusCode != GamesStatusCodes.STATUS_OK) 
    
        Toast.makeText(this, "Error!", Toast.LENGTH_LONG).show();
        return;
    

    // get waiting room intent
    Intent i = Games.RealTimeMultiplayer.getWaitingRoomIntent(getApiClient(), room, Integer.MAX_VALUE);
    startActivityForResult(i, RC_WAITING_ROOM);


@Override
public void onClick(View v) 

    if (v.getId() == R.id.quickGame)
    
        startQuickGame();
    
    else if (v.getId() == R.id.invitePlayers)
    
        startInviteGame();
    



@Override
public void onRealTimeMessageReceived(RealTimeMessage message) 
    // TODO Auto-generated method stub


我发现抛出异常的方法是:

private void startInviteGame()


    GoogleApiClient mClient = getApiClient();


    Intent intent = Games.RealTimeMultiplayer.getSelectOpponentsIntent(mClient, 1, 4, true);

    try
    
        startActivityForResult(intent, RC_SELECT_PLAYERS);
    catch (NullPointerException e)
    
        System.out.println("Error.");
    

我的 GoogleApiClient 是否正确?还是我缺少什么?我认为 MainActivity 中的 getter 足以抓住客户端。

【问题讨论】:

【参考方案1】:

我在一分钟前正在玩耍并遇到了同样的错误。这对我有用。查看Accessing the Developer APIs 页面。重要的部分是:

@Override
public void onCreate(Bundle savedInstanceState) 
    // set requested clients (games and cloud save)
    setRequestedClients(BaseGameActivity.CLIENT_GAMES |        
            BaseGameActivity.CLIENT_APPSTATE);
…

标志 BaseGameActivity.CLIENT_GAMES | BaseGameActivity.CLIENT_APPSTATE 请求 Play Games 和 Cloud Save API。

如果您要扩展 BaseGameActivity 类,还有一个不同的选项可以得到相同的东西。在CollectAllTheStars sample 中,他们将请求的 API 传递给 BaseGameActivity 构造函数:

   public MainActivity() 
        // request that superclass initialize and manage the AppStateClient for us
        super(BaseGameActivity.CLIENT_APPSTATE);
    

此游戏仅使用云保存,因此它只有 CLIENT_APPSTATE 标志。

【讨论】:

以上是关于Android:尝试使用 Google Play 服务启动默认播放器选择器大厅,遇到 NullPointerException的主要内容,如果未能解决你的问题,请参考以下文章

无法解决:com.google.android.support.gms:play-services-map:10.2.0

第一次尝试将应用上传到 Google Play 时出现错误“您的 Android App Bundle 使用错误的密钥签名。”

android google play 库使用过多的 CPU 使用率

Google Play Android 应用内部测试 - 内部测试人员未显示更新按钮

Google play 游戏服务邀请丢失

Android Google Play 订阅不自动更新