foreService

Introduction to Android Foreground Service

Lets start with a basic know hows; As you may know that services are used for time consuming operations which do not require a user interface. But there are situations when long running operations are in progress and you would want the user to know progress of that operation. E.g.:

Music Player
VOIP Calls
File Download
and many more

Services have a unique property; once ‘started’ they can run even if you put the app in background. All the above listed operations are similar sort of operations, once started they don’t need a UI to complete. But for good user experience you would want the user to know that, this sort of operation is going on. Also now notifications in android allow for three action buttons, through which the user can interact with the ongoing operation if they want. Therefore the best suitable approach for these sort of operations is an Android Foreground Service.

In this tutorial for Android foreground service, I would make a music player stub (not an actual player). Here the user would be able to start a foreground service from an activity by calling startService() method. After this when onStartCommand() method is invoked in service class, I would call the actual startForeground() method. By doing this Android would fire a notification, and from now on this service would be called as an Android Foreground Service. The interesting part here would be that after starting the service we can actually close the activity and interact with service through the notification buttons, or notification actions. To start off lets have a look at the App Manifest:
AndroidManifest.xml
XHTML

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

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTask" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <service android:name=".ForegroundService" >
        </service>
    </application>

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

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="19" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name"
            android:launchMode="singleTask" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <service android:name=".ForegroundService" >
        </service>
    </application>

</manifest>

Point to be noted here is that the main activity is launched in android:launchMode=”singleTask”, as whenever we launch this activity we want only once instance of it at task root level. In this Android Foreground Service Example I have used a constants file to keep the code clean. In any project, as a good coding practice it is advised that all constants are kept in a single class, as its a little easier to access. Have a look at the constants file for this example:
Constants.java
Java package com.truiton.foregroundservice;

public class Constants {
    public interface ACTION {
        public static String MAIN_ACTION = "com.truiton.foregroundservice.action.main";
        public static String PREV_ACTION = "com.truiton.foregroundservice.action.prev";
        public static String PLAY_ACTION = "com.truiton.foregroundservice.action.play";
        public static String NEXT_ACTION = "com.truiton.foregroundservice.action.next";
        public static String STARTFOREGROUND_ACTION = "com.truiton.foregroundservice.action.startforeground";
        public static String STOPFOREGROUND_ACTION = "com.truiton.foregroundservice.action.stopforeground";
    }

    public interface NOTIFICATION_ID {
        public static int FOREGROUND_SERVICE = 101;
    }
}
package com.truiton.foregroundservice;

public class Constants {
 public interface ACTION {
 public static String MAIN_ACTION = "com.truiton.foregroundservice.action.main";
 public static String PREV_ACTION = "com.truiton.foregroundservice.action.prev";
 public static String PLAY_ACTION = "com.truiton.foregroundservice.action.play";
 public static String NEXT_ACTION = "com.truiton.foregroundservice.action.next";
 public static String STARTFOREGROUND_ACTION = "com.truiton.foregroundservice.action.startforeground";
 public static String STOPFOREGROUND_ACTION = "com.truiton.foregroundservice.action.stopforeground";
 }

 public interface NOTIFICATION_ID {
 public static int FOREGROUND_SERVICE = 101;
 }
}

Next lets define the layout for main activity:
activity_main.xml
XHTML
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="52dp"
        android:text="Main Screen"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="26dp"
        android:text="Start Foreground Service" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="35dp"
        android:text="Stop Foreground Service" />

</RelativeLayout>

这里写图片描述

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${relativePackage}.${activityClass}" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="52dp"
        android:text="Main Screen"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/textView1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="26dp"
        android:text="Start Foreground Service" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button1"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="35dp"
        android:text="Stop Foreground Service" />

</RelativeLayout>

After defining layout it would look something like this:

Android Foreground Service

Let me define the MainActivity.java, from where I’ll start the service:
MainActivity.java
Java
package com.truiton.foregroundservice;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startButton = (Button)findViewById(R.id.button1);
        Button stopButton = (Button)findViewById(R.id.button2);

        startButton.setOnClickListener(this);
        stopButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.button1:
            Intent startIntent = new Intent(MainActivity.this, ForegroundService.class);
            startIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
            startService(startIntent);
            break;
        case R.id.button2:
            Intent stopIntent = new Intent(MainActivity.this, ForegroundService.class);
            stopIntent.setAction(Constants.ACTION.STOPFOREGROUND_ACTION);
            startService(stopIntent);
            break;

        default:
            break;
        }

    }
}
package com.truiton.foregroundservice;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

public class MainActivity extends Activity implements OnClickListener {

 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 Button startButton = (Button)findViewById(R.id.button1);
 Button stopButton = (Button)findViewById(R.id.button2);

 startButton.setOnClickListener(this);
 stopButton.setOnClickListener(this);
 }

 @Override
 public void onClick(View v) {
 switch (v.getId()) {
 case R.id.button1:
 Intent startIntent = new Intent(MainActivity.this, ForegroundService.class);
 startIntent.setAction(Constants.ACTION.STARTFOREGROUND_ACTION);
 startService(startIntent);
 break;
 case R.id.button2:
 Intent stopIntent = new Intent(MainActivity.this, ForegroundService.class);
 stopIntent.setAction(Constants.ACTION.STOPFOREGROUND_ACTION);
 startService(stopIntent);
 break;

 default:
 break;
 }

 }
}

As you can see in the above piece of code, I have defined the objects for two buttons and on click of first button I am starting the service in foreground mode, and on click of second button I have written the code to stop the service. Now lets define the main class of this Android Foreground Service Example, ForegroundService.java:
ForegroundService.java

Java
package com.truiton.foregroundservice;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

public class ForegroundService extends Service {
    private static final String LOG_TAG = "ForegroundService";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent.getAction().equals(Constants.ACTION.STARTFOREGROUND_ACTION)) {
            Log.i(LOG_TAG, "Received Start Foreground Intent ");
            Intent notificationIntent = new Intent(this, MainActivity.class);
            notificationIntent.setAction(Constants.ACTION.MAIN_ACTION);
            notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
                    | Intent.FLAG_ACTIVITY_CLEAR_TASK);
            PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
                    notificationIntent, 0);

            Intent previousIntent = new Intent(this, ForegroundService.class);
            previousIntent.setAction(Constants.ACTION.PREV_ACTION);
            PendingIntent ppreviousIntent = PendingIntent.getService(this, 0,
                    previousIntent, 0);

            Intent playIntent = new Intent(this, ForegroundService.class);
            playIntent.setAction(Constants.ACTION.PLAY_ACTION);
            PendingIntent pplayIntent = PendingIntent.getService(this, 0,
                    playIntent, 0);

            Intent nextIntent = new Intent(this, ForegroundService.class);
            nextIntent.setAction(Constants.ACTION.NEXT_ACTION);
            PendingIntent pnextIntent = PendingIntent.getService(this, 0,
                    nextIntent, 0);

            Bitmap icon = BitmapFactory.decodeResource(getResources(),
                    R.drawable.truiton_short);

            Notification notification = new NotificationCompat.Builder(this)
                    .setContentTitle("Truiton Music Player")
                    .setTicker("Truiton Music Player")
                    .setContentText("My Music")
                    .setSmallIcon(R.drawable.ic_launcher)
                    .setLargeIcon(
                            Bitmap.createScaledBitmap(icon, 128, 128, false))
                    .setContentIntent(pendingIntent)
                    .setOngoing(true)
                    .addAction(android.R.drawable.ic_media_previous,
                            "Previous", ppreviousIntent)
                    .addAction(android.R.drawable.ic_media_play, "Play",
                            pplayIntent)
                    .addAction(android.R.drawable.ic_media_next, "Next",
                            pnextIntent).build();
            startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE,
                    notification);
        } else if (intent.getAction().equals(Constants.ACTION.PREV_ACTION)) {
            Log.i(LOG_TAG, "Clicked Previous");
        } else if (intent.getAction().equals(Constants.ACTION.PLAY_ACTION)) {
            Log.i(LOG_TAG, "Clicked Play");
        } else if (intent.getAction().equals(Constants.ACTION.NEXT_ACTION)) {
            Log.i(LOG_TAG, "Clicked Next");
        } else if (intent.getAction().equals(
                Constants.ACTION.STOPFOREGROUND_ACTION)) {
            Log.i(LOG_TAG, "Received Stop Foreground Intent");
            stopForeground(true);
            stopSelf();
        }
        return START_STICKY;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.i(LOG_TAG, "In onDestroy");
    }

    @Override
    public IBinder onBind(Intent intent) {
        // Used only in case of bound services.
        return null;
    }
}
package com.truiton.foregroundservice;

import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

public class ForegroundService extends Service {
 private static final String LOG_TAG = "ForegroundService";

 @Override
 public void onCreate() {
 super.onCreate();
 }

 @Override
 public int onStartCommand(Intent intent, int flags, int startId) {
 if (intent.getAction().equals(Constants.ACTION.STARTFOREGROUND_ACTION)) {
 Log.i(LOG_TAG, "Received Start Foreground Intent ");
 Intent notificationIntent = new Intent(this, MainActivity.class);
 notificationIntent.setAction(Constants.ACTION.MAIN_ACTION);
 notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
 | Intent.FLAG_ACTIVITY_CLEAR_TASK);
 PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
 notificationIntent, 0);

 Intent previousIntent = new Intent(this, ForegroundService.class);
 previousIntent.setAction(Constants.ACTION.PREV_ACTION);
 PendingIntent ppreviousIntent = PendingIntent.getService(this, 0,
 previousIntent, 0);

 Intent playIntent = new Intent(this, ForegroundService.class);
 playIntent.setAction(Constants.ACTION.PLAY_ACTION);
 PendingIntent pplayIntent = PendingIntent.getService(this, 0,
 playIntent, 0);

 Intent nextIntent = new Intent(this, ForegroundService.class);
 nextIntent.setAction(Constants.ACTION.NEXT_ACTION);
 PendingIntent pnextIntent = PendingIntent.getService(this, 0,
 nextIntent, 0);

 Bitmap icon = BitmapFactory.decodeResource(getResources(),
 R.drawable.truiton_short);

 Notification notification = new NotificationCompat.Builder(this)
 .setContentTitle("Truiton Music Player")
 .setTicker("Truiton Music Player")
 .setContentText("My Music")
 .setSmallIcon(R.drawable.ic_launcher)
 .setLargeIcon(
 Bitmap.createScaledBitmap(icon, 128, 128, false))
 .setContentIntent(pendingIntent)
 .setOngoing(true)
 .addAction(android.R.drawable.ic_media_previous,
 "Previous", ppreviousIntent)
 .addAction(android.R.drawable.ic_media_play, "Play",
 pplayIntent)
 .addAction(android.R.drawable.ic_media_next, "Next",
 pnextIntent).build();
 startForeground(Constants.NOTIFICATION_ID.FOREGROUND_SERVICE,
 notification);
 } else if (intent.getAction().equals(Constants.ACTION.PREV_ACTION)) {
 Log.i(LOG_TAG, "Clicked Previous");
 } else if (intent.getAction().equals(Constants.ACTION.PLAY_ACTION)) {
 Log.i(LOG_TAG, "Clicked Play");
 } else if (intent.getAction().equals(Constants.ACTION.NEXT_ACTION)) {
 Log.i(LOG_TAG, "Clicked Next");
 } else if (intent.getAction().equals(
 Constants.ACTION.STOPFOREGROUND_ACTION)) {
 Log.i(LOG_TAG, "Received Stop Foreground Intent");
 stopForeground(true);
 stopSelf();
 }
 return START_STICKY;
 }

 @Override
 public void onDestroy() {
 super.onDestroy();
 Log.i(LOG_TAG, "In onDestroy");
 }

 @Override
 public IBinder onBind(Intent intent) {
 // Used only in case of bound services.
 return null;
 }
}

Please Note: R.drawable.truiton_short is just an image I used for displaying the notification large icon in setLargeIcon method, you can use any other drawable or bitmap like R.drawable.ic_launcher.

In the above class, as you can see I am starting the foreground service by calling startForeground() method. This is the point when a normal service transforms into a foreground service. Also please keep in mind that when a foreground service is stopped by calling the stopForeground() method it does not stop the service, it just removes the service from foreground mode. To stop the service you may have to call the stopSelf() method. To start a service in foreground mode, you need to create an Android notification with notification id. So lets have a closer look at the notifications:
Android Notification with Button

In this Android Foreground Service Example a special type of notification is used. This type of notification can perform actions, and they are called Android notification actions. Please have a look at the screen shot below:
这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值