0

I'm newbie to Android. I'd like to create background service in android that listen for new Document created in Firestore. I've all code ready but stuck with service starting again and again.

  • Can you please tell what should I do to start service only once. Whenever I open app it prints >> listener attached in console. I want it to execute only once and keep it running in background.

  • What happens if I update application on Play store and will it restart service with new code updated in application?

Sample code I followed: https://gist.github.com/vikrum/6170193

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.demo">

    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".SignUp">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
       <!-- Other activities -->

        <!-- authentication service -->

        <service android:name=".service.firestore.listener.FirestoreActivityListener"
            android:exported="false"
            android:process=":remote">
        </service>
    </application>
</manifest>

FirestoreActivityListener.java

package com.demo.service.firestore.listener;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

import com.google.firebase.FirebaseApp;
import com.google.firebase.firestore.DocumentChange;
import com.google.firebase.firestore.EventListener;
import com.google.firebase.firestore.FirebaseFirestore;
import com.google.firebase.firestore.FirebaseFirestoreException;
import com.google.firebase.firestore.FirebaseFirestoreSettings;
import com.google.firebase.firestore.QuerySnapshot;

import javax.annotation.Nullable;

import static com.google.firebase.firestore.DocumentChange.Type.ADDED;

/**
 * @author vicky.thakor
 * @since 2019-02-06
 */
public class FirestoreActivityListener extends Service {

    private static final String COLLECTION = "users/%s/activities";
    private FirebaseFirestore firestore;

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

    @Override
    public void onCreate() {
        super.onCreate();
        FirebaseApp.initializeApp(this);
        this.firestore = FirebaseFirestore.getInstance();
        FirebaseFirestoreSettings settings = new FirebaseFirestoreSettings.Builder()
                .setTimestampsInSnapshotsEnabled(true)
                .build();
        firestore.setFirestoreSettings(settings);
        activitiesListener();
    }

    public void activitiesListener(){
        System.err.println(">> listener attached");
        firestore.collection(String.format(COLLECTION, "xxxxxxxxx"))
                .addSnapshotListener(new EventListener<QuerySnapshot>() {
                    @Override
                    public void onEvent(@Nullable QuerySnapshot querySnapshot, @Nullable FirebaseFirestoreException e) {
                        for (DocumentChange dc : querySnapshot.getDocumentChanges()) {
                            switch (dc.getType()) {
                                case ADDED:
                                    System.err.println(">> " + dc.getDocument().toString());
                                    break;
                            }
                        }
                    }
                });
    }
}

SignUp.java

package com.demo;

import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.Button;
import android.widget.EditText;

import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseException;
import com.google.firebase.FirebaseTooManyRequestsException;
import com.google.firebase.auth.FirebaseAuthInvalidCredentialsException;
import com.google.firebase.auth.PhoneAuthCredential;
import com.google.firebase.auth.PhoneAuthProvider;
import com.demo.home.Home;
import com.demo.model.ParcelableUser;
import com.demo.repository.SharedPreferencesRepository;
import com.demo.repository.impl.SharedPreferencesRepositoryImpl;
import com.demo.service.firestore.listener.FirestoreActivityListener;
import com.demo.util.AlertUtil;
import com.demo.util.IntentConstants;
import com.demo.util.StringUtil;

import java.util.concurrent.TimeUnit;

public class SignUp extends AppCompatActivity {

    private SharedPreferencesRepository sharedPreferencesRepository;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        /* initialization */
        sharedPreferencesRepository = new SharedPreferencesRepositoryImpl(this);
        startService(new Intent(this, FirestoreActivityListener.class));
        verifyRegisteredUser();

        setContentView(R.layout.signup);
        registerNewUser();
    }

    private void verifyRegisteredUser() {
        .....
    }

    private void registerNewUser() {
        .....
    }
}
Vicky Thakor
  • 3,847
  • 7
  • 42
  • 67
  • So you want your service to frequently execute some task, do I get that right? – jle Oct 08 '19 at 10:22
  • Nope I want to listen for new documents created in Firestore. This service should run in background. Something like notifications. – Vicky Thakor Oct 08 '19 at 10:23
  • Sorry I wasn't clear enough on that. I mean you want to use the service to frequently check for updates and your problem is to make the service reoccur this check every certain time interval? – jle Oct 08 '19 at 10:25

1 Answers1

6

I'd like to create background service in android that listen for new Document created in Firestore.

It's true that you can do that in an Android Service but please rememeber that a service represents only a way in which you can tell the OS that you have some background work that has to be done and that doesn't really requiere an attached view (activity).

According to the official documentatation regarding Android services, if you want to benefit from a running service as much as possible:

A foreground service performs some operation that is noticeable to the user. For example, an audio app would use a foreground service to play an audio track. Foreground services must display a Notification. Foreground services continue running even when the user isn't interacting with the app.

So with other words, you'll need to provide an icon for the notification, so the user is informed that the app is consuming resources.

According to your comment:

Nope I want to listen for new documents created in Firestore.

Yes, you can have a listener attached to some document in your database or even to a query, and it will update as the results change. But remeber that you will have to pay a read operation for each of the updates that you get, basically meaning that the user will also have the cost for the bandwidth, and the cost of their battery drain. So I always recommend remove the listeners according to the life-cycler of the activity.

That's a way in which you you deal with Firestore when you want to listen to some documents in a background but please also consider using Cloud Functions for Firebase:

Cloud Functions for Firebase let you automatically run backend code in response to events triggered by Firebase features and HTTPS requests. Your code is stored in Google's cloud and runs in a managed environment. There's no need to manage and scale your own servers.

Edit:

How do Cloud Functions actually work?

As in the case of the client, Cloud Functions also allows you to attach a listener to a single document or even to a query. So a Cloud Function can be triggered when something special in your database happens, for instance when some documents gets written into a Firestore collection. Once the function is triggered, you can take some action. As Frank van Puffelen mentioned in his comment, you can send a notification for example. For Android, please see below a straightforward example:

Maybe this is not what you want, to send a notification but it's a simple example and I think you'll be able to get the idea.

Alex Mamo
  • 130,605
  • 17
  • 163
  • 193
  • Great answer Alex! I learned something more about Android services from it. :) – Frank van Puffelen Oct 08 '19 at 13:17
  • For the approach with Cloud Functions it might be useful to add how it would work. I.e. the Cloud Function gets called when a document gets written to Firestore, and it then uses FCM to send a notification to the app, which then wakes up the `onMessageReceived` code in the app, that can show a notification (or do whatever else is needed). – Frank van Puffelen Oct 08 '19 at 13:19
  • @FrankvanPuffelen Thank you for saying that puf . Yes you're right, I should have added more infos regarding Cloud Functions in the first place. Just updated my answer. – Alex Mamo Oct 08 '19 at 13:47