Service. Пример простого сервиса

| 24.02.2015

Service в Android (или служба) часто используется для создания постоянно работающего в фоне коде, например, для отслеживания наступления определенного времени или даты, наличия обновлений на внешнем сервере и т.п. Также с помощью сервиса можно создавать и «одноразовые» задачи. При этом рекомендуется всю продолжительную работу в сервисе запускать в отдельном потоке, чтобы не создавать «торможений» при взаимодействии пользователя с элементами на экране. Также рекомендую познакомиться с AsyncTask для создания асинхронных задач, если вы еще с ними не знакомы.

Сервис, как гласит документация, не является отдельными обособленным процессом. Он является частью приложения и при удалении приложения, конечно же, покидает смартфон вместе с ним:)

Сервис имеет свой жизненный цикл и включает в себя такие методы:

onCreate()
onStartCommand()
onDestroy()

Рассмотрим пример создания сервиса на примере простого приложения. Его суть: в основном Активити мы запустим сервис, в котором в цикле выведем в логи 5 однотипных сообщений с номером итерации.

1. Создадим класс нашего сервиса — SimpleService (название произвольное). Главное, что мы тут должны сделать — это унаследоваться от Service. Также от нас потребуют реализовать один абстрактный метод onBind(). Мы его добавим, но в этот раз пока использовать для наших целей не будем (оставим пустым).

package ru.androiddocs.serviceexample;

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;

import java.util.concurrent.TimeUnit;

public class SimpleService extends Service {
    public SimpleService() {
    }

    public int onStartCommand(Intent intent, int flags, int startId) {  
        Log.d("my_tag", "onStartCommand");      
        logLoop();
        return super.onStartCommand(intent, flags, startId);
    }

    private void logLoop() {
        new Thread(new Runnable() {
            public void run() {
                for (int i = 1; i < 6; i ++) {
                    try {
                        TimeUnit.SECONDS.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    Log.d("my_tag", "hello from service: " + i);
                }
            }
        }).start();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

Метод onStartCommand() срабатывает при запуске сервиса и на вход принимает несколько параметров.

Стоит отметить, что сервис может быть остановлен («убит») принудительно извне, и его перезапуск зависит от того, что возвращает метод onStartCommand(). Например, если мы укажем:

return START_NOT_STICKY;

то сервис не будет перезапускаться повторно. Если же мы получили на входе в метод onStartCommand() какой-то Intent с параметрами и после остановки и перезапуска сервиса захотим получить этот Intent снова, то можно указать другую константу:

return START_REDELIVER_INTENT;

Если нужно просто перезапускать сервис, то вернем START_STICKY:

return START_STICKY;

Если мы возвращаем START_REDELIVER_INTENT, то на входе в метод при перезапуске сервиса значение флага будет START_FLAG_REDELIVERY. Соответственно, в первом параметре метода onStartCommand() мы получим Intent с теми же данными, которые передавали при первом запуске сервиса.

При запуске сервиса вызов super.onStartCommand(intent, flags, startId) у меня возвращает 1, то есть значение равное START_STICKY. Если же нужно явно видеть, что возвращает метод onStartCommand() сервиса, то тут можно заменить вызов родительского метода на явный возврат нужной константы, как указано выше.

Параметр startId — целое число, позволяющее идентифицировать запрос на запуск сервиса. Это идентификатор, используемый, например, для явной остановки сервиса.

В методе onStartCommand() мы запускаем приватный метод logLoop(), где создаем новый поток с циклом вывода сообщений. На каждой итерации цикла мы делаем паузу в 10 секунд:

TimeUnit.SECONDS.sleep(10);

При желании тут можно создать бесконечный цикл, который будет постоянно работать (пока сервис не будет принудительно остановлен).

2. Если вы добавляли класс сервиса вручную, то не забудьте прописать его в файле манифеста. Сервис прописывается на том же «уровне», что и Активити (внутри application).

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="ru.androiddocs.serviceexample" >

    <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" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

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

</manifest>

3. Создадим верстку основного экрана — res/layout/activity_main.xml. Тут просто пустой 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"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

</RelativeLayout>

Добавьте значения отступов в res/values/dimens.xml

<resources>
    <!-- Default screen margins, per the Android Design guidelines. -->
    <dimen name="activity_horizontal_margin">16dp</dimen>
    <dimen name="activity_vertical_margin">16dp</dimen>
</resources>

4. Строковые ресурсы — res/values/strings.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="app_name">Service Example</string>    
</resources>

5. Класс Активити, из которого запускаем сервис.

package ru.androiddocs.serviceexample;

import android.content.Intent;
import android.support.v7.app.ActionBarActivity;
import android.os.Bundle;

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        startService(new Intent(this, SimpleService.class));
    }
}

Запуск происходит аналогично запуску нового Активити с созданием Intent-объекта. Разница лишь в том, что мы используем метод startService().

После запуска в логах можно увидеть вот такие сообщения:

Service. Пример простого сервиса

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*