如何持续向Firebase发送数据,避免进入Doze模式和应用待机模式。

huangapple go评论79阅读模式
英文:

How to send data to Firebase continuously avoiding Doze and App Standby

问题

我开发了这个应用程序,需要在用户按下按钮时将数据发送到Firestore,并在用户停止时结束发送(通过按下另一个按钮)。即使用户在做其他事情,甚至如果用户将手机待机数小时(因此我需要避免Doze和应用待机),数据也必须被发送。

我使用了一个服务来实现这一点,只有一件事似乎做得不对。

服务

public class MyService extends Service {
    // ... 代码部分略去 ...

    private Runnable runnableService = new Runnable() {
        @Override
        public void run() {
            class GetDataTask extends AsyncTask<String, Void, List<Data>> {
                @Override
                protected List<Data> doInBackground(String... v) {
                    // ... 代码部分略去 ...
                    documentReference.set(data).addOnSuccessListener(new OnSuccessListener<Void>() {
                        @Override
                        public void onSuccess(Void aVoid) {
                            Log.i("data", "data added.\n");
                        }
                    });
                    // ... 代码部分略去 ...
                }
            }

            // ... 代码部分略去 ...
        }
    };

    // ... 代码部分略去 ...

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // ... 代码部分略去 ...
        handler = new Handler();
        handler.post(runnableService);
        return START_NOT_STICKY;
    }

    // ... 代码部分略去 ...
}

在 MainActivity 中启动服务的方式如下:

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

如果我不使用手机,让应用在后台发送数据,数据直到我再次打开应用才会被发送到Firebase。服务并未停止,我只需打开应用即可将数据发送到Firebase!我阅读了有关Firebase Cloud Messaging的内容,但我不明白是否需要它们来实现我的目的。我做错了什么?

英文:

I developed this app that needs to send data to Firestore when user press a button and ends when user stop it (by pressing another button). The data must be sent even if the user is doing other things or even if the user leavs the phone in standby for hours (so i need to avoid Doze and App Standby).

I used a service to achieve this and just one thing seems to work in the wrong way.

Service

public class MyService extends Service {

    public static final String CHANNEL_ID = &quot;ForegroundServiceChannel&quot;;

    // 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&lt;String, Void, List&lt;Data&gt;&gt; {

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

                @SuppressLint({&quot;MissingPermission&quot;, &quot;HardwareIds&quot;})
                @Override
                protected List&lt;Data&gt; 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(&quot;data&quot;).document();
						Map&lt;String, Object&gt; data = new HashMap&lt;&gt;();
						data.put(&quot;lat&quot;, myPos.getLat());
						data.put(&quot;date&quot;, myPos.getDate().toString());
						documentReference.set(data).addOnSuccessListener(new OnSuccessListener&lt;Void&gt;() {
							@Override
							public void onSuccess(Void aVoid) {
								Log.i(&quot;data&quot;, &quot;data added.\n&quot;);
							}
						});

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

                    positionsList.add(myPos);
                    return positionsList;
                }

                protected void onPostExecute(List&lt;Data&gt; 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(&quot;inputExtra&quot;);
        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(&quot;Foreground Service&quot;)
                .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, &quot;Service Stopped&quot;, Toast.LENGTH_LONG).show();
        Log.d(&quot;show&quot;, &quot;onDestroy&quot;);
        handler.removeCallbacks(runnableService);
        stopSelf();
    }

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

}

In MainActivity starting the service like this:

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

If I don't use the phone and let the app sending data in background, the data are not sent to Firebase until I open the app back again. The service is not stopped, I just need to open the app in order to send data to firebase!
I read about Firebase Cloud Messaging but I didn't understand if I need them for my purpose. What am I doing wrong?

答案1

得分: 1

数据必须在用户进行其他操作甚至将手机处于待机状态数小时后发送(因此我需要避免Doze和应用待机)。

一般来说,在Doze或应用待机模式下运行应用进程并不是一个好主意。Android文档甚至指出了“网络访问已暂停”;因此,您的进程可能无法可靠运行或优先级较高的其他应用可能会终止该进程。

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

根据文档,“Cloud Firestore支持离线数据持久性。此功能缓存了您的应用程序正在活动使用的Cloud Firestore数据的副本,因此在设备脱机时,您的应用程序可以访问数据。您可以编写、读取、监听和查询缓存数据。当设备重新联机时,Cloud Firestore会将应用程序所做的任何本地更改与Cloud Firestore后端进行同步。”

服务并未停止,我只需要打开应用程序以便将数据发送到Firebase!我阅读了有关Firebase Cloud Messaging的内容,但我不确定是否需要它们来实现我的目的。

一个推荐的解决方案是,在客户端应用进入空闲模式时,使用_Firebase Cloud Messaging_从服务器向您的应用发送消息。当您需要向后端服务器发送实时下行消息或仅通知客户端应用有新数据可用于同步时(这可能是您寻找的内容),此功能非常有用。

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

英文:

> The data must be sent even if the user is doing other things or even if the user leavs the phone in standby for hours (so i need to avoid Doze and App Standby).

Generally speaking, it is not a good idea to have your app running processes when in Doze or App Standby mode. The Android documentation even points out that Network access is suspended; and therefore, your process might not be guaranteed to run reliably or terminate over other apps that may have a higher priority.

> The problem is that if I don't use app for like 1 hour (so phone is in standby) data on firebase are only added when I open the app again. It's like they are saved in cache and sent to DB when app is opened again.

According to the documentation, "Cloud Firestore supports offline data persistence. This feature caches a copy of the Cloud Firestore data that your app is actively using, so your app can access the data when the device is offline. You can write, read, listen to, and query the cached data. When the device comes back online, Cloud Firestore synchronizes any local changes made by your app to the Cloud Firestore backend."

>The service is not stopped, I just need to open the app in order to send data to firebase! I read about Firebase Cloud Messaging but I didn't understand if I need them for my purpose.

A recommended solution would to ping your app from your server using Firebase Cloud Messaging when your client app goes into idle mode. This feature is useful when you need to send real-time downstream messages to your backend server or simply notify your client app that new data is available to sync (which may be what you're looking for).

You can refer to the above documentation for further details.

答案2

得分: 0

在每个平台或语言中,连接到 Firebase 都有不同的方式,所以关于平台的更多信息可能会有所帮助。但你可以查看这个链接,也许它可以帮助你 -> 链接

英文:

in every platform or language, there is a deferent way to connect to the firebase, so, please more information about the platform can help ??,
but you can check this link maybe his can to help you -> link

huangapple
  • 本文由 发表于 2020年8月24日 15:53:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/63556845.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定