Android Firebase实现仅数据库新增数据时触发应用通知
问题根因
当前代码打开应用就触发通知,由三个问题导致:
- Firebase的
addChildEventListener首次绑定节点时,会遍历该路径下所有已存在的子节点,逐个触发onChildAdded回调,并非仅后续新增数据才会触发该回调 - 原有判断逻辑为对比两个全新实例化的空
ArrayList,和当前加载的业务数据完全无关,属于无效判断,无法区分首次全量加载和后续新增数据场景 - 原有
ValueEventListener每次触发回调时直接向listArt追加数据,未清空旧数据,会导致列表数据重复累加
问题1:获取ArrayList元素数量的方法
Java中获取ArrayList类型集合的元素个数,直接调用集合的size()方法即可:
// 获取作业列表的元素总数 int assignmentCount = listArt.size();
注意:普通固定长度数组用length属性获取长度,ArrayList这类动态集合统一用size()方法获取元素数量
问题2:仅真实新增数据时触发通知的实现逻辑
通过「初始加载标记位+列表长度对比」的方案即可实现需求,逻辑如下:
- 新增全局布尔标记位,标识首次全量数据是否加载完成
- 首次全量加载数据时仅渲染列表,不触发通知,加载完成后记录当前列表基准长度、将标记位置为完成状态
- 初始加载完成后,后续
onChildAdded触发时,判断当前列表长度大于基准长度,则确认是真实新增数据,触发通知后同步更新基准长度 - 修复原有列表重复追加数据的bug,数据删除时同步更新基准长度,避免判断失效
修正后的核心代码
public class AssignmentListArt extends AppCompatActivity { private ArrayList<assignmentArt> listArt; private RecyclerView recyclerViewArt; private DatabaseReference databaseArt; private MyAdapterArt myAdapterArt; // 标记初始全量数据是否加载完成 private boolean isInitialLoadDone = false; // 记录列表基准长度,用于对比判断是否有新增数据 private int baseListSize = 0; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_assignment_list_art); recyclerViewArt = findViewById(R.id.assignmentListArt); databaseArt = FirebaseDatabase.getInstance().getReference("yearlevel") .child("1") .child("ART") .child("Assignments"); recyclerViewArt.setHasFixedSize(true); recyclerViewArt.setLayoutManager(new LinearLayoutManager(this)); listArt = new ArrayList<>(); myAdapterArt = new MyAdapterArt(this, listArt); recyclerViewArt.setAdapter(myAdapterArt); // 修正ValueEventListener逻辑,解决列表重复问题 databaseArt.addValueEventListener(new ValueEventListener() { @SuppressLint("NotifyDataSetChanged") @Override public void onDataChange(@NonNull DataSnapshot snapshot) { // 每次回调先清空旧数据,避免列表重复 listArt.clear(); for (DataSnapshot dataSnapshot : snapshot.getChildren()){ assignmentArt item = dataSnapshot.getValue(com.example.timemanagementapp.assignmentArt.class); listArt.add(item); } myAdapterArt.notifyDataSetChanged(); // 首次加载完成后,更新标记位和基准长度 if (!isInitialLoadDone) { isInitialLoadDone = true; baseListSize = listArt.size(); } } @Override public void onCancelled(@NonNull DatabaseError error) { Toast.makeText(AssignmentListArt.this, "数据库加载出错", Toast.LENGTH_SHORT).show(); } }); // 修正ChildEventListener逻辑,仅真实新增时触发通知 databaseArt.addChildEventListener(new ChildEventListener() { @Override public void onChildAdded(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) { // 初始加载阶段不触发通知 if (!isInitialLoadDone) { return; } // 列表长度大于基准长度,判定为真实新增数据 if (listArt.size() > baseListSize) { baseListSize = listArt.size(); notification(); } } @Override public void onChildChanged(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {} @Override public void onChildRemoved(@NonNull DataSnapshot snapshot) { // 数据删除时同步更新基准长度,避免后续判断失效 if (isInitialLoadDone) { baseListSize = listArt.size(); } } @Override public void onChildMoved(@NonNull DataSnapshot snapshot, @Nullable String previousChildName) {} @Override public void onCancelled(@NonNull DatabaseError error) {} }); } private void notification() { if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){ // 通知渠道建议使用有明确含义的命名,方便用户管理通知权限 NotificationChannel channel = new NotificationChannel("assignment_notice", "作业通知", NotificationManager.IMPORTANCE_DEFAULT); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); NotificationCompat.Builder builder = new NotificationCompat.Builder(this, "assignment_notice") .setSmallIcon(R.drawable.ic_baseline_notifications) .setAutoCancel(true) .setContentTitle("新作业通知") .setContentText("ART科目新增了作业,请及时查看"); NotificationManagerCompat managerCompat = NotificationManagerCompat.from(this); managerCompat.notify(999, builder.build()); } } }
额外优化建议
- 类名、变量名建议遵循Java驼峰命名规范,
assignmentArt类建议改名为AssignmentArt,降低后续维护的识别成本 - 如果不需要监听数据修改、删除、移动回调,可以仅用
ChildEventListener实现列表加载+通知触发逻辑,不需要同时注册两个监听器,减少重复的数据库事件回调 - 通知ID建议使用自增ID或和业务数据绑定的唯一ID,避免多条通知互相覆盖
内容的提问来源于stack exchange,提问作者Errie
相关产品推荐
相关产品推荐

