基于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
相关产品推荐
相关产品推荐

