Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keep running background service (Android)

I create background service with is started after user signed in and should work even after activity was 'throw to trash'. Service show notifications that it's running ,register two broadcast receivers for monitoring wifi and phone state and should keep running until token isn't expired or user logout.

Everything works but service is killed by android.The only solution that REALLY WORKS is instruction from this article http://nine-faq.9folders.com/articles/37422-stop-your-huawei-smartphone-from-closing-apps-when-you-lock-the-screen Unfortunate this solution isn't acceptable because it's dependence on user.

Code created by C# using Xamarin , but If any one know how to implement solution from article programically I'll be glad for useful advice even in other languages (java ,kotlin)

Manifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" 
          android:versionCode="1" 
          android:versionName="1.0" 
          package="com.companyname.com.bgapptest">
  <uses-sdk android:minSdkVersion="25" android:targetSdkVersion="28" />
  <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">
    <service
       android:name="com.BGAppTest.BackgroundService"
       android:enabled="true"
       android:exported="false"/>
  </application>
  <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <uses-permission android:name="android.permission.CALL_PHONE" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
  <uses-permission android:name="android.permission.WAKE_LOCK" />
  <uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
</manifest>

Start service from activity

Activity

    using System;
using System.Linq;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using Android.Provider;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Support.V7.App;
using Android.Views;
using Android.Widget;
using AlertDialog = Android.Support.V7.App.AlertDialog;

namespace com.BGAppTest
{
    [Activity(Label = "@string/app_name", Theme = "@style/AppTheme.NoActionBar", MainLauncher = true)]
    public class MainActivity : AppCompatActivity
    {
        public static string HOPE = "Nothing";
        string[] perms = new string[] { "android.permission.ACCESS_FINE_LOCATION", "android.permission.ACCESS_COARSE_LOCATION", "android.permission.ACCESS_NETWORK_STATE", "android.permission.READ_PHONE_STATE","android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"};

        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
            SetContentView(Resource.Layout.activity_main);
            Android.Support.V7.Widget.Toolbar toolbar = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbar);
            SetSupportActionBar(toolbar);
            FloatingActionButton fab = FindViewById<FloatingActionButton>(Resource.Id.fab);
            fab.Click += FabOnClick;
            HOPE = DateTime.Now.ToString();
            StartService();
        }

        public override bool OnCreateOptionsMenu(IMenu menu)
        {
            MenuInflater.Inflate(Resource.Menu.menu_main, menu);
            return true;
        }

        public override bool OnOptionsItemSelected(IMenuItem item)
        {
            int id = item.ItemId;
            if (id == Resource.Id.action_settings)
            {
                return true;
            }

            return base.OnOptionsItemSelected(item);
        }

        private void FabOnClick(object sender, EventArgs eventArgs)
        {
            View view = (View)sender;
            Intent service = new Intent(this, typeof(BackgroundService));
            StopService(service);
        }

        protected void IGnoreBatteryActivity()
        {
            PowerManager m = GetSystemService(PowerService) as PowerManager;
            Intent intent = new Intent();
            if (m.IsIgnoringBatteryOptimizations(this.PackageName))
            {
                //intent.SetAction(Settings.ActionIgnoreBatteryOptimizationSettings);
            }
            else
            {
                intent.SetAction(Settings.ActionRequestIgnoreBatteryOptimizations);
                intent.SetData(Android.Net.Uri.Parse("package:" + PackageName));
                StartActivity(intent);

            }

        }

        void StartService()
        {
            foreach (var p in perms)
            {
                if (CheckSelfPermission(p) == Permission.Denied)
                {
                    RequestPermissions(perms, 2);
                    return;
                }
            }
            IGnoreBatteryActivity();
            Intent service = new Intent(this, typeof(BackgroundService));
            if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
                StartForegroundService(service);
            else
                StartService(service);

        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Permission[] grantResults)
        {
            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
            if (grantResults.Any(x => x == Permission.Denied))
            {
                RunOnUiThread(() =>
                {
                    new AlertDialog.Builder(this)
                       .SetMessage("Uprawnienia są wymagane.Chcesz nadać uprawnienia?")
                       .SetNegativeButton("Nie", delegate
                       {
                           this.FinishAffinity();
                       })
                       .SetPositiveButton("Tak", delegate
                       {
                           RequestPermissions(perms, requestCode);
                       })
                       .SetCancelable(false)
                       .Create()
                       .Show();
                });
            }
            else
                StartService();
        }

    }
}

Service

    [Service(Name = "com.BGAppTest.BackgroundService")]
public class BackgroundService : Service
{
    NetworkChangeReceiver networkReceiver;
    PhoneCallsReceiver receiver;
    const int Service_Running_Notification_ID = 937;
    public bool isStarted = false;
    PowerManager.WakeLock wakeLock;
    public BackgroundService()
    {

    }
    [return: GeneratedEnum]
    public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
    {
        if (isStarted)
            return StartCommandResult.Sticky;
        isStarted = true;
        PowerManager m = GetSystemService(Context.PowerService) as PowerManager;
        wakeLock = m.NewWakeLock(WakeLockFlags.Partial, "MYWeakLock");
        wakeLock.Acquire();
        return StartCommandResult.Sticky;
    }
    public override void OnCreate()
    {
        base.OnCreate();
        RegisterForegroundService();
        RegisterWifiReceiver();
        RegisterPhoneReceiver();

    }



    public void RegisterForegroundService()
    {
        Notification notification = BuildNotification("Title","Text");
        StartForeground(Service_Running_Notification_ID, notification);
    }


    Notification BuildNotification(string title, string message)
    {
        NotificationCompat.Builder notificationBuilder = null;
        if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
        {
            String NOTIFICATION_CHANNEL_ID = "com.BGAppTest";
            NotificationChannel chan = new NotificationChannel(NOTIFICATION_CHANNEL_ID, "MY_Foreground", NotificationImportance.High);
            NotificationManager manager = (NotificationManager)GetSystemService(Context.NotificationService);
            manager.CreateNotificationChannel(chan);
            notificationBuilder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID);
        }
        else
            notificationBuilder = new NotificationCompat.Builder(this);

        return notificationBuilder
          .SetSmallIcon(Resource.Drawable.ic_mtrl_chip_checked_black)
           .SetContentTitle(Resources.GetString(Resource.String.app_name))
           .SetContentIntent(BuildIntentToShowMainActivity())
           .SetContentTitle(title)
           .SetStyle(new NotificationCompat.BigTextStyle().BigText(message))
           .SetOngoing(true)
           .Build();

    }

    private PendingIntent BuildIntentToShowMainActivity()
    {
        var intent = this.PackageManager.GetLaunchIntentForPackage(this.PackageName);
        intent.AddFlags(ActivityFlags.ClearTop);
        var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.UpdateCurrent);
        return pendingIntent;

    }


    public void RegisterWifiReceiver()
    {
        var networkReceiver = new NetworkChangeReceiver();
        IntentFilter intentFilters = new IntentFilter();
        intentFilters.AddAction("android.net.conn.CONNECTIVITY_CHANGE");
        RegisterReceiver(networkReceiver, intentFilters);
    }
    public void RegisterPhoneReceiver()
    {
        var receiver = new PhoneCallsReceiver();
        IntentFilter intentFilters = new IntentFilter();
        intentFilters.AddAction("android.intent.action.PHONE_STATE");
        RegisterReceiver(receiver, intentFilters);

    }

    public override IBinder OnBind(Intent intent)
    {
        return null;
    }
    #region Session checker
    //TODO:przenieść tą fukcionalność do odzielnego serwisu
    System.Timers.Timer timers = new System.Timers.Timer();
    private void StartTokenExpiredTimer()
    {
    }

    #endregion
    public override void OnDestroy()
    {
        base.OnDestroy();
        try
        {
            UnregisterReceiver(receiver);
            UnregisterReceiver(networkReceiver);
        }
        catch { }

    }
}

Receivers

using Android.Content;
using Android.Widget;


namespace com.BGAppTestReceivers
{
    public class NetworkChangeReceiver: BroadcastReceiver
    {

        static NetworkChangeReceiver()
        {

        }
        public NetworkChangeReceiver()
        {
        }
        public override async void OnReceive(Context context, Intent intent)
        {
            Toast.MakeText(context, "BGNetworkChange", ToastLength.Short).Show();

        }


    }

    public class PhoneCallsReceiver : BroadcastReceiver
    {

        static PhoneCallsReceiver()
        {

        }
        public PhoneCallsReceiver()
        {
        }
        public override async void OnReceive(Context context, Intent intent)
        {
            Toast.MakeText(context, "BGPhoneChange", ToastLength.Short).Show();
        }

    }


}

UPDATE 26.09.2020

I add full code of my example application, made it as simple as I could

UPDATE 30.09.2020 Add android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS and ActionRequestIgnoreBatteryOptimizations

like image 214
DespeiL Avatar asked Sep 14 '20 10:09

DespeiL


People also ask

How do I run background services continuously?

This example demonstrates how do I run an android service always in background. Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main. xml.

What are background services in android?

A background service performs an operation that isn't directly noticed by the user. For example, if an app used a service to compact its storage, that would usually be a background service.

How do I run an Android service always in background?

This example demonstrates how do I run an android service always in background. Step 1 − Create a new project in Android Studio, go to File ⇒ New Project and fill all required details to create a new project. Step 2 − Add the following code to res/layout/activity_main.xml.

How do I keep an app running in the background in Android?

Start the Settings app and find Battery Optimization in the Apps section. At the top of the screen, tap "Not Optimized" and then tap "All a Originally Answered: How do I keep any app permanently alive in the background in Android? so simple. t will prevent app from sleep mode. Design your dream database with Kintone Today!

How do you keep the foreground service running on Android?

Theoretically, according to Android documentation, returning RETURN_STICKY from the service’s onStartCommand method should be enough for Android to keep the foreground service running. Michal was testing all this with a Xiaomi Note 5 with Android Pie and every time he swiped an app from recent apps, it worked flawlessly.

What are the limitations of background services in Android?

Hope it helps! Due to Android battery optimizations introduced in Android 8.0 (API level 26), background services have now some important limitations. Essentially, they are killed once the app is in background for a while making them worthless for our purpose of running an always-running service.


Video Answer


2 Answers

You must request permission to exempt your app from battery saving and App standby mode.

You can try it manually at first from Settings > Apps > Special App Access > Battery Optimisation > Turn optimization off for your app.

If it works for you then you can ask the user to whitelist your app using the following permission

android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS
like image 63
Youssef Shamass Avatar answered Oct 18 '22 19:10

Youssef Shamass


I'm author of Floating Apps and with 7 years of development, I've seen running it on almost 11.000 different devices. I've also posted a series of articles on background services and floating windows.

First to say, it's impossible to achieve this on all devices without user interaction. Some vendors change Android system too much and add aggresive memory or process management and manual action is sometimes necessary. Also, there are devices on which this cannot be solved at all.

What helps is to run the service in different process (using multiprocess in AndroidManifest.xml) and you should add android:stopWithTask for the service so it's not being killed with the activity - helps on some devices.

But basically, in Floating Apps, I try to detect phones with known issues and ask users to set their phone for the app to work correctly - disable battery optimisations, add to protected apps on Huawei, etc. Also, a lot of issues is solved by our customer support (my wife on maternity leave actually :-)).

The last instance is to send users to: https://dontkillmyapp.com

Frankly, for the most of users, this is actually not a big deal and with disabled battery optimisations, the app works quite well for the most of them. However, for a part of them, there is no solution at all - not programatic nor manual. Don't try to solve this problem generally for all phones.

like image 43
Václav Hodek Avatar answered Oct 18 '22 20:10

Václav Hodek