В этом примере:

  • запускаем сервис в отдельном процессе
  • создаем и активируем уведомление
  • переход/возврат от уведомление к нашему приложению (и возможность отмены уведомления)
  • шлем уведомление из сервиса

Да, уведомления – отдельная от сервисов тема. Но чаще всего уведомления используются именно в сервисах.

В андроид есть строка вверху экрана. Называется она статус-бар. Туда обычно в виде иконок сваливаются различные уведомления для пользователя (новые письма, смс и прочие). Пользователь открывает статус бар – видит там чуть более подробную инфу о событии. Дальше он может либо стереть это уведомление, либо нажать на него и перейти непосредственно к событию.

Создадим приложение и сервис. Сервис, как будто загружает файл и посылает уведомление, по нажатию на которое будет открываться приложение и отображать «виртуальное имя файла«.

Создадим проект (на базе самого простого шаблона Empty Actuvity):

Имя проекта по вашему усмотрению. Но учтите, что в данном примере выбрано:

Project name: MyApYoga
Build Target: Android 11 R  Minimum SDK API 24: Android 7.0(Nougat)
Package name: info.mir.myapyoga
Create Activity: MainActivity

 

В strings.xml добавляем строки:

<string name="start">Start</string>
<string name="stop">Stop</string>

в activity_main.xml заменяем весь вид Layout (Кнопки для старт/стопа сервиса и TextView для отображения результата):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">
    <Button
        android:id="@+id/btnStart"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClickStart"
        android:text="@string/start">
    </Button>
    <Button
        android:id="@+id/btnStop"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClickStop"
        android:text="@string/stop">
    </Button>
    <TextView
        android:id="@+id/tv"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="">
    </TextView>
</LinearLayout>

Создаем сервис MyService.java и прописываем его в манифесте (AndroidManifest.xml). В манифесте же настроим сервис так, чтобы он работал в отдельном процессе. Для этого надо в его атрибуте process написать двоеточие и дать название.

Создание MyService.java

Вот на что необходимо обратить внимание в манифесте AndroidManifest.xml

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

    <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/Theme.MyApYoga">

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

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

</manifest>

Продолжаем, вот что необходимо добавить в MyService.java:

package info.mir.myapyoga;

import java.util.concurrent.TimeUnit;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.graphics.Color;
import android.net.Uri;
import android.os.IBinder;
import android.provider.MediaStore;
import androidx.core.app.NotificationCompat;

public class MyService extends Service {
    NotificationManager nm;

    @Override
    public void onCreate() {
        super.onCreate();
        nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
    }

    public int onStartCommand(Intent intent, int flags, int startId) {
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        sendNotif();
        return super.onStartCommand(intent, flags, startId);
    }

    void sendNotif() {

        Intent intent = new Intent(this, MainActivity.class);
        intent.putExtra(MainActivity.FILE_NAME, "somefile");
        PendingIntent pIntent = PendingIntent.getActivity(this, 1, intent, 0);

        Notification.Builder builder = new Notification.Builder(this)
                .setAutoCancel(true)   //удаляет уведомление после активации
                .setTicker("Text in status bar")
                .setContentTitle("Notification`s title")
                .setContentText("Notification`s text")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setColor(Color.RED)
                .setContentIntent(pIntent)
//                .setOngoing(true)   //нельзя смахнуть(отменить) уведомление
                .setSubText("This is subtext...")   //API level 16
                .setSound(Uri.withAppendedPath(MediaStore.Audio.Media.INTERNAL_CONTENT_URI, "6")) //= Uri.withAppendedPath(Audio.Media.INTERNAL_CONTENT_URI, "6");
                .setNumber(10);
        Notification notification = builder.build();

        NotificationManager notificationManager =
                (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        notificationManager.notify(1, notification);
    }

    public IBinder onBind(Intent arg0) {
        return null;
    }
}

В onCreate получаем менеджер уведомлений – NotificationManager. Он нам понадобится, чтобы отправить уведомление.

В onStartCommand запускаем паузу на 5 секунд (эмулируем закачку файла) и после этого отправляем уведомление. Именно из-за этой паузы мы и используем другой процесс, чтобы не тормозило основное приложение.

В sendNotif мы создаем и отправляем уведомление. Последовательность действий состоит из трех частей:

  • создаем Intent, который мы бы использовали для вызова нашего Activity (MainActivity.class). Туда помещаем сообщение — в данном примере имя загруженного файла «somefile«.  Activity его достанет и поместит в TextView. Далее мы оборачиваем этот Intent в PendingIntent, с помощью метода getActivity. На вход ему передаем контекст и Intent. Второй параметр не используется (так написано в хелпе). А четвертый – это флаги, влияющие на поведение PendingIntent. Теперь этот созданный PendingIntent содержит информацию о том, что надо вызывать Activity, а также объект Intent, который для этой цели надо использовать. Это будет использовано при нажатии на уведомлении.
  • создаем Notification. В конструкторе указываем иконку и текст, которые будут видны в статус-баре. Также тут можно указать звуковой сопровождение при появлении уведомления. Метод build() активируем уведомление.
  • Далее вызываем метод notify для менеджера уведомлений и передаем туда ID и созданное уведомление. ID используется, если мы хотим изменить или удалить уведомление.

 

Небольшое отступление

При наладке приложения, использующего сервис, обязательно столкнетесь с проблемой — перезапуска запущенного сервиса. Так как сервис, запустившись первый раз, не отключается после выключения главного приложения. Самый простой и долгий способ, это перезагрузить телефон. Но вместо этого, можно уничтожить запущенный процесс, через терминал — командой adb shell kill 12345. Где 12345, это номер PID процесса. Детально.

Продолжим.

Теперь вернемся к центральной части приложения MainActivity

Вот что нужно добавить в  MainActivity.java:

package info.mir.myapyoga;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import android.widget.TextView;

public class MainActivity extends Activity {

    public final static String FILE_NAME = "filename";

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView tv = (TextView) findViewById(R.id.tv);

        Intent intent = getIntent();

        String fileName = intent.getStringExtra(FILE_NAME);
        if (!TextUtils.isEmpty(fileName))
            tv.setText(fileName);
    }


    public void onClickStart(View v) {
        startService(new Intent(this, MyService.class));
    }


    public void onClickStop(View v) {
        stopService(new Intent(this, MyService.class));
    }
}

 

Тот же подход в видео режиме:

Start аndroid: Урок 99. Service. Уведомления — notifications (разработка в Android Studio)

Тут еще будет добавлен архив проекта (…)

Источник #1