23 个重难点打破,带你吃透 Service 知识点「长达 1W+ 字」
2019-11-18杂谈搜奇网29°c
A+ A-媒介
- 学
Android
有一段时刻了,想必不少人也和我一样,日常平凡常常东学西凑,觉得知识点有些缭乱难成体系。所以趁着这几天忙里偷闲,把学的东西归结下,捋捋思绪。
这篇文章主要针对
Service
相干的知识点,举行细致的梳理,祝人人食用兴奋!
文章目次
轻易人人进修,我在 GitHub 建立了 堆栈
堆栈内容与博客同步更新。由于我在
稀土掘金
简书
CSDN
博客园
等站点,都有新内容宣布。所以人人可以直接关注该堆栈,以避免错过精彩内容!堆栈地点:
超等干货!经心归结Android
、JVM
、算法等,列位帅气的老铁支撑一下!给个 Star !
# 第一篇:Service 是什么
1.1 什么是 Service
Service
(效劳) 是一个一种可以在背景实行长时刻运转操纵而没有用户界面的运用组件。- 效劳可由其他运用组件启动(如
Activity
),效劳一旦被启动将在背景一向运转,纵然启动效劳的组件(Activity
)已烧毁也不受影响。 - 另外,组件可以绑定到效劳,以与之举行交互,以至是实行历程间通讯 (
IPC
)。
1.2 Service 平常老是称之为 “背景效劳”
- 个中 “背景” 一词是相关于前台而言的,详细是指:其本身的运转并不依赖于用户可视的
UI
界面 - 因而,从现实营业需求上来明白,
Service
的实用场景应当具有以下前提:
并不依赖于用户可视的
UI
界面(固然,这一条实在也不是相对的,如前台Service
就是与Notification
界面连系运用的)具有较长时刻的运转特性
注重: 是运转在主线程当中的
1.3 效劳历程
效劳历程是经由历程
startService()
要领启动的历程,但不属于前台历程和可见历程。比方,在背景播放音乐或许在背景下载就是效劳历程。体系坚持它们运转,除非没有充足内存来保证一切的前台历程和可视历程。
# 第二篇:生命周期
2.1 Service 的生命周期
- 我们先来看看
Service
的生命周期 的基础流程 - 一张闻名遐迩的图
2.2 开启 Service 的两种体式格局
2.2.1 startService()
定义一个类继承
Service
在
Manifest.xml
文件中设置该Service
运用
Context
的startService(intent)
要领开启效劳。运用
Context
的stopService(intent)
要领封闭效劳。该启动体式格局,
app
杀死、Activity
烧毁没有任何影响,效劳不会住手烧毁。
2.2.2 bindService()
建立
BindService
效劳端,继承Service
并在类中,建立一个完成IBinder
接口的实例对象,并供应大众要领给客户端(Activity
)挪用。从
onBinder()
回调要领返回该Binder
实例。在客户端(
Activity
)中, 从onServiceConnection()
回调要领参数中吸收Binder
,经由历程Binder
对象即可接见Service
内部的数据。在
manifests
中注册BindService
, 在客户端中挪用bindService()
要领开启绑定Service
, 挪用unbindService()
要领注销解绑Service
。该启动体式格局依赖于客户端生命周期,当客户端
Activity
烧毁时, 没有挪用unbindService()
要领 ,Service
也会住手烧毁。
2.3 Service 有哪些启动要领,有什么区分,如何停用 Service
在
Service
的生命周期中,被回调的要领比Activity
少一些,只需onCreate
,onStart
,onDestroy
,onBind
和onUnbind
。平常有两种体式格局启动一个
Service
, 他们对Service
生命周期的影响是不一样的。
2.3.1 经由历程 startService
Service
会阅历onCreate
到onStart
,然后处于运转状况,stopService
的时刻挪用onDestroy
要领。
假如是挪用者本身直接退出而没有挪用
stopService
的话,Service
会一向在背景运转。
2.3.2 经由历程 bindService
Service
会运转 onCreate
,然后是挪用 onBind
, 这个时刻挪用者和 Service
绑定在一起。挪用者退出了,Srevice
就会挪用 onUnbind
-> onDestroyed
要领。
所谓绑定在一起就共存亡了。挪用者也可以经由历程挪用
unbindService
要领来住手效劳,这时刻Srevice
就会挪用onUnbind
->onDestroyed
要领。
2.3.3 须要注重的是假如这几个要领交错在一起的话,会涌现什么状况呢?
一个原则是
Service
的onCreate
的要领只会被挪用一次,就是你不管多少次的startService
又bindService
,Service
只被建立一次。假如先是
bind
了,那末start
的时刻就直接运转Service
的onStart
要领,假如先是start
,那末bind
的时刻就直接运转onBind
要领。假如
service
运转时期挪用了bindService
,这时刻再挪用stopService
的话,service
是不会挪用onDestroy
要领的,service
就stop
不掉了,只能挪用UnbindService
,service
就会被烧毁假如一个
service
经由历程startService
被start
以后,屡次挪用startService
的话,service
会屡次调
用onStart
要领。屡次挪用stopService
的话,service
只会挪用一次onDestroyed
要领。假如一个
service
经由历程bindService
被start
以后,屡次挪用bindService
的话,service
只会挪用一次onBind
要领。屡次挪用unbindService
的话会抛出异常。
# 第三篇:Service 与 Thread
3.1 Service 和 Thread 的区分
3.1.1 起首第一点定义上
thread
是顺序实行的最小单元,他是分派cpu
的基础单元安卓体系中,我们常说的主线程,UI
线程,也是线程的一种。固然,线程内里还可以实行一些耗时的异步操纵。- 而
service
人人记着,它是安卓中的一种特别机制,service
是运转在主线程当中的,所以说它不能做耗时操纵,它是由体系历程托管,实在service
也是一种轻量级的IPC
通讯,由于activity
可以和service
绑定,可以和service
举行数据通讯。 - 而且有一种状况,
activity
和service
是处于差别的历程当中,所以说它们之间的数据通讯,要经由历程IPC
历程间通讯的机制来举行操纵。
3.1.2 第二点是在现实开辟的历程当中
- 在安卓体系当中,线程平常指的是事变线程,就是背景线程,做一些耗时操纵的线程,而主线程是一种特别的线程,它只是担任处置惩罚一些
UI
线程的绘制,UI
线程内里相对不能做耗时操纵,这里是最基础最主要的一点。(这是Thread
在现实开辟历程当中的运用) - 而
service
是安卓当中,四大组件之一,平常状况下也是运转在主线程当中,因而service
也是不可以做耗时操纵的,不然体系会报 ANR 异常(ANR
全称:Application Not Responding
),就是顺序没法做出相应。 - 假如一定要在
service
内里举行耗时操纵,一定要记得开启零丁的线程去做。
3.1.3 第三点是运用场景上
- 当你须要实行耗时的收集,或许这类文件数据的查询,以及别的壅塞
UI
线程的时刻,都应当运用事变线程,也就是开启一个子线程的体式格局。 - 如许才保证
UI
线程不被占用,而影响用户体验。 - 而
service
来讲,我们常常须要长时刻在背景运转,而且不须要举行交互的状况下才会运用到效劳,比方说,我们在背景播放音乐,开启天气预报的统计,另有一些数据的统计等等。
3.2 为何要用 Service 而不是 Thread
Thread
的运转是自力于Activity
的,也就是当一个Activity
被finish
以后,假如没有主动住手Thread
或许Thread
中的run
没有实行终了时那末这个线程会一向实行下去。- 因而这里会涌现一个题目:当
Activity
被finish
以后,你不再持有该Thread
的援用。 - 另一方面,你没有办法在差别的
Activity
中对统一Thread
举行掌握。
3.3 Service 内里是不是能实行耗时的操纵
service 内里不能实行耗时的操纵(收集请求,拷贝数据库,大文件 )
Service
不是自力的历程,也不是自力的线程,它是依赖于运用顺序的主线程的,也就是说,在更多时刻不发起在Service
中编写耗时的逻辑和操纵(比方:收集请求,拷贝数据库,大文件),不然会引起ANR
。假如想在效劳中实行耗时的使命。有以下解决方案:
- 在
service
中开启一个子线程
new Thread(){}.start();
- 可以运用
IntentService
异步治理效劳( 有关IntentService
的内容在后文中给出 )
3.4 Service 是不是在 main thread 中实行
- 默许状况, 假如没有显现的指
service
所运转的历程,Service
和activity
是运 行在当前app
地点历程的main thread
(UI
主线程)内里。 Service
和Activity
在统一个线程,关于统一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(…)
要领轮回发送闹钟播送, 吸收的时刻挪用 service
的 onstart
要领
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
放入了本身的白名单中,保证了历程不死来进步用户体验(如微信、app
一样躲避不了被杀的运气。
# 第四篇:InterService
- 作为一个老司机,假如连
Interservice
都没听说过,那就有点谁人啥了
4.1 什么是 IntentService
IntentService
是Service
的子类,比一般的Service
增加了分外的功用。我们经常使用的
Service
存在两个题目:
Service
不会特地启动一条零丁的历程,Service
与它地点运用位于统一个历程中Service
也不是特地一条新线程,因而不该当在Service
中直接处置惩罚耗时的使命
4.2 IntentService 的特性
会建立自力的
worker
线程来处置惩罚一切的Intent
请求会建立自力的
worker
线程来处置惩罚onHandleIntent()
要领完成的代码,无需处置惩罚多线程题目一切请求处置惩罚完成后,
IntentService
会自动住手,无需挪用stopSelf()
要领住手Service
为
Service
的onBind()
供应默许完成,返回null
为
Service
的onStartCommand
供应默许完成,将请求Intent
增加到行列中
4.3 Service 和 IntentService 区分
4.3.1 Service
是用于背景效劳的
- 当运用顺序被挂到背景的时刻,为了保证运用某些组件依然可以事变而引入了
Service
这个观点 - 那末这内里要强调的是:
Service
不是自力的历程,也不是自力的线程,它是依赖于运用顺序的主线程的,也就是说,在更多时刻不发起在Service
中编写耗时的逻辑和操纵,不然会引起ANR
。
也就是,service 内里不可以举行耗时的操纵。虽然在背景效劳。然则也是在主线程内里。
4.3.2 当我们编写的耗时逻辑,不得不被 service
来治理的时刻,就须要引入 IntentService
。
IntentService
是继承Service
的,那末它包括了Service
的悉数特性,固然也包括service
的生命周期。- 那末与
service
差别的是,IntentService
在实行onCreate
操纵的时刻,内部开了一个线程,去你实行你的耗时操纵。
4.3.3 运用:
- 重写
protected abstract void onHandleIntent(Intent intent)
4.3.4 IntentService
是一个经由历程 Context.startService(Intent)
启动可以处置惩罚异步请求的 Service
- 运用时你只须要继承
IntentService
和重写个中的onHandleIntent(Intent)
要领吸收一个Intent
对象 , 在恰当的时刻会住手本身 ( 平常在事变完成的时刻 ) 。 - 一切的请求的处置惩罚都在一个事变线程中完成 , 它们会交替实行 ( 但不会壅塞主线程的实行 ) ,一次只能实行一个请求。
4.3.5 是一个基于音讯的效劳
- 每次启动该效劳并非立时处置惩罚你的事变,而是起首会建立对应的
Looper
,Handler
并且在MessageQueue
中增加的附带客户Intent
的Message
对象。 - 当
Looper
发明有Message
的时刻接着取得Intent
对象经由历程在onHandleIntent((Intent)msg.obj)
中挪用你的处置惩罚顺序,处置惩罚完后即会住手本身的效劳。 - 意义是
Intent
的生命周期跟你的处置惩罚的使命是一致的,所以这个类用下载使命中异常好,下载使命完毕后效劳本身就会完毕退出。
4.3.6 总结 IntentService
的特性有:
会建立自力的
worker
线程来处置惩罚一切的Intent
请求;会建立自力的
worker
线程来处置惩罚onHandleIntent()
要领完成的代码,无需处置惩罚多线程题目;一切请求处置惩罚完成后,
IntentService
会自动住手,无需挪用stopSelf()
要领住手Service
;
# 第五篇:Service 与 Activity
5.1 Activity 怎样和 Service 绑定,怎样在 Activity 中启动对应的 Service
Activity
经由历程bindService(Intent service, ServiceConnection conn, int flags)
跟Service
举行绑定,当绑定胜利的时刻Service
会将代办对象经由历程回调的情势传给conn
,如许我们就拿到了Service
供应的效劳代办对象。在
Activity
中可以经由历程startService
和bindService
要领启动Service
。平常状况下假如想猎取Service
的效劳对象那末一定须要经由历程bindService()
要领,比方音乐播放器,第三方付出等。假如仅仅只是为了开启一个背景使命那末可以运用
startService()
要领。
5.2 说说 Activity 、Intent 、Service 是什么关联
他们都是
Android
开辟中运用频次最高的类。个中Activity
和Service
都属于Android
的四大组件。他俩都是Context
类的子类ContextWrapper
的子类,因而他俩可以算是兄弟关联吧。不过他们各有各自的本事,
Activity
担任用户界面的显现和交互,Service
担任背景使命的处置惩罚。Activity
和Service
之间可以经由历程Intent
通报数据,因而可以把Intent
看做是通讯使者。
5.3 Service 和 Activity 在统一个线程吗
关于统一 app
来讲默许状况下是在统一个线程中的,main Thread
( UI Thread
)。
5.4 Service 内里可以弹吐司么
- 可以
- 弹吐司有个前提是:得有一个
Context
上下文,而Service
本身就是Context
的子类 - 因而在
Service
内里弹吐司是完全可以的。比方我们在Service
中完成下载使命后可以弹一个吐司关照给用户。
5.5 与 Service 交互体式格局
5.5.1 播送交互
Server
端将现在的下载进度,经由历程播送的体式格局发送出来,Client
端注册此播送的监听器,当猎取到该播送后,将播送中当前的下载进度剖析出来并更新到界面上。- 定义本身的播送,如许在差别的
Activity
、Service
以及运用顺序之间,就可以经由历程播送来完成交互。
5.5.2 同享文件交互
- 我们运用
SharedPreferences
来完成同享,固然也可以运用别的IO
要领完成,经由历程这类体式格局完成交互时须要注重,关于文件的读写的时刻,统一时刻只能一方读一方写,不能两方同时写。 Server
端将当前下载进度写入同享文件中,Client
端经由历程读取同享文件中的下载进度,并更新到主界面上。
5.5.3 Messenger
交互 ( 信使交互 )
Messenger
翻译过来指的是信使,它援用了一个Handler
对象,他人可以向它发送音讯 ( 运用mMessenger.send ( Message msg )
要领)。- 该类许可跨历程间基于
Message
通讯,在效劳端运用Handler
建立一个Messenger
,客户端只需取得这个效劳端的Messenger
对象就可以与效劳端通讯了 - 在
Server
端与 Client 端之间经由历程一个Messenger
对象来通报音讯,该对象相似于信息中转站,一切信息经由历程该对象照顾
5.5.4 自定义接口交互
- 实在就是我们本身经由历程接口的完成来到达
Activity
与Service
交互的目标,我们经由历程在Activity
和Service
之间架设一座桥樑,从而到达数据交互的目标,而这类完成体式格局和AIDL
异常相似 - 自定义一个接口,该接口中有一个猎取当前下载进度的空要领。
Server
端用一个类继承自Binder
并完成该接口,覆写了个中猎取当前下载进度的要领。Client
端经由历程ServiceConnection
猎取到该类的对象,从而可以运用该猎取当前下载进度的要领,终究完成实时交互。
5.5.5 AIDL
交互
- 长途效劳平常经由历程
AIDL
来完成,可以举行历程间通讯,这类效劳也就是长途效劳。 AIDL
属于Android
的IPC
机制,经常使用于跨历程通讯,主要完成道理基于底层Binder
机制。
- Android 口试,与Service交互体式格局
# 第六篇:运用
6.1 什么状况下会运用 Service
6.1.1 经验总结:
Service
实在就是背后搞事变,又不想让他人晓得- 举一个生活当中的例子,你想晓得一件事变不须要直接去问,你可以经由历程正面相识。这就是
Service
设想的初志
6.1.2 Service
为何被设想出来
- 依据
Service
的定义,我们可以晓得须要历久在背景举行的事变我们须要将其放在Service
中去做。 - 得再通熟易懂一点,就是不能放在
Activity
中来实行的事变就必须得放到Service
中去做。 - 如:音乐播放、下载、上传大文件、定时封闭运用等功用。这些功用假如放到
Activity
中做的话,那末Activity
退出被烧毁了的话,那这些功用也就住手了,这显然是不符合我们的设想请求的,所以要将他们放在Service
中去实行。
6.2 onStartCommand() 返回值 int 值的区分
- 有四种返回值,差别值代表的意义以下:
6.2.1 START_STICKY
:
- 假如
service
历程被 kill 掉,保存service
的状况为最先状况,但不保存递送的intent
对象。 - 随后体系会尝试从新建立
service
, 由于效劳状况为最先状况,所以建立效劳后一定会挪用onStartCommand ( Intent, int, int )
要领。 - 假如在此时期没有任何启动敕令被通报到
service
, 那末参数Intent
将为null
。
6.2.2 START_NOT_STICKY
:
- “非粘性的”。
- 运用这个返回值时 , 假如在实行完
onStartCommand
后 , 效劳被异常kill
掉 ,体系不会自动重启该效劳。
6.2.3 START_REDELIVER_INTENT
:
- 重传
Intent
。 - 运用这个返回值时,假如在实行完
onStartCommand
后,效劳被异常 kill 掉 - 体系会自动重启该效劳 , 并将 Intent 的值传入。
6.2.4 START_STICKY_COMPATIBILITY
:
START_STICKY
的兼容版本 , 但不保证效劳被kill
后一定能重启。
6.3 在 service 的生命周期要领 onstartConmand() 可不可以实行收集操纵?如安在 service 中实行收集操纵?
- 可以直接在
Service
中实行收集操纵 - 在
onStartCommand()
要领中可以实行收集操纵
6.4 进步 service 的优先级
在
AndroidManifest.xml
文件中关于intent-filter
可以经由历程android:priority = “1000”
这个属性设置最高优先级,1000
是最高值,假如数字越小则优先级越低,同时实用于播送。在
onStartCommand
内里挪用startForeground()
要领把Service
提升为前台历程级别,然后再onDestroy
内里要记得挪用stopForeground ()
要领。onStartCommand
要领,手动返回START_STICKY
。
- 在
onDestroy
要领里发播送重启service
。
service
+broadcast
体式格局,就是当service
走ondestory
的时刻,发送一个自定义的播送- 当收到播送的时刻,从新启动
service
。( 第三方运用或是在setting
里-运用强迫住手时,APP
历程就直接被干掉了,onDestroy
要领都进不来,所以没法保证会实行 )
- 监听体系播送推断
Service
状况。
- 经由历程体系的一些播送
- 比方:手机重启、界面叫醒、运用状况转变等等监听并捕获到,然后推断我们的
Service
是不是还存活。
Application
加上Persistent
属性。
6.5 Service 的 onRebind ( Intent ) 要领在什么状况下会实行
- 假如在
onUnbind()
要领返回true
的状况下会实行 , 不然不实行。
# 总结
- 本文基础涵盖了
Android Service
相干的知识点。由于篇幅缘由,诸如 InterService 详细运用要领等,没办法细致的引见,人人很轻易就可以在网上找到材料举行进修。 重点
:关于Android
的四大组件,到现在为止我才总结完Activity
和Service
,我将继承针对,BroadcastRecevier
ContentProvider
等,以及四大组件以外的,事宜分发、滑动争执、新能优化等主要模块,举行周全总结,迎接人人关注_yuanhao 的 博客园 ,轻易实时吸收更新- 最先前还认为总结不难,现实写文章的历程当中,才晓得什么是艰苦。也不晓得本身能不能咬牙坚持下去,愿望人人给我勉励,就算只是一个赞,也是我坚持下去的来由!
码字不容易,你的点赞是我总结的最大动力!
由于我在「稀土掘金」「简书」「
CSDN
」「博客园」等站点,都有新内容宣布。所以人人可以直接关注我的GitHub
堆栈,以避免错过精彩内容!堆栈地点:
超等干货!经心归结Android
、JVM
、算法等,列位帅气的老铁支撑一下!给个 Star !1W
多字长文,加上优美头脑导图,记得点赞哦,迎接关注 _yuanhao 的 博客园 ,我们下篇文章见!相干文章均可在我的主页、GitHub 上看到,这里限于篇幅缘由,也为了坚持界面整齐,让人人能有跟舒心的浏览体验就不给出了,我们下篇文章不见不散!