0

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 = "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);
        }
    }

}

In MainActivity starting the service like this:

Intent serviceIntent = new Intent(this, MyService.class);
serviceIntent.putExtra("inputExtra", "run");
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?

NoProg
  • 147
  • 2
  • 14
  • Does this answer your question? [How to create firebase background service in Android?](https://stackoverflow.com/questions/58284392/how-to-create-firebase-background-service-in-android) – sllopis Aug 25 '20 at 09:42
  • If the above link doesn't help, please elaborate on how you expect your app to work and what it is that your app is doing that prevents you from achieving this. Thanks! – sllopis Aug 25 '20 at 13:16
  • @sllopis No, I've seen this link above before posting but it didn't help. What i expect my app to do is: 1) the user press the start button and since that moment, I get his position from GPS 2) this position is stored into Firebase every 2 secs no matter what, even if the user doesn't use the phone for 2 hours 3) when user press the stop button, service is stopped and data are no stored anymore. That's it. – NoProg Aug 25 '20 at 13:28
  • Is this user's phone supposed to be offline during that span of time? If so, please do know that there are some operations that may not supported when your device is offline. This [documentation](https://cloud.google.com/firestore/docs/manage-data/enable-offline#listen_to_offline_data) explains a bit about this feature. Your observation was accurate, the Android app will maintain its local version when connected to a Firebase database, which will be synced with the current server state once your app is back online. – sllopis Aug 25 '20 at 15:18
  • @sllopis No, user's phone can be online! To understand what i mean, think about e.g. the messaging system of Telegram. If you're not using phone, you'll be notified anyways of a new message. I want do the same; even if the user is not using the phone, his position must be stored in Firebase – NoProg Aug 25 '20 at 19:56

2 Answers2

1

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.

sllopis
  • 2,292
  • 1
  • 8
  • 13
  • I tried to use Firebase Cloud Messaging and my app recevie them but I didn't undestand how to set things to let my app receive notification automaticaly.. the way I implemented them needs me to go on Firebase Console and send msgs manyally – NoProg Aug 26 '20 at 11:46
  • This comment about the implementation of the Firebase Cloud Messaging for your specific use case may warrant a whole new question in StackOverflow. I would recommend that you follow the documentation provided above and please elaborate further on what did not work the way you expected, along with a sample code of your implementation. – Artemis Georgakopoulou Aug 26 '20 at 15:06
0

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

Elias Salom
  • 99
  • 1
  • 13
  • My problem is not about connecting to Firebase. I can connect to Firebase and I can even add data to Firestore until my app still opened. 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. – NoProg Aug 25 '20 at 09:46