Unkillable Android Service (Boot,Foreground,Sticky)

The main tasks of the program include executing methods in the background, for which Android Services is used.

You need to start the Service in such a way that after closing MainActivity, the service remains in working condition and can create notifications, as well as, in the case of closing the system or rebooting, it is restored independently.

At the moment, the main problem is keeping MyService alive after closing the user interface.

AutoStart:

In AndroidManifest.xml were registered

 <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETE"/>

 

<receiver android:name="ru.package.BootReceiver">
        <intent-filter>
            <action android:name = "android.intent.action.BOOT_COMPLETED"/>
        </intent-filter>
</receiver>

In BootReceiver.java:

@Override
public void onReceive(Context context, Intent intent) {
    Intent serviceIntent = new Intent(context, MyService.class);
    context.startService(serviceIntent);
}

Service:

In AndroidManifest.xml registered:

<service
        android:name="ru.package.MyService"
        android:enabled="true"
        android:exported="true"
        android:process=":AnyProcessName">
</service>

In MyService.java contains the following code, with the exception of some parts (the most important elements are described, which may contain errors that do not allow you to solve the problem task):

public void onCreate() {
    super.onCreate();
    notificationManager = (NotificationManager)
    this.getSystemService(this.NOTIFICATION_SERVICE);
}

 

public int onStartCommand(Intent intent, int flags, int startId) {
    sendNotification("Service started", "Service", "started");
    return Service.START_STICKY;
}

 

public void sendNotification(String Ticker,String Title,String Text) {
    Intent notificationIntent = new Intent(this, MainActivity.class);
    notificationIntent.setAction(Intent.ACTION_MAIN);
    notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);

    PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
    builder.setContentIntent(contentIntent)
    .setOngoing(true)   //invulnerable
    .setTicker(Ticker)
    .setContentTitle(Title)
    .setContentText(Text)
    .setWhen(System.currentTimeMillis());

    Notification notification;
    if (android.os.Build.VERSION.SDK_INT<=15) {
        notification = builder.getNotification(); // API 15 and lower
    }else {
        notification = builder.build();
    }

    startForeground(DEFAULT_NOTIFICATION_ID,notification);

 

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

@Override
public void onRebind(Intent intent) {
    super.onRebind(intent);
}

@Override
public boolean onUnbind(Intent intent) {
    return super.onUnbind(intent);
}

@Override
public void onDestroy()
{
    //Removing any notifications
    notificationManager.cancel(DEFAULT_NOTIFICATION_ID);

    //Disabling service
    stopSelf();
    super.onDestroy();
}

Starts MyService from MainActivity as follows:

Intent intentService;

 

@Override
protected void onCreate(Bundle savedInstanceState)
{
    intentService = new Intent(this,MyService.class);
}

 

public void runService() {
    boolean working = isMyServiceRunning(MyService.class);
    if (!working) {
        startService(intentService);
    } else {
        stopService(intentService);
    }
}

 

public boolean isMyServiceRunning(Class<?> serviceClass) {
    ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if (serviceClass.getName().equals(service.service.getClassName())) {
            return true;
        }
    }
    return false;
}

Please indicate the mistakes made during programming, adding them with third-party materials (examples) or search keywords.

Author: Mikhail Kolesnikov, 2016-01-09

1 answers

For the adequate operation of all the requirements presented to the "immortal" service, it was necessary to uninstall the non-working version from the device, and then download the fresh build. This was most likely caused by the permissions. I started building my program almost from scratch, so after the first installation, the application was given an insufficient number of permissions to implement the functions of the "immortal" service. At the moment, the application uses only two permissions: Notifications and Autostart. This idea was prompted by the full functionality of the application on all other devices.

I will try to reflect in this answer all the code elements necessary for the operation of the "immortal" service.

Android Manifest:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

<service
    android:name=".MyService"
    android:enabled="true"
    android:exported="true" >
</service>

<receiver
    android:name=".MyReceiver"
    android:permission="android.permission.RECEIVE_BOOT_COMPLETED"
    android:enabled="true">

    <intent-filter>
        <action android:name="android.intent.action.BOOT_COMPLETED" />
        <action android:name="android.intent.action.QUICKBOOT_POWERON" />
        <action android:name="android.intent.action.REBOOT"/>
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>

</receiver>

MainActivity:

public class MainActivity extends AppCompatActivity {
    Intent intentService;

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        intentService = new Intent(this,MyService.class);
    }

    public void click_Service(View v) {
        if (!isMyServiceRunning(MyService.class)) {
            startService(intentService);
        } else {
            stopService(intentService);
        }
    }

    private boolean isMyServiceRunning(Class<?> serviceClass) {
        ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);

        for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
            if (serviceClass.getName().equals(service.service.getClassName())) {
                return true;
            }
        }
        return false;
    }
}

MyService:

public class MyService extends Service {
    private NotificationManager notificationManager;
    public static final int DEFAULT_NOTIFICATION_ID = 101;

    public void onCreate() {
        super.onCreate();

        notificationManager = (NotificationManager) this.getSystemService(this.NOTIFICATION_SERVICE);
    }

    public int onStartCommand(Intent intent, int flags, int startId) {

        //Send Foreground Notification
        sendNotification("Ticker","Title","Text");

        //Task
        doTask();

        //return Service.START_STICKY;
        return START_REDELIVER_INTENT;
    }

    //Send custom notification
    public void sendNotification(String Ticker,String Title,String Text) {

        //These three lines makes Notification to open main activity after clicking on it
        Intent notificationIntent = new Intent(this, MainActivity.class);
        notificationIntent.setAction(Intent.ACTION_MAIN);
        notificationIntent.addCategory(Intent.CATEGORY_LAUNCHER);

        PendingIntent contentIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        builder.setContentIntent(contentIntent)
            .setOngoing(true)   //Can't be swiped out
            .setSmallIcon(R.mipmap.ic_launcher)
            //.setLargeIcon(BitmapFactory.decodeResource(res, R.drawable.large))   // большая картинка
            .setTicker(Ticker)
            .setContentTitle(Title) //Заголовок
            .setContentText(Text) // Текст уведомления
            .setWhen(System.currentTimeMillis());

        Notification notification;
        if (android.os.Build.VERSION.SDK_INT<=15) {
            notification = builder.getNotification(); // API-15 and lower
        }else{
            notification = builder.build();
        }

        startForeground(DEFAULT_NOTIFICATION_ID, notification);
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();

        //Removing any notifications
        notificationManager.cancel(DEFAULT_NOTIFICATION_ID);

        //Disabling service
        stopSelf();
    }
}

MyReceiver:

public class MyReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent intentService = new Intent(context, MyService.class);
        context.startService(intentService);
    }
}

Thus, we get a service that automatically starts after the start the system. The user interface can be triggered by clicking on a notification that cannot be removed from the notification bar. After opening the user interface, the service can be disabled or enabled manually by clicking on the corresponding button. If you close the app, including swiping the app from the Recent Task Bar, the service will remain enabled and continue working.

The only possible way to terminate MyService is without using a custom one The interface is to close the Application process using Force Stop in the list of applications, or stop the process itself manually in the system settings menu.

 6
Author: Mikhail Kolesnikov, 2016-01-15 09:36:10