foreService

来源:互联网 发布:linux 清空回收站 编辑:程序博客网 时间:2024/04/30 04:42

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 PlayerVOIP CallsFile Downloadand 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.xmlXHTML<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

Javapackage 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
原创粉丝点击