android Foreground Service 前台服务/notification全局通知

Android Foreground Service 实现与Notification全局通知
本文介绍了如何在Android中使用Foreground Service和Notification实现应用在后台持续运行并展示状态栏通知。通过声明服务、获取权限、初始化Notification、创建NotificationChannel、更新及移除通知,确保服务在内存紧张时不会被系统杀死。示例代码和效果演示展示了功能的实现,同时作者在开发过程中遇到横幅显示问题寻求解答。
部署运行你感兴趣的模型镜像

前言

我在设计一款自用的用于自我管理的app,为了增强自我反馈,我想要这个软件能够持续的显示我执行某一个事件所用的时间。并且为了使得软件不会被系统自动关闭,百般斟酌后我选了前台服务方案。

要素简介

前台服务(Foreground Service)

前台服务(Foreground Service)相比起后台服务(Service),拥有一个优势那就是在系统内存不足的时候不允许系统杀死的服务,并且在运行时需要能被用户所知,需要在状态栏创建一个通知来管理。

全局通知(notification)

全局通知(notification),常用于告知用户某些事件在某个事件发生,在发动时会在状态栏和通知栏都显示信息,用户可以点击信息进行某些功能的管理

功能实现

声明服务与获取权限

声明权限

 <!-- 通知权限 -->
<!--    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>-->
    <!-- 前台权限权限 -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

声明service

<service android:name=".AppService"/>

在最新版本中notification已被启用,改为使用NotificationCompat
案例如下:

Notification 初始化

		registerNotificationChannel();
 		notifyId = 0x975;//(int) System.currentTimeMillis();

        Intent nfIntent = new Intent(this, HostActivity.class);
        mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID);
        mBuilder.setContentIntent(PendingIntent.
                getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
                .setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
                        R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标)
                .setContentTitle("下拉列表中的Title") // 设置下拉列表里的标题
                .setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
                .setContentText("要显示的内容") // 设置上下文内容
                .setWhen(System.currentTimeMillis()) // 设置该通知发生的时间
                //.setPriority(Notification.PRIORITY_MIN) //设定为最低优先级
                .setPriority(Notification.PRIORITY_HIGH) //设定为最高优先级
                .setOngoing(true)//设定为点击后不消失
        ;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            mBuilder.setContentTitle(getResources().getString(R.string.app_name));
        }
        //notificationManager.notify(notifyId, mBuilder.build());
        startForeground(notifyId, mBuilder.build());
  1. 其中notifyId是通知的唯一标识当通知的notify一致时,再发布通知则会覆盖原有的通知内容。这个方法也常用于实时更新通知内容
  2. 前台服务发布通知的方法为startForeground,使用方法和notificationManager.notify类似,不需要另外再注册Manager。

NotificationChannel 通知渠道

而在高版本的安卓中,还需要注册通知渠道:

 	/**
     * 注册通知通道
     */
    private void registerNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationChannel notificationChannel = notificationManager.getNotificationChannel(CHANNEL_ID);
            if (notificationChannel == null) {
                NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                        CHANNEL_NAME,
                        NotificationManager.IMPORTANCE_HIGH
                        //NotificationManager.IMPORTANCE_MIN
                );
                //NotificationManager.IMPORTANCE_HIGH //NONE 为不弹出横幅,HIGH为弹出横幅
                ;
                //是否在桌面icon右上角展示小红点
                channel.enableLights(true);
                //小红点颜色
                channel.setLightColor(Color.RED);
                //通知显示
                channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
                //是否在久按桌面图标时显示此渠道的通知
                channel.setShowBadge(true);
                notificationManager.createNotificationChannel(channel);
            }
        }
    }

update

用于刷新通知内容

/**
     * 主自定义线程启动函数
     */
    private void startMyThread() {
        stopThread=false;
        Thread dateFlash=new Thread() {
            @Override
            public void run() {
                super.run();
                while (!stopThread) {
                    try {
                        Thread.sleep(1000);//每1s刷新一次
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    updateUI();
                    startForeground(notifyId, mBuilder.build());
                }
            }
        };
        dateFlash.start();
    }

移除通知

stopForeground(true);
//true表示移除

启动服务

启动前台服务的方法和一般方法相同

    /**
     * 启动前台服务
     */
    private  void show2(){

        Intent mIntent=new Intent(MainActivity.this,AppService.class) ;

//        mIntent.putExtra("startId",now_Event.getEvent_id());
//        mIntent.putExtra("startHour",now_Event.getStart_hour());
//        mIntent.putExtra("startMin",now_Event.getStart_min());

        startService(mIntent);//直接启动服务方式启动
        //mAppService.startForeground(110, notification);// 开始前台服务
    }

完整AppService.java代码

package com.example.myapplication;

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log;

import java.util.Calendar;

import androidx.core.app.NotificationCompat;

public class AppService extends Service {

    public static final String CHANNEL_ID = " com.example.myapplication.AppService";
    public static final String CHANNEL_NAME = " com.example.myapplication";
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    private static final String TAG = AppService.class.getSimpleName();


    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate()");
        registerNotificationChannel();
        notifyId = 0x975;//(int) System.currentTimeMillis();

        Intent nfIntent = new Intent(this, HostActivity.class);
        mBuilder = new NotificationCompat.Builder(this, CHANNEL_ID);
        mBuilder.setContentIntent(PendingIntent.
                getActivity(this, 0, nfIntent, 0)) // 设置PendingIntent
                .setLargeIcon(BitmapFactory.decodeResource(this.getResources(),
                        R.mipmap.ic_launcher)) // 设置下拉列表中的图标(大图标)
                .setContentTitle("下拉列表中的Title") // 设置下拉列表里的标题
                .setSmallIcon(R.mipmap.ic_launcher) // 设置状态栏内的小图标
                .setContentText("要显示的内容") // 设置上下文内容
                .setWhen(System.currentTimeMillis()) // 设置该通知发生的时间
                //.setPriority(Notification.PRIORITY_MIN) //设定为最低优先级
                .setPriority(Notification.PRIORITY_HIGH) //设定为最低优先级
                .setOngoing(true)//设定为点击后不消失
        ;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {
            mBuilder.setContentTitle(getResources().getString(R.string.app_name));
        }
        //notificationManager.notify(notifyId, mBuilder.build());
        startForeground(notifyId, mBuilder.build());
        startMyThread();            //启动主线程

    }

    @Override
    public void onDestroy() {
        Log.d(TAG, "onDestroy()");
        stopForeground(true);
        super.onDestroy();
    }

    /**
     * 自定义变量群
     */
    boolean stopThread=false;
    NotificationCompat.Builder mBuilder;
    NotificationManager notificationManager;
    int notifyId;
    int test_cnt=0;
    Calendar calendar = Calendar.getInstance();

    /**
     * 主自定义线程启动函数
     */
    private void startMyThread() {
        stopThread=false;
        Thread dateFlash=new Thread() {
            @Override
            public void run() {
                super.run();
                while (!stopThread) {
                    try {
                        Thread.sleep(1000);//每1s刷新一次
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    updateUI();
                    startForeground(notifyId, mBuilder.build());
                }
            }
        };
        dateFlash.start();
    }


    /**
     * 注册通知通道
     */
    private void registerNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            NotificationChannel notificationChannel = notificationManager.getNotificationChannel(CHANNEL_ID);
            if (notificationChannel == null) {
                NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                        CHANNEL_NAME,
                        NotificationManager.IMPORTANCE_HIGH
                        //NotificationManager.IMPORTANCE_MIN
                );
                //NotificationManager.IMPORTANCE_HIGH //NONE 为不弹出横幅,HIGH为弹出横幅
                ;
                //是否在桌面icon右上角展示小红点
                channel.enableLights(true);
                //小红点颜色
                channel.setLightColor(Color.RED);
                //通知显示
                channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
                //是否在久按桌面图标时显示此渠道的通知
                channel.setShowBadge(true);
                notificationManager.createNotificationChannel(channel);
            }
        }
    }


    private void updateUI() {
        /**
         * 显示当前任务和执行时间
         */
        mEvent now_Event = MainActivity.now_Event;
        String event_time = "";
        //Log.i(TAG, "--------flash--------");
        if (now_Event == null)
            return;
        String event_name = "";
        switch (now_Event.getEvent_id()) {
            case 10001:
                event_name = "休眠";
                break;
            case 10002:
                event_name = "恰饭";
                break;
            case 10003:
                event_name = "其他生理需求";
                break;
            case 10004:
                event_name ="开发项目 ";
                break;
            case 10005:
                event_name ="巩固技术 ";
                break;
            case 10006:
                event_name ="其他工作任务 ";
                break;
            case 10007:
                event_name ="满足求知欲 ";
                break;
            case 10008:
                event_name ="满足好胜心 ";
                break;
            case 10009:
                event_name="休闲娱乐 ";
                break;
            case 10010:
                event_name="学习";
                break;
            case 10011:
                event_name="锻炼";
                break;
            case 10012:
                event_name="自我提升 ";
                break;
        }
        calendar = Calendar.getInstance();
        int n_hour = calendar.get(Calendar.HOUR_OF_DAY);
        int n_minute = calendar.get(Calendar.MINUTE);
        int show_hour;
        int show_minute;
        int set_hour = now_Event.getStart_hour();
        int set_minute = now_Event.getStart_min();
        if (n_minute < set_minute) {
            show_hour = n_hour - set_hour - 1;
            show_minute = n_minute + 60 - set_minute;
        } else {
            show_hour = n_hour - set_hour;
            show_minute = n_minute - set_minute;
        }
        event_time = ""+show_hour + "小时" + show_minute+"分钟";//记录时间
        mBuilder.setContentTitle(event_name); // 设置下拉列表里的标题
        mBuilder.setContentText("执行了:"+ event_time);// 设置上下文内容
    }

}

效果演示图

某个正在执行的任务:
任务图
通知栏状态显示:
通知栏演示图

完整工程下载(待完成)

结语

完整的APP还在制作中,但是这一部分功能还是成功实现了。
在实现的过程中遇到了一个难题:无法隐藏或者显示横幅
生成通知时会产生一个横幅,有时候又不生成。我期望是不生成的,期望通过app内设置能够实现,但是目前设置均无效,渴望路过的大神能够解惑(〃‘▽’〃)

您可能感兴趣的与本文相关的镜像

Qwen-Image-Edit-2509

Qwen-Image-Edit-2509

图片编辑
Qwen

Qwen-Image-Edit-2509 是阿里巴巴通义千问团队于2025年9月发布的最新图像编辑AI模型,主要支持多图编辑,包括“人物+人物”、“人物+商品”等组合玩法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值