如何从FireStore取数据转自定义对象并返回?及返回值为空问题
问题1:如何将FireStore中的数据转换为自定义对象并返回该对象?
FireStore提供了非常便捷的方式来将文档快照转换为自定义对象,主要有两种常用方法:
方法1:使用FireStore内置的toObject()方法
这是最推荐的方式,前提是你的自定义类满足两个条件:
- 拥有无参构造函数(FireStore需要通过反射创建实例)
- 类的字段名称和FireStore文档中的字段名称一致(如果不一致,可以用
@PropertyName注解指定映射关系)
举个例子,假设你的自定义类是Vehicle:
public class Vehicle { // 无参构造必须要有 public Vehicle() {} // 和FireStore文档对应的字段 public String id; public String model; public int year; // 可选:带参数的构造函数,方便手动创建对象 public Vehicle(String id, String model, int year) { this.id = id; this.model = model; this.year = year; } }
然后在获取文档的回调中,直接调用toObject()转换:
DocumentReference docRef = db.collection("vehicles").document("123"); docRef.get().addOnCompleteListener(task -> { if (task.isSuccessful()) { DocumentSnapshot document = task.getResult(); if (document.exists()) { // 直接转换为Vehicle对象 Vehicle vehicle = document.toObject(Vehicle.class); // 这里就可以使用这个vehicle对象了 System.out.println("Vehicle model: " + vehicle.model); } else { System.out.println("No such document!"); } } else { System.out.println("Get failed with ", task.getException()); } });
如果字段名不一致,比如FireStore里是car_model,而类里是model,可以用注解:
@PropertyName("car_model") public String model;
方法2:手动映射字段
如果需要更精细的控制(比如字段类型转换、额外处理),可以手动读取每个字段并赋值:
if (document.exists()) { Vehicle vehicle = new Vehicle(); vehicle.id = document.getId(); vehicle.model = document.getString("model"); vehicle.year = document.getLong("year").intValue(); // FireStore存数字默认是Long,转成int // 其他字段同理 }
问题2:运行指定方法时静态变量v返回null,但回调内部v并非null
这个问题的核心是FireStore的get()方法是异步执行的,你的tessst()方法会在回调onComplete执行完毕之前就返回,这时候静态变量v还没被赋值,所以拿到的是null。
为什么会这样?
代码的执行顺序是这样的:
- 你调用
tessst(),创建了一个空的Vehicle对象赋值给v - 调用
docRef.get(),这个方法会立刻返回,然后后台线程去请求FireStore数据 tessst()方法执行完毕,返回(这时候回调还没触发,v还是最初的空对象,或者如果回调里才给v赋值的话,还是null)- 一段时间后,FireStore返回数据,
onComplete回调触发,这时候才给v赋值,但方法已经返回了
怎么解决?
不能用同步返回的方式获取异步数据,推荐用以下几种方案:
方案1:使用自定义回调接口
定义一个回调接口,在数据准备好的时候通知调用方:
// 定义回调接口 public interface VehicleCallback { void onVehicleLoaded(Vehicle vehicle); void onFailure(Exception e); } // 修改你的方法,接收回调参数 public static void getVehicle(VehicleCallback callback) { DocumentReference docRef = db.collection("vehicles").document("123"); docRef.get().addOnCompleteListener(task -> { if (task.isSuccessful()) { DocumentSnapshot document = task.getResult(); if (document.exists()) { Vehicle vehicle = document.toObject(Vehicle.class); callback.onVehicleLoaded(vehicle); // 数据准备好,通知回调 } else { callback.onFailure(new Exception("No such document")); } } else { callback.onFailure(task.getException()); } }); } // 使用方式 getVehicle(new VehicleCallback() { @Override public void onVehicleLoaded(Vehicle vehicle) { // 这里可以拿到非null的vehicle对象 System.out.println("Loaded vehicle: " + vehicle.model); } @Override public void onFailure(Exception e) { e.printStackTrace(); } });
方案2:使用CompletableFuture(Java 8+)
如果你的项目支持Java 8,可以用CompletableFuture来包装异步操作,更优雅地处理结果:
public static CompletableFuture<Vehicle> getVehicle() { CompletableFuture<Vehicle> future = new CompletableFuture<>(); DocumentReference docRef = db.collection("vehicles").document("123"); docRef.get().addOnCompleteListener(task -> { if (task.isSuccessful()) { DocumentSnapshot document = task.getResult(); if (document.exists()) { Vehicle vehicle = document.toObject(Vehicle.class); future.complete(vehicle); } else { future.completeExceptionally(new Exception("No such document")); } } else { future.completeExceptionally(task.getException()); } }); return future; } // 使用方式 getVehicle().thenAccept(vehicle -> { // 处理非null的vehicle System.out.println("Vehicle year: " + vehicle.year); }).exceptionally(e -> { e.printStackTrace(); return null; });
方案3:Android平台可以用LiveData
如果你是在Android项目中,可以用LiveData来观察数据变化,自动通知UI更新:
public static LiveData<Vehicle> getVehicleLiveData() { MutableLiveData<Vehicle> liveData = new MutableLiveData<>(); DocumentReference docRef = db.collection("vehicles").document("123"); docRef.get().addOnCompleteListener(task -> { if (task.isSuccessful()) { DocumentSnapshot document = task.getResult(); if (document.exists()) { liveData.setValue(document.toObject(Vehicle.class)); } } }); return liveData; } // 在Activity/Fragment中观察 getVehicleLiveData().observe(this, vehicle -> { if (vehicle != null) { // 更新UI } });
注意:永远不要试图用Thread.sleep()等方式阻塞主线程等待异步结果,这会导致ANR(Android)或者程序卡顿,是非常糟糕的实践。
内容的提问来源于stack exchange,提问作者isme
相关产品推荐
相关产品推荐

