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

Android Studio中Firestore Timestamp空指针异常问题(跨Activity场景)

嘿,我来帮你搞定这个空指针问题!从你的代码和异常信息来看,问题出在两个关键地方,我给你一步步拆解并提供解决方案:

问题原因分析

这个空指针是因为你调用timestamp.toDate()时,timestamp对象是null,背后的诱因有两个:

  1. 提交逻辑的异步顺序错误:你点击提交按钮后立刻调用了startActivity(intent)跳转回ListView,但Firestore的set()写入操作是异步执行的——APP跳转时,新建议的文档可能还没被Firestore服务器完全写入,尤其是createdAt用的是FieldValue.serverTimestamp(),这个值是服务器端生成的,客户端刚发起写入时拿到的临时快照里,这个字段是null,此时ListView的实时监听到不完整的文档,就会触发空指针。
  2. 缺少Null安全判断:你没有对createdAt字段做任何非空校验,哪怕是旧文档出现数据缺失的情况,也会直接崩溃。

解决方案

1. 修正提交页面的跳转时机

把跳转代码移到Firestore写入成功的回调里,确保数据完全同步到服务器后再回到ListView:

send.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        v.getId();
        String title = art1.getText().toString();
        String content = art2.getText().toString();
        Intent intent = new Intent(create_announce_main.this, SuggestionMain.class);
        Map<String, Object> suggest = new HashMap<>();
        suggest.put("title", title);
        suggest.put("content", content);
        suggest.put("name", name);
        suggest.put("recID", recID);
        suggest.put("admin_name", "");
        suggest.put("response_content", "");
        suggest.put("response_status", "未回覆");
        suggest.put("response_time",FieldValue.serverTimestamp());
        suggest.put("createdAt", FieldValue.serverTimestamp());
        
        firebaseFirestore.collection("Suggestion").document().set(suggest).addOnCompleteListener(new OnCompleteListener<Void>() {
            @Override
            public void onComplete(@NonNull Task<Void> task) {
                if (task.isSuccessful()) {
                    Toast.makeText(create_announce_main.this, "added succesfully", Toast.LENGTH_LONG).show();
                    // 写入成功后再跳转,确保数据完整
                    startActivity(intent);
                    // 关闭当前页面,避免返回时重复提交
                    finish();
                } else {
                    // 处理写入失败的场景,给用户提示
                    Toast.makeText(create_announce_main.this, "提交失败,请重试", Toast.LENGTH_LONG).show();
                }
            }
        });
        // 移除原来的startActivity,不要提前跳转
    }
});

2. 在ListView页面添加Null安全校验

即使遇到文档未完全同步或数据缺失的情况,也能避免崩溃,同时给用户合理的提示:

DocumentReference docRef = firebaseFirestore.collection("users").document(userID);
docRef.get().addOnCompleteListener(new OnCompleteListener<DocumentSnapshot>() {
    @Override
    public void onComplete(@NonNull Task<DocumentSnapshot> task) {
        if (task.isSuccessful()) {
            DocumentSnapshot document = task.getResult();
            if (document.exists()) {
                Log.d("TAG", "DocumentSnapshot data: " + document.getData());
                recID = document.getString("recID");
                firebaseFirestore.collection("Suggestion")
                        .whereEqualTo("recID",recID)
                        .orderBy("createdAt", Query.Direction.DESCENDING)
                        .addSnapshotListener((documentSnapshots, error) -> {
                            // 先处理监听错误
                            if (error != null) {
                                Log.e("TAG", "监听Suggestion集合出错", error);
                                return;
                            }
                            // 校验快照是否为空
                            if (documentSnapshots == null) {
                                Log.w("TAG", "未获取到Suggestion数据");
                                return;
                            }
                            
                            ar.clear();
                            for (DocumentSnapshot snapshot : documentSnapshots){
                                idlv = snapshot.getId();
                                // 安全处理createdAt字段
                                String date2 = "未知时间";
                                Object createdAtObj = snapshot.getData().get("createdAt");
                                if (createdAtObj instanceof Timestamp) {
                                    Timestamp timestamp = (Timestamp) createdAtObj;
                                    Date date = timestamp.toDate();
                                    date2 = date.toString();
                                }
                                
                                // 其他字段也做非空兜底
                                String adminName = snapshot.getString("admin_name") != null ? snapshot.getString("admin_name") : "無";
                                String responseStatus = snapshot.getString("response_status") != null ? snapshot.getString("response_status") : "未回覆";
                                
                                ar.add(new itemAnnounce(R.drawable.notes, 
                                                       snapshot.getString("title"),
                                                       "回饋於 "+date2,
                                                       "回覆管理者:"+ adminName,
                                                       "回覆狀態:"+ responseStatus,
                                                       idlv,
                                                       url));
                            }
                            // 优化:可以把adapter作为成员变量,避免每次重新创建
                            adapterAnnounce adapterAnnounce = new adapterAnnounce(getApplicationContext(), R.layout.list_row_announce, ar);
                            lv1.setAdapter(adapterAnnounce);
                            
                            lv1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
                                @Override
                                public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                                    Object selectedObj =adapterAnnounce.getItem(position).getId();
                                    String obj = selectedObj.toString();
                                    Intent i = new Intent(SuggestionMain.this, announce_Page.class);
                                    i.putExtra("annId",obj);
                                    startActivity(i);
                                }
                            });
                        });
            } else {
                Log.d("TAG", "No such document");
            }
        } else {
            Log.d("TAG", "get failed with ", task.getException());
        }
    }
});

另外还有个小优化:你每次监听触发都重新创建Adapter,其实可以把Adapter设为Activity的成员变量,初始化一次后,每次清空数据直接调用adapter.notifyDataSetChanged(),这样性能会更好。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 18:57:41