WakefulBroadcastReceiver

WakefulBroadcastReceiver

我的目标

要构建允许用户打开和关闭位置跟踪的应用。启用跟踪后,我想将用户的位置数据推送到Firebase。

我尝试的解决方案

我使用PendingIntent和Google Play服务FusedLocationAPI在后台跟踪位置。在每次位置更新(以30秒为标点)时,PendingIntent都会向WakefulBroadcastReceiver触发一个意图(如果有位置更新)。然后WakefulBroadcastReceiver应该将位置数据保存到Firebase。

这是WakefulBroadcastReceiver的样子:

public class LocationProcessingReceiver extends WakefulBroadcastReceiver {

    private static final String TAG = "LocationProcessingRcv";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (LocationResult.hasResult(intent)) {
            Log.i(this.TAG, "Received location result");

            // Extract location from result and push to server
            Location location = LocationResult.extractResult(intent).getLastLocation();
            this.pushLocationToServer(location, intent.getStringExtra("userId"));
        }
        else {
            Log.i(TAG, "No location result");
        }
    }

    /**
     * Pushes location to Firebase server
     * @param location
     */
    private void pushLocationToServer(Location location, String userId) {

        FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();

        // Data to update the latest status of the user
        Map<String, Object> statusMap = new HashMap<String, Object>();
        // ... various statusMap.put(...) calls go here

        // Get reference to Firebase as well as key at which to update reference
        DatabaseReference dbRef = FirebaseDatabase.getInstance().getReference();

        dbRef.child(DB_LOC_HISTORY_REFERENCE).push().updateChildren(historyMap, new DatabaseReference.CompletionListener() {
            @Override
            public void onComplete(DatabaseError databaseError, DatabaseReference databaseReference) {
                if (databaseError == null) {
                    Log.i(TAG, "Successfully pushed to server");
                }
                else {
                    Log.i(TAG, "Server push failed: " + databaseError.toString());
                }
            }
        });
    }
}


问题

在某些电话上,从不调用onComplete回调(在某些设备上!)。我不知道为什么这不起作用...在某些设备上,回调正常工作。在其他情况下,永远不会收到回调,而且我也永远不会在Firebase中看到更新的数据。

日志显示位置更新工作正常,并且按预期方式定期调用WakefulBroadcastReceiver的onReceive()方法。

我本来希望onComplete被调用,但是如果存在连接问题,则会显示一个错误。但这永远不会发生,我正在努力了解原因。

注意事项:这些设备中的许多都在3G上运行。当我的应用程序在后台运行时,是否有可能因为3G连接速度太慢而在Firebase完成此操作之前杀死了BroadcastReceiver?

最佳答案

该问题可能与BroadcastReceiver的生命周期有关。

该文档说明:


  BroadcastReceiver对象仅在通话期间有效
  到onReceive(Context,Intent)。一旦您的代码从这里返回
  功能,系统认为该对象已完成且不再
  活性。
  
  这对您可以在
  onReceive(Context,Intent)实现:需要的任何东西
  异步操作不可用,因为您将需要
  从函数返回以处理异步操作,但在
  这时BroadcastReceiver不再处于活动状态,因此
  系统在异步操作之前可以随意终止其进程
  完成。


由于FirebaseDatabase操作是在后台线程上异步执行的,因此在某些情况下,系统将在执行DB操作之前销毁LocationProcessingReceiver实例。

您可能认为使用WakefulBroadcastReceiver可以防止接收器对象过早损坏,但事实并非如此。 WakefulBroadcastReceiver旨在用于安全地启动服务,您在此处无需执行此操作。这没有任何好处。请改用BroadcastReceiver

为确保允许完成DB操作,请在pushLocationToServer()中获取一个Wakelock(可能出于安全考虑可能会超时),然后在onComplete()回调中释放它。

09-28 09:27