Android启动页需完成Firebase同步后再启动主页面求助
问题分析
你遇到的核心问题是Firebase的异步操作和AsyncTask的执行逻辑冲突:
- 你在
AsyncTask的doInBackground里添加了addValueEventListener,但Firebase的数据库回调(onDataChange)和存储下载(getFile的OnSuccessListener)都是异步执行的。 doInBackground会在添加监听器后立刻返回null,直接触发onPostExecute跳转到MainActivity,这时候数据库同步和音频下载其实还在后台跑,所以MainActivity启动时本地数据库没有数据。
另外,addValueEventListener会持续监听数据变化,而你只需要启动时同步一次,改用addListenerForSingleValueEvent更合适,避免不必要的实时监听。
解决方案
我们需要跟踪所有同步和下载任务的完成状态,等所有操作都结束后再启动MainActivity。具体步骤如下:
1. 移除AsyncTask,直接在SplashScreen中处理Firebase操作
Firebase本身就是异步设计,不需要额外用AsyncTask包裹,反而会导致逻辑混乱。
2. 使用addListenerForSingleValueEvent替代addValueEventListener
只在启动时同步一次数据,避免持续监听浪费资源。
3. 跟踪所有音频下载任务的完成情况
用一个计数器记录需要下载的音频数量,每完成一个下载就递减计数器,当计数器归0时,再启动MainActivity。
修改后的完整代码
public class SplashScreen extends Activity { private DatabaseHandler db; private int pendingDownloads = 0; // 跟踪待完成的下载任务数 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); // 创建存储文件夹 File folder = new File(Environment.getExternalStorageDirectory() + "/" + getString(R.string.folder_name)); if (!folder.exists()) { folder.mkdirs(); } db = new DatabaseHandler(this); startFirebaseSync(); } private void startFirebaseSync() { FirebaseDatabase database = FirebaseDatabase.getInstance(); DatabaseReference dbRef = database.getReference("categorie"); // 单次监听数据变化,同步完成后自动取消监听 dbRef.addListenerForSingleValueEvent(new ValueEventListener() { @Override public void onDataChange(DataSnapshot dataSnapshot) { try { List<String> listCat = db.getAllCategoriesId(); List<String> listSounds = db.getAllSoundsId(); Gson gson = new Gson(); JSONArray jsonArray = new JSONArray(gson.toJson(dataSnapshot.getValue())); // 先同步分类数据 for (int i = 0; i < jsonArray.length(); i++) { JSONObject cat = jsonArray.getJSONObject(i); String nom = cat.getString("nom"); String id = cat.getString("id"); if (!listCat.contains(id)) { db.addCategorie(nom, id); } // 处理音频数据,统计需要下载的数量 JSONArray sounds = cat.getJSONArray("son"); for (int j = 0; j < sounds.length(); j++) { JSONObject sound = sounds.getJSONObject(j); String soundId = sound.getString("id"); if (!listSounds.contains(soundId)) { pendingDownloads++; // 需要下载,计数器+1 downloadAudioAndUpdateDb(sound, id); } } } // 如果没有需要下载的音频,直接跳转 if (pendingDownloads == 0) { navigateToMainActivity(); } } catch (JSONException e) { e.printStackTrace(); // 同步过程出错,也跳转(可以根据需求添加错误提示) navigateToMainActivity(); } } @Override public void onCancelled(DatabaseError databaseError) { databaseError.toException().printStackTrace(); // 数据库访问失败,跳转 navigateToMainActivity(); } }); } private void downloadAudioAndUpdateDb(JSONObject sound, String categoryId) { try { String soundId = sound.getString("id"); String soundName = sound.getString("nom"); String soundLien = sound.getString("lien"); FirebaseStorage storage = FirebaseStorage.getInstance(); StorageReference storageRef = storage.getReferenceFromUrl("gs://soundbox-a6dd8.appspot.com"); StorageReference audioRef = storageRef.child(soundLien + ".mp3"); File rootPath = new File(Environment.getExternalStorageDirectory(), getString(R.string.folder_name)); if (!rootPath.exists()) { rootPath.mkdirs(); } final File localFile = new File(rootPath, soundLien + ".mp3"); audioRef.getFile(localFile) .addOnSuccessListener(taskSnapshot -> { Log.e("firebase", ";local temp file created: " + localFile.toString()); // 下载成功,更新本地数据库 db.addSound(soundId, soundName, soundLien, categoryId); // 计数器递减,检查是否所有任务完成 pendingDownloads--; checkIfAllTasksDone(); }) .addOnFailureListener(exception -> { Log.e("firebase", ";local temp file not created: " + exception.toString()); // 下载失败,计数器也要递减(避免卡住跳转) pendingDownloads--; checkIfAllTasksDone(); }); } catch (JSONException e) { e.printStackTrace(); pendingDownloads--; checkIfAllTasksDone(); } } private void checkIfAllTasksDone() { if (pendingDownloads == 0) { // 所有同步和下载任务完成,跳转MainActivity navigateToMainActivity(); } } private void navigateToMainActivity() { Intent i = new Intent(SplashScreen.this, MainActivity.class); startActivity(i); finish(); } }
关键改进点说明
- 移除AsyncTask:直接利用Firebase的异步回调处理逻辑,避免线程嵌套导致的时序问题。
- 单次数据监听:
addListenerForSingleValueEvent只触发一次数据同步,完成后自动解绑监听器,减少资源消耗。 - 下载任务计数:用
pendingDownloads跟踪所有需要下载的音频,确保所有下载(无论成功失败)都完成后才跳转,避免MainActivity启动时数据不完整。 - 错误处理:在同步或下载出错时,依然保证能跳转到MainActivity,避免应用卡在启动页。
内容的提问来源于stack exchange,提问作者king ramizy
相关产品推荐
相关产品推荐

