Android前台服务的使用(一)

0学更好的别人,

做更好的自己。

——《微卡智享》

af8b6b3d34fa4fe4baf6825be01346ed.jpeg

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

前言

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

53545fc361bf66728580684cbdac6283.png

为什么要用到Service?

A

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

为什么要用前台服务?

A

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

代码实现

b6d5e63ff6c331e72e1364cbbbda3daa.png

微卡智享

01

创建Service

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

79904a5ce7c44f21895f961668c928c3.png

MySerivce代码

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代码

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代码

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中的配置

<?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代码

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)
        }
    }
}

实现效果

957b6a1639befee0a300ce46dd4c5a16.png

0406a77382ad649c95b55bdde82913a1.png

b667f296c353aedb471f81fcc0c9b7c5.png

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

4194c906d05fa885a457820420fdbfbd.png

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

a9a8f6be467c1cd62bf30586ee9eb43b.png

新建一个testSrv,plugins设置为application

MainActivity代码

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中的发送广播基本一个,只不过这里显示了点击时自己的包名,接下来看看运行效果。

实现效果

d35e24d976ab3b49ee080d8730244c6f.jpeg

87ad790360ea108f6652cd9c5c421ada.jpeg

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

d19cbe8d770f6715b705705f9ac7a3b5.png

169828838baf6e76f89f28108b8806e0.png

往期精彩回顾

c44f8987fc074897a7af07da6db04552.jpeg

Android Kotlin使用ARouter组件化路由及DataStore替代SharedPreferences保存数据


bcd32acbd748103a3e51932222e2cb30.jpeg

Android本地Sqlite数据库的备份和还原


6f9f1d48e72a4032b15118546573effb.jpeg

使用MotionLayout实现折叠屏分屏效果


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vaccae

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值