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

在这里插入图片描述

Service是一种可在后台执行时间运行操作而不提供界面应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用服务仍将在后台继续运行

此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。 例如,服务可在后台处理网络事务播放音乐执行文件 I/O 或与内容提供程序进行交互

前台服务

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

启动前台服务

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

activity启动服务,调用startForegroundService(Intent)方法

startForegroundService(Intent(applicationContext, ForegroundService1::class.java))

然后service 中,需要对应地使用startForeground方法

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand flags:$flags, startId:$startId [$this] ${Thread.currentThread()}")

        val pendingIntent: PendingIntent =
                Intent(this, ForegroundDemoAct::class.java).let { notificationIntent ->
                    PendingIntent.getActivity(this, 0, notificationIntent, 0)
                }

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val chanId = "f-channel"
            val chan = NotificationChannel(chanId, "前台服务channel",
                    NotificationManager.IMPORTANCE_NONE)
            chan.lightColor = Color.BLUE
            chan.lockscreenVisibility = Notification.VISIBILITY_PRIVATE
            val service = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
            service.createNotificationChannel(chan)
            Log.d(TAG, "服务调用startForeground")

            val notification: Notification =
                    Notification.Builder(applicationContext, chanId)
                            .setContentTitle("RustFisher前台服务")
                            .setContentText("https://an.rustfisher.com")
                            .setSmallIcon(R.drawable.f_zan_1)
                            .setContentIntent(pendingIntent)
                            .build()
            startForeground(1, notification)
        } else {
            Log.d(TAG, "${Build.VERSION.SDK_INT} < O(API 26) ")
        }
        return super.onStartCommand(intent, flags, startId)
    }` 

我们来看 service 里的这段代码创建一个简单Notification

设备上会显示出一个通知,点击这个通知,会跳转到 ForegroundDemoAct 。这是之前用 PendingIntent 设置的。

停止服务

可以stopService 来停止服务

stopService(Intent(applicationContext, ForegroundService1::class.java))

这样 Service 退出,走onDestroy方法

停止前台服务

在Service中调用stopForeground(boolean)方法,能停止前台,但是不退出整个服务。 这个boolean表示是否取消掉前台服务的通知。false表示保留通知。

例如在Service中调用

stopForeground(false)

服务变成了后台服务,并没有退出。此时对应的通知可以滑动取消掉。

报错信息

ANR

在Activity调用startForegroundService(Intent)启动服务,但是不调用Service.startForeground()。 一加5手机Android10运行log如下

2021-08-26 23:03:25.352 25551-25551/com.rustfisher.tutorial2020 D/rustAppUseStartService: 调用 startForegroundService 主线信息Thread[main,5,main]
2021-08-26 23:03:25.368 25551-25551/com.rustfisher.tutorial2020 D/rustAppForeground1: onCreate Thread[main,5,main] rustfisher.com
2021-08-26 23:03:25.370 25551-25551/com.rustfisher.tutorial2020 D/rustAppForeground1: onStartCommand flags:0, startId:1 [com.rustfisher.tutorial2020.service.foreground.ForegroundService1@c77d408] Thread[main,5,main]
2021-08-26 23:03:35.375 1596-1720/? W/ActivityManager: Bringing down service while still waiting for start foreground: ServiceRecord{53d70f2 u0 com.rustfisher.tutorial2020/.service.foreground.ForegroundService1}
2021-08-26 23:03:35.382 25551-25551/com.rustfisher.tutorial2020 D/rustAppForeground1: onDestroy [com.rustfisher.tutorial2020.service.foreground.ForegroundService1@c77d408] Thread[main,5,main]

2021-08-26 23:03:52.956 1596-1720/? E/ActivityManager: ANR in com.rustfisher.tutorial2020
    PID: 25551
    Reason: Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord{53d70f2 u0 com.rustfisher.tutorial2020/.service.foreground.ForegroundService1}` 

Bad notification

我们ForegroundService1的方法onStartCommand加入startForeground。 如果startForeground(0, noti)id传入0,则会报错RemoteServiceException

29871-29871/com.rustfisher.tutorial2020 E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.rustfisher.tutorial2020, PID: 29871
    android.app.RemoteServiceException: Bad notification for startForeground

后台服务

后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩存储空间,则此服务通常是后台服务。

  • 文中的服务/Service 指的是后台服务。
  • 示例使用Kotlin实现
新建服务

我们新建一个 ServiceStartDemo 类继承 Service

class ServiceStartDemo : Service() {

    companion object {
        const val TAG = "rustAppStartDemoService"
    }

    override fun onCreate() {
        super.onCreate()
        Log.d(TAG, "onCreate ${Thread.currentThread()}")
    }

    override fun onBind(intent: Intent): IBinder? {
        Log.d(TAG, "onBind ${Thread.currentThread()}")
        return null
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand flags:$flags, startId:$startId [$this] ${Thread.currentThread()}")
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        super.onDestroy()
        Log.d(TAG, "onDestroy [$this] ${Thread.currentThread()}")
    }
}

在 AndroidManifest.xml注册这个服务

<manifest xmlns:android="http://schemas.android.com/apk/res/android" >
  <!-- ... -->
  <application>
    <service
        android:name=".service.start.ServiceStartDemo"
        android:enabled="true"
        android:exported="false" />
            <!-- ... -->
  </application>
</manifest>

注意

startService 启动服务

activity调用 startService 方法,启动服务。

startService(Intent(applicationContext, ServiceStartDemo::class.java))

调用方法后,ServiceStartDemo服务会启动起来。 首次启动的话,服务会走onCreate和onStartCommand方法。 初始化性质的代码,放在onCreate里。

服务已经存在的情况下,用startService方法启动服务,服务会走onStartCommand方法。 此时onStartCommand里的startId会自增。用这个数值可以看出这个Service对象被启动了多少次。

同时我们可以在Service的log里观察到,Service的生命周期函数是在主线程中执行的。 因此Service也可能遇到ANR问题。不能把过于耗时任务放在生命周期函数里。

Activity 与 Service 沟通

Activity 与 Service 是相互独立的组件。用startService方法启动服务并不会让 activity 持有service 的实例。 它们之间可以广播来进行沟通。或者用 EventBus 之类的工具进行沟通。

停止服务

完成任务后,我们可以停止服务。节省系统资源前面是用startService方法启动的服务,后面用stopService(Intent)来停止服务。

方法 介绍
stopService(Intent) Activity或其他组件调用这个方法,停止目标service
stopSelf() Service调用这个方法来停止自己

例如在Activity中

stopService(Intent(applicationContext, ServiceStartDemo::class.java))

在Service中

stopSelf()

一旦请求使用 stopSelf()stopService() 来停止服务,服务会走onDestroy()方法。 系统会尽快销毁服务。

绑定服务

当应用组件通过调用bindService()绑定到服务时,服务即处于绑定状态

绑定服务会提供客户端服务器接口以便组件与服务进行交互发送请求接收结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁

Service 相关面试题

1. Service是什么

Service 是 Android 四大组件之一,它可以在后台执行长时间运行操作而没有用户界面的应用组件。

Service 的启动方式有两种:startService 启动和 bindService 启动。

注意:服务与其他应用程序对象一样,在其托管进程的主线程中运行。这意味着,如果你的服务要执行任何CPU密集型(例如 MP3 播放)或阻塞(例如网络)操作,它应该在Service中再创建一个线程然后这里处理耗时操作就没问题了。

2. 注册Service需要注意什么

Service 还是运行在主线程当中的,所以如果需要执行一些复杂逻辑操作,最好在服务的内部手动创建线程进行处理,否则会出现UI线程阻塞的问题。

3. Service与Activity怎么实现通信

方法一:

方法二

4. IntentService与Service的区别intentservice的优点)

IntentService是Service的子类,是一个异步的,会自动停止的服务,很好解决传统的Service中处理完耗时操作忘记停止并销毁Service的问题

5. Service 是否在 main thread 中执行, service 里面是否 能执行耗时的操作?

默认情况,如果没有显示的指 service 所运行的进程, Service 和 activity 是运 行在当前 app 所在进程的 main thread(UI 主线程)里面

service 里面不能执行耗时的操作(网络请求,拷贝数据库,大文件 )

特殊情况 ,可以在清单文件配置 service 执行所在的进程 ,让 service 在另 外的进程中执行

<service android:name="com.baidu.location.f" android:enabled="true" android:process=":remote" >
</service>

6. Service的生命周期

Service 有绑定模式和非绑定模式,以及这两种模式混合使用方式。不同 的使用方法生命周期方法也不同

  • 非绑定模式:当第一次调用 startService 的时候执行的方法依次为 onCreate()、onStartCommand(),当 Service 关闭的时候调用 onDestory 方 法。
  • 绑定模式:第一次 bindService()的时候,执行的方法为 onCreate()、 onBind()解除绑定的时候会执行 onUnbind()、onDestory()。

上面的两种生命周期是在相对单纯的模式下的情形。我们在开发过程中还 必须注意 Service 实例只会有一个,也就是说如果当前要启动的 Service 已经存 在了那么就不会再次创建该 Service 当然也不会调用 onCreate()方法。

一个 Service 可以被多个客户进行绑定,只有所有的绑定对象都执行了

onBind() 方法后该 Service 才会销毁,不过如果有一个客户执行了 onStart() 方法,那么这个时候如果所有的 bind 客户都执行了 unBind() 该 Service 也不会 销毁。

Service 的生命周期图如下所示,帮助大家记忆

只使用 startService 启动服务的生命周期

img

只使用BindService绑定服务的生命周期

img

同时使用 startService() 启动服务、BindService() 绑定服务的生命周期

img

7. Activity、Intent、Service 是什么关系

他们都是 Android 开发中使用频率最高的类。其中 Activity 和 Service 都是 Android 四大组件之一。他俩都是 Context 类的子类 ContextWrapper子类, 因此他俩可以算是兄弟关系吧。不过兄弟俩各有各自的本领, Activity 负责用户 界面的显示和交互, Service 负责后台任务的处理。Activity 和 Service 之间可 以通过 Intent 传递数据,因此可以把 Intent 看作是通信使者。

8. Service 和 Activity 在同一个线程吗?

对于同一 app 来说默认情况下是在同一个线程中的,main Thread (UI Thread)。

9. 如何提高service的优先级

10. Service 的 onStartCommand 方法有几种返回值?各代表什么意思?

有四种返回值:

  • **START_STICKY:**如果 service 进程被 kill 掉,保留 service 的状态为开始状态,但不保留递送的 intent 对象。随 后系统会尝试重新创建 service,由于服务状态为开始状态,所以创建服务后一定会调用 onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令传递到 service,那么参数 Intent 将为 null
  • START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异常 kill 掉,系统不会自动重启该服务。
  • **START_REDELIVER_INTENT:**重传 Intent。使用这个返回值时,如果在执行完 onStartCommand 后,服务被异 常 kill 掉,系统会自动重启该服务,并将 Intent 的值传入。
  • START_STICKY_COMPATIBILITY: START_STICKY 的兼容版本,但不保证服务被 kill 后一定能重启。

11. Activity 调用 Service 中的方法都有哪些方式?

img

12. Service和Thread的区别

Service是安卓中系统的组件,它运行在独立进程的主线程中,不可以执行耗时操作。 Thread是程序执行最小单元分配 CPU 的基本单位,可以开启子线程执行耗时操作。 Service 在不同 Activity 中可以获取自身实例,可以方便的对 Service 进行操作。 Thread 在不同的 Activity 中难以获取自身实例,如果 Activity 被销毁,Thread实例就很难再获取得到。

13. 使用IntentService

IntentService 是 Scrvice 的子类,因此它不是普通的 Service,它比普通的Service 增加了额外功能

先看 Service 本身存在的两个问题。

  • Service不会专门启动一个单独的进程,Service与它所在应用位于同一个进程中。
  • Service不是一条新的线程,因此不应该在Service中直接处理耗时的任务。

IntentService正好弥补了Service的不足。

IntentService的特点:

IntentService实例

  1. 创建 SccIntentService.java 继承自 IntentService 类,重写 onHandleIntent() 方法、创建一个无参构造函数,其代码如下:
public class SccIntentService extends IntentService {
 public SccIntentService() {
 super("SccIntentService");
 }
 @Override
 protected void onHandleIntent(Intent intent) {
 MLog.e(getClass().getName(), "onHandleWork");
 for (int i = 0; i < 3; i++) {
 try {
 MLog.e(getClass().getName(), "Number:开始"+i);
 Thread.sleep(10000);
 MLog.e(getClass().getName(), "Number:结束"+i);
 } catch (InterruptedException e) {
 e.printStackTrace();
 }
 }
 }
 @Override
 public void onDestroy() {
 super.onDestroy();
 MLog.e(getClass().getName(), "onDestroy");
 }
}

2. 添加 IntentService 组件声明,在 AndroidManifest.xml 文件中声明一个 Service 组件,其代码如下:

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

3. 启动 SccIntentService

startService(new Intent(ServiceActivity.this, SccIntentService.class));</pre>

4. 运行结果

07-07 18:00:39.505 E/-SCC-: com.scc.demo.actvitiy.ServiceActivityonCreate
07-07 18:00:39.531 E/-SCC-: com.scc.demo.actvitiy.ServiceActivityonStart
07-07 18:00:39.531 E/-SCC-: com.scc.demo.actvitiy.ServiceActivityonResume
07-07 18:01:12.690 E/-SCC-com.scc.demo.service.SccIntentService: onHandleWork
07-07 18:01:12.690 E/-SCC-com.scc.demo.service.SccIntentService: Number:开始0
07-07 18:01:22.691 E/-SCC-com.scc.demo.service.SccIntentService: Number:结束0
07-07 18:01:22.697 E/-SCC-com.scc.demo.service.SccIntentService: Number:开始1
07-07 18:01:32.698 E/-SCC-com.scc.demo.service.SccIntentService: Number:结束1
07-07 18:01:32.698 E/-SCC-com.scc.demo.service.SccIntentService: Number:开始2
07-07 18:01:42.699 E/-SCC-com.scc.demo.service.SccIntentService: Number:结束2
07-07 18:01:42.716 E/-SCC-com.scc.demo.service.SccIntentService: onDestroy

普通 Service 直接执行 20S 的的耗时操作,会阻塞主线程,造成 ANR (程序响应)异常

IntentService 执行30S的耗时操作,不会阻塞主线程,更不会产生ANR。如上图开始18:01:12>18:01:42长达30S,正常运行未产生ANR。

IntentService还有个好处就是 「用完即走」。执行完onHandleIntent()方法里面的耗时操作后,自行调用onDestroy()方法,进行关闭

更多Android零基础入门精通资料完整资料可以扫码免费领取!!!

腾讯技术团队出品】Android零基础入门精通,Android Studio安装教程+全套安卓基础教程

Android编程入门教程

Java语言基础从入门到熟悉

在这里插入图片描述

Kotlin语言基础从入门到熟悉

在这里插入图片描述

Android 技术栈从入门到熟悉

在这里插入图片描述

Android Jetpack 全家桶全面学习

在这里插入图片描述

对于新手来说可能安装Android Studio存在一定困难你可以看着以下视频一步步的跟着学习安装运行

Android Studio 安装教程

在这里插入图片描述

有了Java阶段学习,这一阶段建议以视频学习为主辅以图书查漏补缺。如果以图书为主,可以根据图书讲解代码,辅以教学视频查漏补缺。遇到问题可以去百度,入门的问题一般会有很多遇到,并且给出比较好的解答。

需要掌握基本知识点比如四大组件如何使用、如何创建Service、如何进行布局简单自定义View动画网络通信常见技术。

全套零基础教程已经为你们准备好了,需要的可以添加下方二维码免费领取

全套安卓基础教程

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

原文地址:https://blog.csdn.net/Android23333/article/details/132423453

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_49799.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注