23 个重难点突破,带你吃透 Service 知识点「长达 1W+ 字」

前言

  • Android 有一段时间了,想必很多人也和我同样,平时常常东学西凑,感受知识点有些凌乱难成体系。因此趁着这几天忙里偷闲,把学的东西概括下,捋捋思路。

这篇文章主要针对 Service 相关的知识点,进行详细的梳理,祝你们食用愉快!html

文章目录

文章目录

方便你们学习,我在 GitHub 创建了 仓库


# 第一篇:Service 是什么

Service 是什么

1.1 什么是 Service

什么是 Service

  • Service (服务) 是一个一种能够在后台执行长时间运行操做而没有用户界面的应用组件。
  • 服务可由其余应用组件启动(如 Activity ),服务一旦被启动将在后台一直运行,即便启动服务的组件( Activity )已销毁也不受影响。
  • 此外,组件能够绑定到服务,以与之进行交互,甚至是执行进程间通讯 ( IPC )。

1.2 Service 一般老是称之为 “后台服务”

Service 一般老是称之为 “后台服务”

  • 其中 “后台” 一词是相对于前台而言的,具体是指:其自己的运行并不依赖于用户可视的 UI 界面
  • 所以,从实际业务需求上来理解,Service 的适用场景应该具有如下条件:
  1. 并不依赖于用户可视的 UI 界面(固然,这一条其实也不是绝对的,如前台 Service 就是与 Notification 界面结合使用的)git

  2. 具备较长时间的运行特性github

  3. 注意: 是运行在主线程当中的面试

1.3 服务进程

服务进程

  • 服务进程是经过 startService() 方法启动的进程,但不属于前台进程和可见进程。例如,在后台播放音乐或者在后台下载就是服务进程。算法

  • 系统保持它们运行,除非没有足够内存来保证全部的前台进程和可视进程。数据库

# 第二篇:生命周期

生命周期

2.1 Service 的生命周期

  • 咱们先来看看 Service 的生命周期 的基本流程
  • 一张闻名遐迩的图
    Service的生命周期

2.2 开启 Service 的两种方式

开启 Service 的两种方式

2.2.1 startService()

startService()

  1. 定义一个类继承 Service微信

  2. Manifest.xml 文件中配置该 Service网络

  3. 使用 ContextstartService(intent) 方法开启服务。

  4. 使用 ContextstopService(intent) 方法关闭服务。

  5. 该启动方式,app 杀死、Activity 销毁没有任何影响,服务不会中止销毁。

2.2.2 bindService()

bindService()

  1. 建立 BindService 服务端,继承 Service 并在类中,建立一个实现 IBinder 接口的实例对象,并提供公共方法给客户端( Activity )调用。

  2. onBinder() 回调方法返回该 Binder 实例。

  3. 在客户端( Activity )中, 从 onServiceConnection() 回调方法参数中接收 Binder ,经过 Binder 对象便可访问 Service 内部的数据。

  4. manifests 中注册 BindService , 在客户端中调用 bindService() 方法开启绑定 Service , 调用 unbindService() 方法注销解绑 Service

  5. 该启动方式依赖于客户端生命周期,当客户端 Activity 销毁时, 没有调用 unbindService() 方法 , Service 也会中止销毁。

2.3 Service 有哪些启动方法,有什么区别,怎样停用 Service

Service 的启动与绑定

  • Service 的生命周期中,被回调的方法比 Activity 少一些,只有 onCreate , onStart , onDestroy , onBindonUnbind

  • 一般有两种方式启动一个 Service , 他们对 Service 生命周期的影响是不同的。

2.3.1 经过 startService

被启动的服务的生命周期

  • Service 会经历 onCreateonStart ,而后处于运行状态,stopService 的时候调用 onDestroy
    方法。

若是是调用者本身直接退出而没有调用 stopService 的话,Service 会一直在后台运行。

2.3.2 经过 bindService

被绑定的服务的生命周期

Service 会运行 onCreate ,而后是调用 onBind , 这个时候调用者和 Service 绑定在一块儿。调用者退出了,Srevice 就会调用 onUnbind -> onDestroyed 方法。

所谓绑定在一块儿就共存亡了。调用者也能够经过调用 unbindService 方法来中止服务,这时候 Srevice 就会调用 onUnbind -> onDestroyed 方法。

2.3.3 须要注意的是若是这几个方法交织在一块儿的话,会出现什么状况呢?

被启动又被绑定的服务的生命周期

  1. 一个原则是 ServiceonCreate 的方法只会被调用一次,就是你不管多少次的 startServicebindServiceService 只被建立一次。

  2. 若是先是 bind 了,那么 start 的时候就直接运行 ServiceonStart 方法,若是先是 start ,那么 bind 的时候就直接运行 onBind 方法。

  3. 若是 service 运行期间调用了 bindService ,这时候再调用 stopService 的话,service 是不会调用 onDestroy 方法的,servicestop 不掉了,只能调用 UnbindService , service 就会被销毁

  4. 若是一个 service 经过 startServicestart 以后,屡次调用 startService 的话,service 会屡次调
    onStart 方法。屡次调用 stopService 的话,service 只会调用一次 onDestroyed 方法。

  5. 若是一个 service 经过 bindServicestart 以后,屡次调用 bindService 的话,service 只会调用一次 onBind 方法。屡次调用 unbindService 的话会抛出异常。

# 第三篇:Service 与 Thread

Service 与 Thread

3.1 Service 和 Thread 的区别

Service 和 Thread 的区别

3.1.1 首先第一点定义上

定义上

  1. thread 是程序执行的最小单元,他是分配 cpu 的基本单位安卓系统中,咱们常说的主线程,UI 线程,也是线程的一种。固然,线程里面还能够执行一些耗时的异步操做。
  2. service 你们记住,它是安卓中的一种特殊机制,service 是运行在主线程当中的,因此说它不能作耗时操做,它是由系统进程托管,其实 service 也是一种轻量级的 IPC 通讯,由于 activity 能够和 service 绑定,能够和 service 进行数据通讯。
  3. 并且有一种状况,activityservice 是处于不一样的进程当中,因此说它们之间的数据通讯,要经过 IPC 进程间通讯的机制来进行操做。

3.1.2 第二点是在实际开发的过程中

第二点是在实际开发的过程中

  1. 在安卓系统当中,线程通常指的是工做线程,就是后台线程,作一些耗时操做的线程,而主线程是一种特殊的线程,它只是负责处理一些 UI 线程的绘制,UI 线程里面绝对不能作耗时操做,这里是最基本最重要的一点。(这是 Thread 在实际开发过程中的应用)
  2. service 是安卓当中,四大组件之一,通常状况下也是运行在主线程当中,所以 service 也是不能够作耗时操做的,不然系统会报 ANR 异常( ANR 全称:Application Not Responding ),就是程序没法作出响应。
  3. 若是必定要在 service 里面进行耗时操做,必定要记得开启单独的线程去作。

3.1.3 第三点是应用场景上

应用场景上

  1. 当你须要执行耗时的网络,或者这种文件数据的查询,以及其它阻塞 UI 线程的时候,都应该使用工做线程,也就是开启一个子线程的方式。
  2. 这样才能保证 UI 线程不被占用,而影响用户体验。
  3. service 来讲,咱们常常须要长时间在后台运行,并且不须要进行交互的状况下才会使用到服务,好比说,咱们在后台播放音乐,开启天气预报的统计,还有一些数据的统计等等。

3.2 为何要用 Service 而不是 Thread

为何要用 Service 而不是 Thread

  • Thread 的运行是独立于 Activity 的,也就是当一个 Activityfinish 以后,若是没有主动中止 Thread 或者 Thread 中的 run 没有执行完毕时那么这个线程会一直执行下去。
  • 所以这里会出现一个问题:当 Activityfinish 以后,你再也不持有该 Thread 的引用。
  • 另外一方面,你没有办法在不一样的 Activity 中对同一 Thread 进行控制。

3.3 Service 里面是否能执行耗时的操做

Service 里面是否能执行耗时的操做

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

  • Service 不是独立的进程,也不是独立的线程,它是依赖于应用程序的主线程的,也就是说,在更多时候不建议在 Service 中编写耗时的逻辑和操做(好比:网络请求,拷贝数据库,大文件),不然会引发 ANR

  • 若是想在服务中执行耗时的任务。有如下解决方案:

  1. service 中开启一个子线程
new Thread(){}.start();
  1. 可使用 IntentService 异步管理服务( 有关 IntentService 的内容在后文中给出 )

3.4 Service 是否在 main thread 中执行

Service 是否在 main thread 中执行

  • 默认状况, 若是没有显示的指 service 所运行的进程, Serviceactivity 是运 行在当前 app 所在进程的 main thread ( UI 主线程)里面。
  • ServiceActivity 在同一个线程,对于同一 app 来讲默认状况下是在同一个线程中的 main Thread ( UI Thread )
  • 特殊状况 ,能够在清单文件配置 service 执行所在的进程 ,让 service 在另 外的进程中执行 Service 不死之身

3.4.1 在 onStartCommand 方法中将 flag 设置为 START_STICKY ;

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

3.4.2 在 xml 中设置了 android:priority

<!--设置服务的优先级为MAX_VALUE-->
 <service android:name=".MyService"
          android:priority="2147483647"
          >
 </service>

3.4.3 在 onStartCommand 方法中设置为前台进程

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
  Notification notification = new Notification(R.mipmap.ic_launcher, "服务正在运行",System.currentTimeMillis());
   Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,notificationIntent,0);
    RemoteViews remoteView = new RemoteViews(this.getPackageName(),R.layout.notification);
    remoteView.setImageViewResource(R.id.image, R.mipmap.ic_launcher);
    remoteView.setTextViewText(R.id.text , "Hello,this message is in a custom expanded view");
    notification.contentView = remoteView;
    notification.contentIntent = pendingIntent;
    startForeground(1, notification);
    return Service.START_STICKY;
}

3.4.4 在 onDestroy 方法中重启 service

@Override
public void onDestroy() {
    super.onDestroy();
    startService(new Intent(this, MyService.class));
}

3.4.5 用 AlarmManager.setRepeating(…) 方法循环发送闹钟广播, 接收的时候调用 serviceonstart 方法

Intent intent = new Intent(MainActivity.this,MyAlarmReciver.class);
PendingIntent sender = PendingIntent.getBroadcast( MainActivity.this, 0, intent, 0);

// We want the alarm to go off 10 seconds from now.
Calendar calendar = Calendar.getInstance();
calendar.setTimeInMillis(System.currentTimeMillis());
calendar.add(Calendar.SECOND, 1);
AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
//重复闹钟
/**
 *  @param type
 * @param triggerAtMillis t 闹钟的第一次执行时间,以毫秒为单位
 * go off, using the appropriate clock (depending on the alarm type).
 * @param intervalMillis 表示两次闹钟执行的间隔时间,也是以毫秒为单位
 * of the alarm.
 * @param operation 绑定了闹钟的执行动做,好比发送一个广播、给出提示等等
 */
am.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 2 * 1000, sender);

3.4.6 目前市场面的不少三方的消息推送 SDK 唤醒 APP , 例如 Jpush

PS: 以上这些方法并不表明着你的 Service 就永生不死了,只能说是提升了进程的优先级。迄今为止我没有发现可以经过常规方法达到流氓需求 (经过长按 home 键清除都清除不掉) 的方法,目前全部方法都是指经过 Android 的内存回收机制和普通的第三方内存清除等手段后仍然保持运行的方法,有些手机厂商把这些知名的 app 放入了本身的白名单中,保证了进程不死来提升用户体验(如微信、QQ 、陌陌都在小米的白名单中)。若是从白名单中移除,他们终究仍是和普通 app 同样躲避不了被杀的命运。

# 第四篇:InterService

  • 做为一个老司机,若是连 Interservice 都没据说过,那就有点那个啥了

InterService

4.1 什么是 IntentService

什么是 IntentService

  • IntentServiceService 的子类,比普通的 Service 增长了额外的功能。

  • 咱们经常使用的 Service 存在两个问题:

  1. Service 不会专门启动一条单独的进程,Service 与它所在应用位于同一个进程中

  2. Service 也不是专门一条新线程,所以不该该在 Service 中直接处理耗时的任务

4.2 IntentService 的特征

IntentService 的特征

  • 会建立独立的 worker 线程来处理全部的 Intent 请求

  • 会建立独立的 worker 线程来处理 onHandleIntent() 方法实现的代码,无需处理多线程问题

  • 全部请求处理完成后,IntentService 会自动中止,无需调用 stopSelf() 方法中止 Service

  • ServiceonBind() 提供默认实现,返回 null

  • ServiceonStartCommand 提供默认实现,将请求 Intent 添加到队列中

4.3 Service 和 IntentService 区别

Service 和 IntentService 区别

4.3.1 Service 是用于后台服务的

Service 是用于后台服务的

  1. 当应用程序被挂到后台的时候,为了保证应用某些组件仍然能够工做而引入了 Service 这个概念
  2. 那么这里面要强调的是:Service 不是独立的进程,也不是独立的线程,它是依赖于应用程序的主线程的,也就是说,在更多时候不建议在 Service 中编写耗时的逻辑和操做,不然会引发 ANR

也就是,service 里面不能够进行耗时的操做。虽然在后台服务。可是也是在主线程里面。

4.3.2 当咱们编写的耗时逻辑,不得不被 service 来管理的时候,就须要引入 IntentService

耗时逻辑

  1. IntentService 是继承 Service 的,那么它包含了 Service 的所有特性,固然也包含 service 的生命周期。
  2. 那么与 service 不一样的是,IntentService 在执行 onCreate 操做的时候,内部开了一个线程,去你执行你的耗时操做。

4.3.3 使用:

使用

  1. 重写 protected abstract void onHandleIntent(Intent intent)

4.3.4 IntentService 是一个经过 Context.startService(Intent) 启动能够处理异步请求的 Service

经过 Context.startService(Intent) 启动

  1. 使用时你只须要继承 IntentService 和重写其中的 onHandleIntent(Intent) 方法接收一个 Intent 对象 , 在适当的时候会中止本身 ( 通常在工做完成的时候 ) 。
  2. 全部的请求的处理都在一个工做线程中完成 , 它们会交替执行 ( 但不会阻塞主线程的执行 ) ,一次只能执行一个请求。

4.3.5 是一个基于消息的服务

是一个基于消息的服务

  1. 每次启动该服务并非立刻处理你的工做,而是首先会建立对应的 LooperHandler 而且在 MessageQueue 中添加的附带客户 IntentMessage 对象。
  2. Looper 发现有 Message 的时候接着获得 Intent 对象经过在 onHandleIntent((Intent)msg.obj) 中调用你的处理程序,处理完后即会中止本身的服务。
  3. 意思是 Intent 的生命周期跟你的处理的任务是一致的,因此这个类用下载任务中很是好,下载任务结束后服务自身就会结束退出。

4.3.6 总结 IntentService 的特征有:

总结 IntentService 的特征

  1. 会建立独立的 worker 线程来处理全部的 Intent 请求;

  2. 会建立独立的 worker 线程来处理 onHandleIntent() 方法实现的代码,无需处理多线程问题;

  3. 全部请求处理完成后,IntentService会自动中止,无需调用 stopSelf() 方法中止 Service

# 第五篇:Service 与 Activity

Service 与 Activity

5.1 Activity 怎么和 Service 绑定,怎么在 Activity 中启动对应的 Service

Service 与 Activity

  • Activity 经过 bindService(Intent service, ServiceConnection conn, int flags)Service 进行绑定,当绑定成功的时候 Service 会将代理对象经过回调的形式传给 conn ,这样咱们就拿到了 Service 提供的服务代理对象。

  • Activity 中能够经过 startServicebindService 方法启动 Service。通常状况下若是想获取 Service 的服务对象那么确定须要经过 bindService() 方法,好比音乐播放器,第三方支付等。

  • 若是仅仅只是为了开启一个后台任务那么可使用 startService() 方法。

5.2 说说 Activity 、Intent 、Service 是什么关系

Activity 、Intent 、Service 是什么关系

  • 他们都是 Android 开发中使用频率最高的类。其中 ActivityService 都属于 Android 的四大组件。他俩都是 Context 类的子类 ContextWrapper 的子类,所以他俩能够算是兄弟关系吧。

  • 不过他们各有各自的本领,Activity 负责用户界面的显示和交互,Service 负责后台任务的处理。

  • ActivityService 之间能够经过 Intent 传递数据,所以能够把 Intent 看做是通讯使者。

5.3 Service 和 Activity 在同一个线程吗

Service 和 Activity 在同一个线程吗

对于同一 app 来讲默认状况下是在同一个线程中的,main ThreadUI Thread )。

5.4 Service 里面能够弹吐司么

Service 里面能够弹吐司么

  • 能够
  • 弹吐司有个条件是:得有一个 Context 上下文,而 Service 自己就是 Context 的子类
  • 所以在 Service 里面弹吐司是彻底能够的。好比咱们在 Service 中完成下载任务后能够弹一个吐司通知给用户。

5.5 与 Service 交互方式

与 Service 交互方式

5.5.1 广播交互

广播交互

  1. Server 端将目前的下载进度,经过广播的方式发送出来,Client 端注册此广播的监听器,当获取到该广播后,将广播中当前的下载进度解析出来并更新到界面上。
  2. 定义本身的广播,这样在不一样的 ActivityService 以及应用程序之间,就能够经过广播来实现交互。

5.5.2 共享文件交互

共享文件交互

  1. 咱们使用 SharedPreferences 来实现共享,固然也可使用其它 IO 方法实现,经过这种方式实现交互时须要注意,对于文件的读写的时候,同一时间只能一方读一方写,不能两方同时写。
  2. Server 端将当前下载进度写入共享文件中,Client 端经过读取共享文件中的下载进度,并更新到主界面上。

5.5.3 Messenger 交互 ( 信使交互 )

Messenger 交互 ( 信使交互 )

  1. Messenger 翻译过来指的是信使,它引用了一个 Handler 对象,别人可以向它发送消息 ( 使用 mMessenger.send ( Message msg ) 方法)。
  2. 该类容许跨进程间基于 Message 通讯,在服务端使用 Handler 建立一个 Messenger ,客户端只要得到这个服务端的 Messenger 对象就能够与服务端通讯了
  3. Server 端与 Client 端之间经过一个 Messenger 对象来传递消息,该对象相似于信息中转站,全部信息经过该对象携带

5.5.4 自定义接口交互

自定义接口交互

  1. 其实就是咱们本身经过接口的实现来达到 ActivityService 交互的目的,咱们经过在 ActivityService 之间架设一座桥樑,从而达到数据交互的目的,而这种实现方式和 AIDL 很是相似
  2. 自定义一个接口,该接口中有一个获取当前下载进度的空方法。Server 端用一个类继承自 Binder 并实现该接口,覆写了其中获取当前下载进度的方法。Client 端经过 ServiceConnection 获取到该类的对象,从而可以使用该获取当前下载进度的方法,最终实现实时交互。

5.5.5 AIDL 交互

AIDL交互

  1. 远程服务通常经过 AIDL 来实现,能够进行进程间通讯,这种服务也就是远程服务。
  2. AIDL 属于 AndroidIPC 机制,经常使用于跨进程通讯,主要实现原理基于底层 Binder 机制。

# 第六篇:使用

使用

6.1 什么状况下会使用 Service

什么状况下会使用 Service

6.1.1 经验总结:

经验总结

  1. Service 其实就是背地搞事情,又不想让别人知道
  2. 举一个生活当中的例子,你想知道一件事情不须要直接去问,你能够经过侧面了解。这就是 Service 设计的初衷

6.1.2 Service 为何被设计出来

Service 为何被设计出来

  1. 根据 Service 的定义,咱们能够知道须要长期在后台进行的工做咱们须要将其放在 Service 中去作。
  2. 得再通熟易懂一点,就是不能放在 Activity 中来执行的工做就必须得放到 Service 中去作。
  3. 如:音乐播放、下载、上传大文件、定时关闭应用等功能。这些功能若是放到 Activity 中作的话,那么 Activity 退出被销毁了的话,那这些功能也就中止了,这显然是不符合咱们的设计要求的,因此要将他们放在 Service 中去执行。

6.2 onStartCommand() 返回值 int 值的区别

  • 有四种返回值,不一样值表明的意思以下:

onStartCommand() 返回值 int 值的区别

6.2.1 START_STICKY :

  1. 若是 service 进程被 kill 掉,保留 service 的状态为开始状态,但不保留递送的 intent 对象。
  2. 随后系统会尝试从新建立 service, 因为服务状态为开始状态,因此建立服务后必定会调用 onStartCommand ( Intent, int, int ) 方法。
  3. 若是在此期间没有任何启动命令被传递到 service , 那么参数 Intent 将为 null

6.2.2 START_NOT_STICKY :

  1. “非粘性的”。
  2. 使用这个返回值时 , 若是在执行完 onStartCommand 后 , 服务被异常 kill 掉 ,系统不会自动重启该服务。

6.2.3 START_REDELIVER_INTENT:

  1. 重传 Intent
  2. 使用这个返回值时,若是在执行完 onStartCommand 后,服务被异常 kill 掉
  3. 系统会自动重启该服务 , 并将 Intent 的值传入。

6.2.4 START_STICKY_COMPATIBILITY:

  1. START_STICKY 的兼容版本 , 但不保证服务被 kill 后必定能重启。

6.3 在 service 的生命周期方法 onstartConmand() 可不能够执行网络操做?如何在 service 中执行网络操做?

onstartConmand() 可不能够执行网络操做?如何在 service 中执行网络操做?

  • 能够直接在 Service 中执行网络操做
  • onStartCommand() 方法中能够执行网络操做

6.4 提升 service 的优先级

提升 service 的优先级

  • AndroidManifest.xml 文件中对于 intent-filter 能够经过 android:priority = “1000” 这个属性设置最高优先级,1000 是最高值,若是数字越小则优先级越低,同时实用于广播。

  • onStartCommand 里面调用 startForeground() 方法把 Service 提高为前台进程级别,而后再 onDestroy 里面要记得调用 stopForeground () 方法。

  • onStartCommand 方法,手动返回 START_STICKY

广播

  • onDestroy 方法里发广播重启 service
  1. service + broadcast 方式,就是当 serviceondestory 的时候,发送一个自定义的广播
  2. 当收到广播的时候,从新启动 service 。( 第三方应用或是在 setting 里-应用强制中止时,APP 进程就直接被干掉了,onDestroy 方法都进不来,因此没法保证会执行 )
  • 监听系统广播判断 Service 状态。
  1. 经过系统的一些广播
  2. 好比:手机重启、界面唤醒、应用状态改变等等监听并捕获到,而后判断咱们的 Service 是否还存活。
  • Application 加上 Persistent 属性。

6.5 Service 的 onRebind ( Intent ) 方法在什么状况下会执行

onRebind ( Intent ) 方法在什么状况下会执行

  • 若是在 onUnbind() 方法返回 true 的状况下会执行 , 不然不执行。

# 总结

  1. 本文基本涵盖了 Android Service 相关的知识点。因为篇幅缘由,诸如 InterService 具体使用方法等,没办法详细的介绍,你们很容易就能在网上找到资料进行学习。
  2. 重点:关于 Android 的四大组件,到如今为止我才总结完 ActivityService,我将继续针对,BroadcastRecevier ContentProvider 等,以及四大组件以外的,事件分发、滑动冲突、新能优化等重要模块,进行全面总结,欢迎你们关注_yuanhao 的 博客园 ,方便及时接收更新
  3. 开始前还觉得总结不难,实际写文章的过程当中,才知道什么是艰辛。也不知道本身能不能咬牙坚持下去,但愿你们给我鼓励,就算只是一个赞,也是我坚持下去的理由!

码字不易,你的点赞是我总结的最大动力!


相关文章
相关标签/搜索