安卓App首次启动Firebase数据读取慢致SharedPreferences取空值咨询
嘿,我完全懂你碰到的这个坑——Firebase的数据库操作是异步执行的,这就导致你在onCreate/onStart里直接读取SharedPreferences时,Firebase还没完成数据拉取和写入操作,自然拿到的都是默认的null。下面给你几个实用的解决方案:
方案1:用启动页(Splash Activity)做预加载
最稳妥的方式是先做一个启动页,在这个页面里完成所有Firebase数据的读取和SharedPreferences写入,确认数据准备好后再跳转到目标Activity。这样目标Activity启动时,数据已经稳稳存在SharedPreferences里了。
示例代码(SplashActivity):
public class SplashActivity extends AppCompatActivity { private FirebaseDatabase db; private DatabaseReference dbRef; private FirebaseAuth mAuth; private SharedPreferences sf; private static final String SHARED_PREF = "MyPrefs"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); // 初始化实例 mAuth = FirebaseAuth.getInstance(); db = FirebaseDatabase.getInstance(); dbRef = db.getReference().child("users"); sf = getSharedPreferences(SHARED_PREF, MODE_PRIVATE); // 执行Firebase查询 dbRef.orderByChild("email").equalTo(mAuth.getCurrentUser().getEmail()) .addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { if (snapshot.exists()) { // 因为email是唯一的,直接取第一个子节点(不用循环) DataSnapshot ds = snapshot.getChildren().iterator().next(); SharedPreferences.Editor editor = sf.edit(); editor.putString("phone", ds.child("phone").getValue(String.class)); editor.putString("email", ds.child("email").getValue(String.class)); editor.putString("fullName", ds.child("fName").getValue(String.class)); editor.putString("lastName", ds.child("lName").getValue(String.class)); editor.apply(); // 数据写入完成,跳转到目标Activity Intent intent = new Intent(SplashActivity.this, YourTargetActivity.class); startActivity(intent); finish(); } else { // 处理用户不存在的情况,比如跳转到注册页 Intent intent = new Intent(SplashActivity.this, RegisterActivity.class); startActivity(intent); finish(); } } @Override public void onCancelled(@NonNull DatabaseError error) { // 处理查询失败的情况,比如提示用户并退出或重试 Toast.makeText(SplashActivity.this, "数据加载失败:" + error.getMessage(), Toast.LENGTH_SHORT).show(); finish(); } }); } }
方案2:在当前Activity中延迟UI更新,等待数据加载完成
如果不想加启动页,可以把读取SharedPreferences并更新UI的逻辑,移到Firebase的onDataChange回调里。这样只有当数据写入SharedPreferences后,才会执行UI更新操作,避免拿到空值。
修改你的目标Activity代码:
// 把原来在onCreate里的name拼接逻辑删掉,换成下面的写法 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.your_layout); sf = getSharedPreferences(SHARED_PREF, MODE_PRIVATE); mAuth = FirebaseAuth.getInstance(); dbRef = FirebaseDatabase.getInstance().getReference().child("users"); // 执行Firebase查询 dbRef.orderByChild("email").equalTo(mAuth.getCurrentUser().getEmail()) .addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(@NonNull DataSnapshot snapshot) { if (snapshot.exists()) { DataSnapshot ds = snapshot.getChildren().iterator().next(); SharedPreferences.Editor editor = sf.edit(); editor.putString("phone", ds.child("phone").getValue(String.class)); editor.putString("email", ds.child("email").getValue(String.class)); editor.putString("fullName", ds.child("fName").getValue(String.class)); editor.putString("lastName", ds.child("lName").getValue(String.class)); editor.apply(); // 现在数据已经写入,再读取并更新UI String name = sf.getString("fullName", "null") + " " + sf.getString("lastName", "null"); // 比如设置TextView的文本 ((TextView) findViewById(R.id.tv_name)).setText(name); } } @Override public void onCancelled(@NonNull DatabaseError error) { // 处理错误 } }); }
额外优化:简化Firebase查询逻辑
你的查询是按email匹配,而email通常是唯一的,所以snapshot.getChildren()只会有一个节点,完全不需要用for循环遍历,直接取第一个子节点就行,这样代码更高效简洁(上面的示例已经做了这个优化)。
为什么原来的方法会失效?
再给你理清楚根源:Firebase的addListenerForSingleValueEvent是异步操作,它会在后台线程去拉取数据库数据,而你的onCreate方法是同步执行的——当你在onCreate里读取SharedPreferences时,onDataChange回调还没触发,SharedPreferences里根本还没写入数据,所以拿到的都是默认的null。
内容的提问来源于stack exchange,提问作者Golu

