You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Firebase的Android多人游戏(Join Code)异常排查求助

问题排查请求

我正在开发一款类似Among Us、通过Join Code实现多人连接的Android游戏,但遇到问题:无报错信息,却在各模拟器上的应用表现不一致。主界面是MainActivity,包含「创建房间」和「加入房间」按钮;LobbyActivity通过Gemini API生成真心话大冒险提示,并显示用户名。已尝试用Toast捕获错误但无效,附上相关代码:


MainActivity代码

import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import androidx.activity.EdgeToEdge;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.FirebaseDatabase;
import com.google.firebase.database.ValueEventListener;

import java.util.ArrayList;
import java.util.Random;

public class MainActivity extends AppCompatActivity {

    Button create_a_room;
    Button join_a_room;
    FirebaseDatabase database;
    DatabaseReference roomRef;
    String enteredcode = "";
    EditText username;
    String roomName;
    String playername;
    ArrayList<String> roomsList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EdgeToEdge.enable(this);
        setContentView(R.layout.activity_main);
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
            Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
            return insets;
        });

        create_a_room = findViewById(R.id.createButton);
        username = findViewById(R.id.userTxt);
        join_a_room = findViewById(R.id.joinButton);

        database = FirebaseDatabase.getInstance();
        SharedPreferences prefs = getSharedPreferences("PREFS", 0);
        playername = prefs.getString("playerName", "");
        roomsList = new ArrayList<>();

        create_a_room.setOnClickListener(v -> {
            if (!username.getText().toString().isEmpty()) {
                playername = username.getText().toString();
                roomName = generateJoinCode();
                roomRef = database.getReference("rooms").child(roomName);
                roomRef.setValue(playername + "'s Room").addOnCompleteListener(task -> {
                    if (task.isSuccessful()) {
                        Toast.makeText(MainActivity.this, "Room created. Join code: " + roomName, Toast.LENGTH_LONG).show();
                        startLobbyActivity();
                    } else {
                        Toast.makeText(MainActivity.this, "Failed to create room", Toast.LENGTH_SHORT).show();
                    }
                });
            } else {
                Toast.makeText(MainActivity.this, "Please enter a username", Toast.LENGTH_SHORT).show();
            }
        });

        join_a_room.setOnClickListener(v -> {
            AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
            final View customLayout = getLayoutInflater().inflate(R.layout.alertsy, null);
            builder.setView(customLayout);

            builder.setMessage("Please enter the join code as shown on the room creator's device.")
                    .setTitle("Enter your code:")
                    .setCancelable(false)
                    .setPositiveButton("Submit", (dialog, which) -> {
                        EditText editText = customLayout.findViewById(R.id.editText);
                        enteredcode = editText.getText().toString();
                        if (!enteredcode.isEmpty() && !username.getText().toString().isEmpty()) {
                            playername = username.getText().toString();
                            roomRef = database.getReference("rooms").child(enteredcode);
                            addRoomEventListener(enteredcode);
                        } else {
                            Toast.makeText(MainActivity.this, "Please enter a join code and username", Toast.LENGTH_SHORT).show();
                        }
                    })
                    .setNegativeButton("Cancel", (dialog, which) -> dialog.cancel());

            AlertDialog alertDialog = builder.create();
            alertDialog.show();
        });
    }

    private void addRoomEventListener(String joinCode) {
        roomRef.addValueEventListener(new ValueEventListener() {
            @Override
            public void onDataChange(@NonNull DataSnapshot snapshot) {
                if (snapshot.exists()) {
                    startLobbyActivity();
                } else {
                    Toast.makeText(MainActivity.this, "Invalid join code", Toast.LENGTH_SHORT).show();
                }
            }

            @Override
            public void onCancelled(@NonNull DatabaseError error) {
                Toast.makeText(MainActivity.this, "Failed to join room", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void startLobbyActivity() {
        Intent intent = new Intent(MainActivity.this, LobbyActivity.class);
        intent.putExtra("playerName", playername);
        intent.putExtra("roomName", roomName);
        startActivity(intent);
        finish();
    }

    private String generateJoinCode() {
        Random random = new Random();
        StringBuilder joinCode = new StringBuilder();
        for (int i = 0; i < 6; i++) {
            joinCode.append((char) (random.nextInt(26) + 'A'));
        }
        return joinCode.toString();
    }
}

LobbyActivity代码

package com.example.mobile_final;

import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.google.ai.client.generativeai.GenerativeModel;
import com.google.ai.client.generativeai.java.GenerativeModelFutures;
import com.google.ai.client.generativeai.type.Content;
import com.google.ai.client.generativeai.type.GenerateContentResponse;
import com.google.common.util.concurrent.FutureCallback;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;

import java.util.concurrent.Executor;


public class LobbyActivity extends AppCompatActivity {

    TextView lobbyTextView;
    String recentResult = "";
    Button next;
    Button swipe;
    Button card;
    String players;

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

        next = findViewById(R.id.next);
        swipe = findViewById(R.id.swipe);
        card = findViewById(R.id.backCard);
        lobbyTextView = findViewById(R.id.userName);

        String playerName = getIntent().getStringExtra("playerName");
        players += playerName + "\n";

        lobbyTextView.setText(players);
        next.setOnClickListener(v -> {
            GenerativeModel gm = new GenerativeModel("gemini-1.5-flash", "API KEY");
            GenerativeModelFutures model = GenerativeModelFutures.from(gm);

            Content content = new Content.Builder()
                    .addText("Generate a new and spicy truth or dare prompt. Only return one prompt.")
                    .build();

            Executor executor = Runnable::run;

            ListenableFuture<GenerateContentResponse> response = model.generateContent(content);
            Futures.addCallback(response, new FutureCallback<GenerateContentResponse>() {
                @Override
                public void onSuccess(GenerateContentResponse result) {
                    String resultText = result.getText();
                    System.out.println(resultText);
                    card.setText(resultText);
                    recentResult = resultText;
                }

                @Override
                public void onFailure(Throwable t) {
                    t.printStackTrace();
                }
            }, executor);

        });

        swipe.setOnClickListener(v -> {

        });
    }
}

核心问题排查与修复方案

1. 多人连接逻辑漏洞

  • 加入房间时roomName未赋值:在join_a_room的点击事件中,调用startLobbyActivity()前需将enteredcode赋值给roomName,否则LobbyActivity接收的房间名称为空:
    // 在addRoomEventListener(enteredcode);前添加
    roomName = enteredcode;
    
  • 重复监听导致多次跳转:addRoomEventListener中使用addValueEventListener会持续监听数据变化,导致房间存在时多次触发跳转,应改为单次监听:
    roomRef.addListenerForSingleValueEvent(new ValueEventListener() {
        // 原有onDataChange、onCancelled逻辑不变
    });
    

2. LobbyActivity初始化问题

  • players变量未初始化:未赋值直接拼接会导致开头出现null字符串,需初始化:
    String players = "";
    
  • 主线程执行网络请求:Executor executor = Runnable::run;会在主线程执行Gemini API请求,触发ANR(应用无响应),需改用后台线程:
    import java.util.concurrent.Executors;
    // ...
    Executor executor = Executors.newSingleThreadExecutor();
    
  • API密钥硬编码:代码中"API KEY"需替换为实际有效密钥,建议通过local.properties配置,避免提交到版本控制。

3. 模拟器表现不一致的潜在原因

  • 网络环境差异:检查各模拟器的网络权限、代理设置,确保均可正常访问Firebase和Gemini API。
  • Firebase数据库规则:开发阶段可临时设置规则为公开(上线后需配置安全规则):
    {
      "rules": {
        ".read": true,
        ".write": true
      }
    }
    
  • SharedPreferences未更新:创建房间时更新的用户名未写入本地存储,重启应用后丢失,需添加:
    // 在create_a_room的点击事件中,playername赋值后添加
    SharedPreferences.Editor editor = prefs.edit();
    editor.putString("playerName", playername);
    editor.apply();
    

内容的提问来源于stack exchange,提问作者Anika Thakur

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 23:34:56