前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Android前台服务的使用(一)

Android前台服务的使用(一)

作者头像
Vaccae
发布于 2022-09-28 04:23:52
发布于 2022-09-28 04:23:52
2K00
代码可运行
举报
文章被收录于专栏:微卡智享微卡智享
运行总次数:0
代码可运行

0学更好的别人,

做更好的自己。

——《微卡智享》

本文长度为5428,预计阅读7分钟

前言

最近由于工作调整,经常会在各地出差的路上,所以原创相对频率可能会慢些,当然空余时间还是会做为学习的输出,今天这篇主要就是介绍了Android的Service组件,Service做为四大组件之一,虽然没有Activity用的多,但是也会使用到,正好最近也是有个想法,先做的Demo技术验证。

为什么要用到Service?

A

其实主要原因是我这边做智能设备的,原来的App程序与硬件交互也都是整一个App下的Module实现,但是每一类的设备,可能对接的硬件不是完全一样,考虑想用单独的Service进程统一管理,App中只做业务逻辑的部分,与硬件的交互通过进程间的通讯完成,这样做的目的:减少App的包大小,因为硬件交互这块通讯成功基本很少有改动,改App里业务逻辑会需要调整或是UI界面修改,那就需要定期升级新版本,这样可以整个分享出来。

为什么要用前台服务?

A

早期写的Service都是后台运行的,而后台运行的Service优先级也相对较低 ,当系统内存不足时,在后台运行的Service有可能会被回收。而前台服务是用户可见的,并且系统内存不足时不允许系统杀死,前台服务还必须有一个状态栏的通知,只有服务被终止或从前台主动移除通知后才能被解除。

代码实现

微卡智享

01

创建Service

新建了一个有Activity的应用程序ServiceDemo,主要是程序的一些设置是需要有配置界面的,在Activity创建一个Service

MySerivce代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package pers.vaccae.servicedemo

import android.app.*
import android.content.Intent
import android.content.IntentFilter
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat

const val TAG = "MyService"
const val MESSAGE_ACTION = "MESSAGE_ACTION"
const val MESSAGE_ID = "MESSAGE_ID"

class MyService : Service() {

    private lateinit var mMsgRecv: MessageReceiver

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "service onCreate()")

        //注册广播
        mMsgRecv = MessageReceiver()
        val mFilter = IntentFilter()
        mFilter.addAction(MESSAGE_ACTION)
        registerReceiver(mMsgRecv, mFilter)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "service onStartCommand()")

        NotificationUtil.getInstance(this, MainActivity::class.java, packageName)

        val notification = NotificationUtil.mNotifiCationBuilder
            .setContentTitle("前台服务测试")
            .setContentText("我是一个前台服务的Demo")
            .setWhen(System.currentTimeMillis())
            .setSmallIcon(androidx.loader.R.drawable.notification_bg)
            .setContentIntent(NotificationUtil.mPendingIntent)
            .build()
        // 开始前台服务
        startForeground(110, notification)
        NotificationUtil.mNotificationManager.notify(1, notification)
        return super.onStartCommand(intent, flags, startId)

    }

    override fun onBind(intent: Intent): IBinder {
        Log.d(TAG, "service onBind()")
        TODO("Return the communication channel to the service.")
    }

    override fun onDestroy() {
        Log.d(TAG, "service onDestroy")
        //停止前台服务
        stopForeground(true)
        //终止广播
        unregisterReceiver(mMsgRecv)

        super.onDestroy()

    }
}

代码中创建了一个广播MessageReceiver,用于Activity点击向服务中发送消息,通过广播实现的,并且跨进程中通讯也可以通过广播来实现。

MessageReceiver代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package pers.vaccae.servicedemo

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log


class MessageReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        if(MESSAGE_ACTION == intent.action){
            val messageid = intent.getStringExtra(MESSAGE_ID)
            messageid?.let {
                Log.d(TAG, it)
                val notification = NotificationUtil.mNotifiCationBuilder
                    .setContentTitle("前台服务测试")
                    .setContentText(it)
                    .setWhen(System.currentTimeMillis())
                    .setSmallIcon(androidx.loader.R.drawable.notification_bg)
                    .setContentIntent(NotificationUtil.mPendingIntent)
                    .setSound(null)
                    .build()
                NotificationUtil.mNotificationManager.notify(1, notification)
            }
        }
    }


}

Receiver中接收到广播消息后,通过Notification中进行通知显示,在MyService中也用到了Notification,文章最初介绍前台服务时也说过前台服务还必须有一个状态栏的通知,只有服务被终止或从前台主动移除通知后才能被解除。

所以这里增加了一个NotificationUtil的类,将通知这里做了一个简单的封装,方便两边同时调用。

NotificationUtil代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package pers.vaccae.servicedemo

import android.annotation.SuppressLint
import android.app.*
import android.content.Context
import android.content.Context.NOTIFICATION_SERVICE
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat

/**
 * 作者:Vaccae
 * 邮箱:3657447@qq.com
 * 创建时间:12:17
 * 功能模块说明:
 */
class NotificationUtil {

    companion object {
        @SuppressLint("StaticFieldLeak")
        lateinit var mNotifiCationBuilder: NotificationCompat.Builder

        lateinit var mNotificationManager: NotificationManager

        @SuppressLint("StaticFieldLeak")
        lateinit var mContext: Context

        lateinit var mPendingIntent: PendingIntent

        fun <T> getInstance(context: Context, clazz: Class<T>, packageName: String) {

            mContext = context
            mNotificationManager =
                context.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
            val intent = Intent(mContext, clazz)
            mPendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0)

            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                val channel = NotificationChannel(
                    packageName,
                    packageName,
                    NotificationManager.IMPORTANCE_HIGH
                )
                channel.enableLights(true)
                channel.setShowBadge(true)
                channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
                mNotificationManager?.let { mng ->
                    mng.createNotificationChannel(channel)
                    mNotifiCationBuilder =
                        NotificationCompat.Builder(mContext, "default")
                            .setChannelId(packageName)
                }
            } else {
                mNotifiCationBuilder =
                    NotificationCompat.Builder(mContext, "default")
            }
        }
    }
}

AndroidManifest中的配置

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="pers.vaccae.servicedemo">

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

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.ServiceDemo"
        tools:targetApi="31">
        <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>

        <receiver
            android:name=".MessageReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="MESSAGE_ACTION" />
            </intent-filter>
        </receiver>

        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="MY_SERVICE" />
            </intent-filter>
        </service>
    </application>

</manifest>

上面前台的服务配置就完成了,我们在MainActivity中开启前台服务,并点击看看发送广播有没有变化。

MainActivity代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        //启动前台服务
        val srvintent = Intent(this, MyService::class.java)
        srvintent.action = "MY_SERVICE"
        startForegroundService(srvintent)

        binding.tvtest.setOnClickListener {
            //发送广播
            val broadcast = Intent()
            broadcast.action = "MESSAGE_ACTION"
            broadcast.putExtra("MESSAGE_ID","我点击了tvtest组件")
            sendOrderedBroadcast(broadcast,null)
        }
    }
}

实现效果

上图中可以看到,前台服务运行后,通知栏里显示了正在运行服务,点击TextView后,通知栏中也显示了点击的消息提示。一个简单的前台服务就这样完成了。

本来做这个的目的就是为了跨进程的通讯,所以接下来就是验证新建一个App发送广播后,当前的服务能否接收到。

新建一个testSrv,plugins设置为application

MainActivity代码

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package pers.vaccae.testsrv

import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import pers.vaccae.testsrv.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        
        binding.tvmsg.setOnClickListener{
            val broadcast = Intent()
            broadcast.action = "MESSAGE_ACTION"
            broadcast.putExtra("MESSAGE_ID","${packageName}中点击了tvmsg组件")
            sendOrderedBroadcast(broadcast,null)

        }
    }
}

代码和ServiceDemo中的发送广播基本一个,只不过这里显示了点击时自己的包名,接下来看看运行效果。

实现效果

上图中可以看到,跨进程的通讯也没有问题了,这个简单的Demo只是实现了服务端的广播接收,往往跨进程中通讯需要相互的,这样App还要写自己的Receiver,Service中还要发送广播,比较麻烦,还记得我以前写的《Android使用LiveEventBus消息实现组件间通讯》这篇文章,里面介绍时说过LiveEventBus中可以实现跨进程的消息通讯,那下一篇我们就来试试LiveEventBus在跨进程中的通讯。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-08-15,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 微卡智享 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验