首页 > 解决方案 > 如何连续向 Firebase 发送数据,避免 Doze 和 App Standby

问题描述

我开发了这个应用程序,它需要在用户按下按钮时向 Firestore 发送数据,并在用户停止它时结束(通过按下另一个按钮)。即使用户正在做其他事情,或者即使用户让手机处于待机状态数小时,也必须发送数据(所以我需要避免打瞌睡和应用待机)。

我使用了一项服务来实现这一点,但只有一件事似乎以错误的方式工作。

服务

public class MyService extends Service {

    public static final String CHANNEL_ID = "ForegroundServiceChannel";

    // vars declaration

    private Date date = null;

    // get every X seconds 
    public static final long DEFAULT_SYNC_INTERVAL = 60000;

    private Runnable runnableService = new Runnable() {
        @Override
        public void run() {

            class GetDataTask extends AsyncTask<String, Void, List<Data>> {

                @Override
                protected void onPreExecute() {
                    super.onPreExecute();
                    if(getPositionData.isCancelled()){
                        return;
                    }
                }

                @SuppressLint({"MissingPermission", "HardwareIds"})
                @Override
                protected List<Data> doInBackground(String... v) {

                    // skipping get Timestamp code
                    

                    // skipping get position code
                    
                        myPos = new Data(position.getId(), latitude, longitude, timestamp);

                        // Insert data into Firebase
                        documentReference = firebaseFirestore.collection("data").document();
                        Map<String, Object> data = new HashMap<>();
                        data.put("lat", myPos.getLat());
                        data.put("date", myPos.getDate().toString());
                        documentReference.set(data).addOnSuccessListener(new OnSuccessListener<Void>() {
                            @Override
                            public void onSuccess(Void aVoid) {
                                Log.i("data", "data added.\n");
                            }
                        });

                        Toast.makeText(DataPollService.this, "" +
                                "ID: " + myPos.getImei()
                                        + " Latitude: " + myPos.getLat()
                                        + " Longitude " + myPos.getLng()
                                        + " Date: " + myPos.getDate()
                                , Toast.LENGTH_SHORT).show();
                                    }
                                }
                            }, Looper.getMainLooper());

                    positionsList.add(myPos);
                    return positionsList;
                }

                protected void onPostExecute(List<Data> result) {
                    Position.getInstance().setPositions(result);
                }
            }


            // RUN TASK
            getPositionData = new GetDataTask();
            getPositionData.execute(position.getId());

            handler.postDelayed(runnableService, DEFAULT_SYNC_INTERVAL);
        }
    };

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        String input = intent.getStringExtra("inputExtra");
        createNotificationChannel();
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,
                0, notificationIntent, 0);
        Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setContentTitle("Foreground Service")
                .setContentText(input)
                .setSmallIcon(R.drawable.ksurf)
                .setContentIntent(pendingIntent)
                .build();
        startForeground(1, notification);

        handler = new Handler();
        handler.post(runnableService);

        return START_NOT_STICKY;
    }

    // onBind
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        Toast.makeText(this, "Service Stopped", Toast.LENGTH_LONG).show();
        Log.d("show", "onDestroy");
        handler.removeCallbacks(runnableService);
        stopSelf();
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel serviceChannel = new NotificationChannel(
                    CHANNEL_ID,
                    "Foreground Service Channel",
                    NotificationManager.IMPORTANCE_DEFAULT
            );
            NotificationManager manager = getSystemService(NotificationManager.class);
            manager.createNotificationChannel(serviceChannel);
        }
    }

}

在 MainActivity 中像这样启动服务:

Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.putExtra("inputExtra", "run");
ContextCompat.startForegroundService(this, serviceIntent);

如果我不使用手机并让应用程序在后台发送数据,则数据不会发送到 Firebase,直到我再次打开应用程序。服务没有停止,我只需要打开应用程序就可以将数据发送到 Firebase!我阅读了有关 Firebase Cloud Messaging 的信息,但我不明白我是否需要它们来达到我的目的。我究竟做错了什么?

标签: javaandroidfirebasegoogle-cloud-firestoreandroid-service

解决方案


即使用户正在做其他事情,或者即使用户让手机处于待机状态数小时,也必须发送数据(所以我需要避免打瞌睡和应用待机)。

一般来说,在打盹或应用待机模式下让应用运行进程并不是一个好主意。Android 文档甚至指出网络访问被暂停;因此,您的进程可能无法保证可靠运行或终止于可能具有更高优先级的其他应用程序。

问题是,如果我在 1 小时内不使用应用程序(因此手机处于待机状态),则只有在我再次打开应用程序时才会添加 firebase 上的数据。就像它们被保存在缓存中并在再次打开应用程序时发送到数据库一样。

根据文档“Cloud Firestore 支持离线数据持久化。此功能缓存您的应用程序正在使用的 Cloud Firestore 数据的副本,因此您的应用程序可以在设备离线时访问数据。您可以写、读、听到,并查询缓存的数据。当设备重新上线时,Cloud Firestore 会将您的应用所做的任何本地更改同步到 Cloud Firestore 后端。

服务没有停止,我只需要打开应用程序就可以将数据发送到 Firebase!我阅读了有关 Firebase Cloud Messaging 的信息,但我不明白我是否需要它们来达到我的目的。

当您的客户端应用程序进入空闲模式时,推荐的解决方案是使用Firebase Cloud Messaging从您的服务器 ping 您的应用程序。当您需要将实时下游消息发送到后端服务器或只是通知您的客户端应用程序有新数据可供同步(这可能是您正在寻找的)时,此功能很有用。

您可以参考上述文档以获取更多详细信息。


推荐阅读