您现在的位置是:首页 >学无止境 >Android 前台服务网站首页学无止境

Android 前台服务

夜未央ぴ陌上花开丶 2023-06-19 20:00:02
简介Android 前台服务

零、前言

1.服务是什么(Service)

Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。

2.前台服务(ForegroundService)是什么?

前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。

应用场景

最常见的表现形式就是音乐播放服务,应用程序后台运行时,用户可以通过通知栏,知道当前播放内容,并进行暂停、继续、切歌等相关操作。

3.为什么用前台服务

后台运行的Service系统优先级相对较低,当系统内存不足时,在后台运行的Service就有可能被回收,为了保持后台服务的正常运行及相关操作,可以选择将需要保持运行的Service设置为前台服务,从而使APP长时间处于后台或者关闭(进程未被清理)时,服务能够保持工作。

4.小结

前台服务可以给用户提供界面上的操作。 每个前台服务都必须要在通知栏显示一个通知(notification)。用户可以感知到app的前台服务正在运行。 这个通知(notification)默认是不能移除的。服务停止后,通知会被系统移除。 当用户不需要直接操作app,app需要给用户一个状态显示的时候,可以用前台服务。

一、创建服务

import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Build;
import android.os.IBinder;
import android.text.TextUtils;
​
import com.zg.fragmentdemo.MainActivity;
import com.zg.fragmentdemo.R;
import com.zg.fragmentdemo.utils.NotificationUtils;
​
import static android.app.Notification.VISIBILITY_SECRET;
​
/***
 * @Description: 前台服务
 * channelId必须要一致,否则会报 android.app.RemoteServiceException: Bad notification for startForeground 错误
 * 8.0之上一定要使用 NotificationChannel 适配下才行
 * 步骤
 * 1.通过 “通知服务” 创建 NotificationChannel
 * 2.通过 Notification.Builder 构造器 创建 Notification
 * 3.通过 startForeground 开启服务
 * 4.高于9.0的版本 manifest需要增加  <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
 *
 * 在onCreate中创建一个广播接收器,试试能不能接收到 开单或者预约结束后的通知
 */
public class ForeService extends Service {
​
    private MessageReceiver mMsgRecv;
​
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
​
    @Override
    public void onCreate() {
        super.onCreate();
        startForeground(1, getNotification("标题", "内容"));
​
        //注册广播
        mMsgRecv = new MessageReceiver();
        IntentFilter mFilter = new IntentFilter();
        mFilter.addAction(MessageReceiver.MESSAGE_ACTION);
        registerReceiver(mMsgRecv, mFilter);
    }
​
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_STICKY;
    }
​
    @Override
    public void onDestroy() {
        super.onDestroy();
        //取消监听广播
        unregisterReceiver(mMsgRecv);
        //停止的时候销毁前台服务
        stopForeground(true);
    }
​
    private Notification getNotification(String title, String message) {
        createNotificationChannel();
        //创建一个跳转到活动页面的意图
        Intent clickIntent = new Intent(this, MainActivity.class);
        //clickIntent.putExtra("flag", count);//这里可以传值
        //创建一个用于页面跳转的延迟意图
        PendingIntent contentIntent = PendingIntent.getActivity(this, 1012, clickIntent
                , PendingIntent.FLAG_UPDATE_CURRENT);
        //创建一个通知消息的构造器
        Notification.Builder builder = new Notification.Builder(this);
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            //Android8.0开始必须给每个通知分配对应的渠道
            builder = new Notification.Builder(this, "f_channel_id");
        }
        builder.setContentIntent(contentIntent)//设置内容的点击意图
                .setAutoCancel(true)//设置是否允许自动清除
                .setSmallIcon(R.mipmap.ic_launcher)//设置状态栏里的小图标
                .setTicker("提示消息来啦")//设置状态栏里面的提示文本
                .setWhen(System.currentTimeMillis())//设置推送时间,格式为"小时:分钟"
                .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))//设置通知栏里面的大图标
                .setContentTitle(title)//设置通知栏里面的标题文本
                .setContentText(message);//设置通知栏里面的内容文本
        //根据消息构造器创建一个通知对象
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.JELLY_BEAN) {
            Notification notify = builder.build();
            return notify;
        }
        return null;
    }
​
    @TargetApi(Build.VERSION_CODES.O)
    private void createNotificationChannel() {
        NotificationChannel channel = new NotificationChannel("f_channel_id", "CHANNEL_NAME", NotificationManager.IMPORTANCE_HIGH);
        //是否绕过请勿打扰模式
        channel.canBypassDnd();
        //闪光灯
        channel.enableLights(true);
        //锁屏显示通知
        channel.setLockscreenVisibility(VISIBILITY_SECRET);
        //闪关灯的灯光颜色
        channel.setLightColor(Color.RED);
        //桌面launcher的消息角标
        channel.canShowBadge();
        //是否允许震动
        channel.enableVibration(true);
        //获取系统通知响铃声音的配置
        channel.getAudioAttributes();
        //获取通知取到组
        channel.getGroup();
        //设置可绕过  请勿打扰模式
        channel.setBypassDnd(true);
        //设置震动模式
        channel.setVibrationPattern(new long[]{100, 100, 200});
        //是否会有灯光
        channel.shouldShowLights();
        getManager().createNotificationChannel(channel);
    }
​
    private NotificationManager mManager;
​
    private NotificationManager getManager() {
        if (mManager == null) {
            mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        }
        return mManager;
    }
}

AndroidManifest.xml文件中使用前台服务

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="...">
 
    <!--  前台服务权限  -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
 
    <application ...  >
 
        <service
            android:name=".jpush.ForeService"
            android:enabled="true"
            android:exported="true" />
 
        <activity
            android:name=".MainActivity"
            android:launchMode="singleTask" />
 
    </application>
</manifest>

在manifest里注册MainActivityForeService。并且申请权限FOREGROUND_SERVICE

Activity的启动模式我们选择了singleTop。是为了方便演示点击通知时候的跳转效果。

广播接收器

public class MessageReceiver extends BroadcastReceiver {
​
    public static final String MESSAGE_ACTION = "MESSAGE_ACTION";
    public static final String MESSAGE_ID = "MESSAGE_ID";
​
    @Override
    public void onReceive(Context context, Intent intent) {
        if (MESSAGE_ACTION == intent.getAction()) {
            String messageid = intent.getStringExtra(MESSAGE_ID);
            if (!TextUtils.isEmpty(messageid)) {
                new NotificationUtils(context).sendNotification("提示", messageid);
            }
        }
    }
}

二、使用服务

Intent mForegroundService = new Intent(this, ForeService.class);
mForegroundService.putExtra("","")//传值
​
// Android 8.0使用startForegroundService在前台启动新服务
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
    startForegroundService(mForegroundService);
} else {
    startService(mForegroundService);
}

ForeService

外面调用服务,在service中,需要对应地使用startForeground方法。

@Override
public void onCreate() {
   super.onCreate();
   startForeground(1, getNotification("标题", "内容"));
}

三、暂停服务

ForeService

@Override
public void onDestroy() {
    super.onDestroy();
    //停止的时候销毁前台服务。这里只是 “是否取消掉前台服务的通知”。false表示保留通知。true不保留
    stopForeground(true);
    //stopForeground(false);//服务变成了后台服务,并没有退出。此时对应的通知可以滑动取消掉。
}

在其他的地方暂停服务

//停止服务,是停止整个服务
mForegroundService = new Intent(this, ForeService.class);
stopService(mForegroundService)

四、注意

8.0适配:通知需要加上NotificationChannel,开启前台服务的方式startForegroundService()

9.0适配:manifest.xml文件中需要增加权限

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

五、发送广播

同一个手机上另外一个程序使用广播可以推送消息给前台服务

//发送广播,跨进程通信
Intent broadcast = new Intent();
broadcast.setAction("MESSAGE_ACTION");
broadcast.putExtra("MESSAGE_ID",
        "您有新的订单");
sendOrderedBroadcast(broadcast, null);

注意:其他的手机上的应用不行。

六、通知工具类

package com.zg.fragmentdemo.utils;
​
import android.annotation.TargetApi;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.os.Build;
​
import androidx.core.app.NotificationCompat;
​
import com.zg.fragmentdemo.MainActivity;
import com.zg.fragmentdemo.R;
​
import static android.app.Notification.PRIORITY_DEFAULT;
import static android.app.Notification.VISIBILITY_SECRET;
​
public class NotificationUtils extends ContextWrapper {
​
    public static final String CHANNEL_ID = "default";
    private static final String CHANNEL_NAME = "Default Channel";
    private static final String CHANNEL_DESCRIPTION = "this is default channel!";
    private NotificationManager mManager;
​
    public NotificationUtils(Context base) {
        super(base);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            createNotificationChannel();
        }
    }
​
    @TargetApi(Build.VERSION_CODES.O)
    private void createNotificationChannel() {
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
        //是否绕过请勿打扰模式
        channel.canBypassDnd();
        //闪光灯
        channel.enableLights(true);
        //锁屏显示通知
        channel.setLockscreenVisibility(VISIBILITY_SECRET);
        //闪关灯的灯光颜色
        channel.setLightColor(Color.RED);
        //桌面launcher的消息角标
        channel.canShowBadge();
        //是否允许震动
        channel.enableVibration(true);
        //获取系统通知响铃声音的配置
        channel.getAudioAttributes();
        //获取通知取到组
        channel.getGroup();
        //设置可绕过  请勿打扰模式
        channel.setBypassDnd(true);
        //设置震动模式
        channel.setVibrationPattern(new long[]{100, 100, 200});
        //是否会有灯光
        channel.shouldShowLights();
        getManager().createNotificationChannel(channel);
    }
​
    private NotificationManager getManager() {
        if (mManager == null) {
            mManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        }
        return mManager;
    }
​
    /**
     * 发送通知
     */
    public void sendNotification(String title, String content) {
        NotificationCompat.Builder builder = getNotification(title, content);
        getManager().notify(1, builder.build());
    }
​
    private NotificationCompat.Builder getNotification(String title, String content) {
        NotificationCompat.Builder builder = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID);
        } else {
            builder = new NotificationCompat.Builder(getApplicationContext());
            builder.setPriority(PRIORITY_DEFAULT);
        }
        //标题
        builder.setContentTitle(title);
        //文本内容
        //builder.setContentText(content);
        builder.setStyle(new NotificationCompat.BigTextStyle().bigText(content));
        //小图标
        builder.setSmallIcon(R.mipmap.logo);
        //设置点击信息后自动清除通知
        builder.setAutoCancel(true);
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, -1,
                intent, PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(pendingIntent);
        return builder;
    }
​
    /**
     * 发送通知
     */
    public void sendNotification(int notifyId, String title, String content) {
        NotificationCompat.Builder builder = getNotification(title, content);
        getManager().notify(notifyId, builder.build());
    }
​
    /**
     * 发送带有进度的通知
     */
    public void sendNotificationProgress(String title, String content, int progress, PendingIntent intent) {
        NotificationCompat.Builder builder = getNotificationProgress(title, content, progress, intent);
        getManager().notify(0, builder.build());
    }
​
    /**
     * 获取带有进度的Notification
     */
    private NotificationCompat.Builder getNotificationProgress(String title, String content,
                                                               int progress, PendingIntent intent) {
        NotificationCompat.Builder builder = null;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            builder = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID);
        } else {
            builder = new NotificationCompat.Builder(getApplicationContext());
            builder.setPriority(PRIORITY_DEFAULT);
        }
        //标题
        builder.setContentTitle(title);
        //文本内容
        builder.setContentText(content);
        //小图标
        builder.setSmallIcon(R.mipmap.ic_launcher);
        //设置大图标,未设置时使用小图标代替,拉下通知栏显示的那个图标
        //设置大图片 BitmpFactory.decodeResource(Resource res,int id) 根据给定的资源Id解析成位图
        builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
        if (progress > 0 && progress < 100) {
            //一种是有进度刻度的(false),一种是循环流动的(true)
            //设置为false,表示刻度,设置为true,表示流动
            builder.setProgress(100, progress, false);
        } else {
            //0,0,false,可以将进度条隐藏
            builder.setProgress(0, 0, false);
            builder.setContentText("下载完成");
        }
        //设置点击信息后自动清除通知
        builder.setAutoCancel(true);
        //通知的时间
        builder.setWhen(System.currentTimeMillis());
        //设置点击信息后的跳转(意图)
        builder.setContentIntent(intent);
        return builder;
    }
}

参考

Android前台服务的使用(一)

Android前台服务讲解一

Android 使用前台服务

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。