Android【Service组件】【基本概念及使用】

£神魔★判官ぃ 2022-06-14 01:49 279阅读 0赞

概念

1.Service是一个应用组件,它用来在后台完成一个时间跨度比较大的工作,且没有关联任何界面

  1. 着重理解这里的后台,去区分Service和线程的区别:
  2. Service是由主线程执行的,也就是创建主Activity的线程来执行Service,并且
  3. 是后台执行,所以它是页面无关联的组件。最重要的是,如果我们想要在Service
  4. 执行耗时操作,那么就不能够占用主线程,我们需要开启分线程来执行!!!
  5. 和线程的区别就是,线程是重新开启了一个线程,通常这种行为叫做分线程!!!

2.一个Service通常可以完成下面的工作:

  • 访问网络
  • 播放音乐
  • 文件IO操作
  • 大数据量的数据库操作等等。。。

当然,上面的操作需要开启分线程!!!因为比较耗时间!!!

3.服务的特点:

  • Service在后台运行,不用与用户进行交互。
  • 即使应用退出,服务也不会停止。

    这里需要区别一下:

    我说的应用退出有两种退出:
    1.back退出(没有被特殊的重写过。。。)。2.小米类似的清理工具杀死进程。

    back退出,Service是运行在应用程序进程中的,而back退出是不会杀死进程的,
    所以Service照常运行。

    但是,如果你通过小米清理工具一键清理后,应用程序进程将会被销毁,而进程没有
    了,Service当然也会被销毁,但是,这里有个特别的地方,那就是Service被销毁
    后能够起死回生!!!这很重要!!!

  • 在默认情况下,Service运行在应用程序进程的主线程(UI进程)中,如果需要在Service中处理一些网络连接等耗时的操作,那么应该将这些任务放在分线程中处理,避免阻塞用户界面。

Service的分类:

1.Local Service(本地服务)

Service对象与Service的启动者在同个进程中运行,两者的通信是进程内通信。那么就是本地服务。

2.Remote Service(远程服务)

Service对象与Service的启动者不在同一个进程中运行,这时存在一个进程间通信的问题,Android 专门为此设计了AIDL来实现进程间通信!!!

  1. 正常情况下,Android一个应用程序只有一个进程,但是可以创建独立进程。

Service类:

如果我们想要写一个我们的服务,那么就必须继承Service类。

我们可以发现,Service是一个抽象类,而且有着一个唯一的抽象方法onBind(Intent intent)。

这里写图片描述

查看Service的继承结构可以知道,Service能够调用Activity的大部分方法,因为Service也继承了Context类!!!

这里写图片描述

通过案例讲解Service的各种特点:

案例界面:

这里写图片描述

目的:通过该案例探讨Service的各种特点。

1.创建一个安卓项目:ServiceBestTest:

2.创建一个LocalService类,继承Service,实现我们自己的服务:

创建Service

  1. /**
  2. * Created by FireLang on 2017/6/6.
  3. */
  4. public class LocalService extends Service{
  5. /**
  6. * 关键任务:
  7. *
  8. * 查看:
  9. * 一次点击启动Service和一次点击停止Service时,LocalService的生命周期方法执行!!!
  10. * 多次点击启动Service和一次点击停止Service时,LocalService的生命周期方法执行!!!
  11. */
  12. private static final String TAG = "LocalService";
  13. /**
  14. * 一般我们不将构造方法归于生命周期方法
  15. */
  16. public LocalService (){
  17. Log.d(TAG, "LocalService:");
  18. }
  19. /**
  20. * 生命周期方法
  21. */
  22. @Override
  23. public void onCreate() {
  24. super.onCreate();
  25. Log.d(TAG, "onCreate: ");
  26. }
  27. /**
  28. * 生命周期方法
  29. */
  30. @Override
  31. public void onDestroy() {
  32. super.onDestroy();
  33. Log.d(TAG, "onDestroy: ");
  34. }
  35. //唯一的抽象方法
  36. @Override
  37. public IBinder onBind(Intent intent) {
  38. return null;
  39. }
  40. }
  41. Service中,我只重写了一部分生命周期方法,onCreate()和onDestroy()方
  42. 法,还有一个onStartComment()方法没有重写!!!
  43. 其中onBind()方法是必须重写的!在每个需要的方法中,我都打印了日志,方
  44. 便我们查看最后的探讨效果!!!
  45. 其中,我在代码最上边还给出了说明,我们要怎么去探讨Service!!!

注册Service

在AndroidManifest.xml中完成Service注册,不知道你发现没有,凡是Android的4大组件都需要在AndroidManifest.xml中完成注册!!!

这里写图片描述

写布局:(修改主Activity的布局文件)

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <LinearLayout
  3. xmlns:android="http://schemas.android.com/apk/res/android"
  4. xmlns:tools="http://schemas.android.com/tools"
  5. android:id="@+id/activity_main"
  6. android:orientation="vertical"
  7. android:layout_width="match_parent"
  8. android:layout_height="match_parent"
  9. android:paddingLeft="@dimen/activity_horizontal_margin"
  10. android:paddingRight="@dimen/activity_horizontal_margin"
  11. android:paddingTop="@dimen/activity_vertical_margin"
  12. android:paddingBottom="@dimen/activity_vertical_margin"
  13. tools:context="cn.domarvel.servicebesttest.MainActivity">
  14. <TextView
  15. android:layout_width="match_parent"
  16. android:layout_height="wrap_content"
  17. android:text="Local Service"
  18. android:gravity="center_horizontal"
  19. />
  20. <LinearLayout
  21. android:layout_width="match_parent"
  22. android:layout_height="wrap_content"
  23. android:gravity="center_horizontal"
  24. android:orientation="horizontal"
  25. >
  26. <Button
  27. android:id="@+id/StartLocalService"
  28. android:layout_width="0dp"
  29. android:layout_height="wrap_content"
  30. android:text="StartLocalService"
  31. android:textAllCaps="false"
  32. android:layout_weight="1"
  33. android:onClick="StartLocalService"
  34. />
  35. <!--
  36. android:textAllCaps:让文本都转换为大写??true或false
  37. android:layout_weight:设置weight的时候,最佳实践就是把
  38. android:layout_width设置为0dp
  39. -->
  40. <Button
  41. android:id="@+id/StopLocalService"
  42. android:layout_width="0dp"
  43. android:layout_height="wrap_content"
  44. android:text="StopLocalService"
  45. android:textAllCaps="false"
  46. android:layout_weight="1"
  47. android:onClick="StopLocalService"
  48. />
  49. </LinearLayout>
  50. <TextView
  51. android:layout_width="match_parent"
  52. android:layout_height="wrap_content"
  53. android:text="Remote Service"
  54. android:gravity="center_horizontal"
  55. />
  56. <LinearLayout
  57. android:layout_width="match_parent"
  58. android:layout_height="wrap_content"
  59. android:gravity="center_horizontal"
  60. android:orientation="horizontal"
  61. >
  62. <Button
  63. android:id="@+id/StartRemoteService"
  64. android:layout_width="0dp"
  65. android:layout_height="wrap_content"
  66. android:text="StartRemoteService"
  67. android:textAllCaps="false"
  68. android:layout_weight="1"
  69. />
  70. <Button
  71. android:id="@+id/StopRemoteService"
  72. android:layout_width="0dp"
  73. android:layout_height="wrap_content"
  74. android:text="StopRemoteService"
  75. android:textAllCaps="false"
  76. android:layout_weight="1"
  77. />
  78. </LinearLayout>
  79. </LinearLayout>

完成Activity的Java代码:

  1. public class MainActivity extends AppCompatActivity {
  2. private static final String TAG = "MainActivity";
  3. @Override
  4. protected void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. setContentView(R.layout.activity_main);
  7. }
  8. public void StartLocalService(View view) {
  9. //这是启动本地进程的Service方法
  10. /**
  11. * 启动Service有两种方式,一种是显式启动,一种是隐式启动,和启动Activity是一样的!!!
  12. * 但是这里是启动本地进程的Service,建议用显式启动。
  13. * 因为我们能够直接访问到类,而启动其它进程的Service,就用隐式启动。
  14. */
  15. //其中把this换成getApplicationContext()也可以,因为在底层源代码中,它只是通过Content对象类型获取了下包名就完了。。。
  16. //你可以打印包名试试看,最终打出来就是你Android在开始的时候取得名字。具体请看下面的图片。
  17. Intent intent = new Intent(this, LocalService.class);
  18. startService(intent);
  19. }
  20. public void StopLocalService(View view) {
  21. //这是停止本地进程的Service方法
  22. /**
  23. * 停止Service有两种方式,一种是显式停止,一种是隐式停止!!!
  24. */
  25. Intent intent = new Intent(this, LocalService.class);
  26. stopService(intent);
  27. }
  28. }

这里写图片描述

在Activity代码中,我写了两个点击事件代码,StartLocalService(View view)和StopLocalService(View view)代码。相信你能够看得懂,为什么我没有实现View.OnclickListener类就能够做关于事件的事,那是因为我在布局文件中指定了哪儿个按钮onClick后调用哪儿个方法了,这个原理很简单,相信你能够很快百度到!!!

然后就是,startService(intent)和stopService(intent),这两个都是Context中的方法,所以在服务中,我们还可以启动或者停止另外一个服务。

最后运行程序!!!

这里写图片描述

当点击一次StartLocalService按钮后,你将会在控制台看到输出:

这里写图片描述

证明,Service调用构造方法后,就马上调用onCreate方法。

当点击一次StopLocalService按钮后,你将会在控制台看到输出:

这里写图片描述

调用了onDestroy方法,证明Service已经被销毁了!!!

当多次点击StartLocalService按钮后,你将会在控制台看到输出:

这里写图片描述

证明构造方法和onCreate方法只会在Service的生命周期中调用一次!!!

当多次点击StopLocalService按钮后,你将会在控制台看到输出:

这里写图片描述

证明:当Service被关闭后再次进行关闭,是不会报错的!!!

到这里,我们已经学会启动Service了,但是还有一个方法能够启动Service,那就是bind(绑定)!!!

通过bind来启动Service:

向界面xml中添加配置:

这里写图片描述

添加下面代码:

  1. <TextView
  2. android:layout_width="match_parent"
  3. android:layout_height="wrap_content"
  4. android:text="Local Service bindService"
  5. android:gravity="center_horizontal"
  6. />
  7. <LinearLayout
  8. android:layout_width="match_parent"
  9. android:layout_height="wrap_content"
  10. android:gravity="center_horizontal"
  11. android:orientation="horizontal"
  12. >
  13. <Button
  14. android:id="@+id/BindLocalService"
  15. android:layout_width="0dp"
  16. android:layout_height="wrap_content"
  17. android:text="BindLocalService"
  18. android:textAllCaps="false"
  19. android:layout_weight="1"
  20. android:onClick="BindLocalService"
  21. />
  22. <Button
  23. android:id="@+id/UnBindLocalService"
  24. android:layout_width="0dp"
  25. android:layout_height="wrap_content"
  26. android:text="UnBindLocalService"
  27. android:textAllCaps="false"
  28. android:layout_weight="1"
  29. android:onClick="UnBindLocalService"
  30. />
  31. </LinearLayout>

向LocalService中添加代码:

  1. @Override
  2. public IBinder onBind(Intent intent) {
  3. /**
  4. * 这里返回的IBinder对象将会在Activity的onServiceConnected中接受到该参数!!!
  5. * 当返回值不是null的时候,才会触发Activity中的onServiceConnected方法。
  6. */
  7. Log.d(TAG, "onBind: ");
  8. return new Binder();
  9. }
  10. /**
  11. * 当Activity和Service断开连接后调用该方法!!!
  12. * 当重写了该方法后,才会触发Activity中的onServiceDisconnected方法。
  13. * @param intent
  14. * @return
  15. */
  16. @Override
  17. public boolean onUnbind(Intent intent) {
  18. Log.d(TAG, "onUnbind: ");
  19. return super.onUnbind(intent);
  20. }

向MainActivity中添加代码:

  1. private ServiceConnection connection;
  2. /**
  3. * 绑定Service,或者叫做创建Service后,Activity再连接Service!!!
  4. * @param view
  5. */
  6. public void BindLocalService(View view) {
  7. /**
  8. * 我们现在是测试一个Activity和Service只进行一次连接。当然也可以进行多次连接,不同Activity
  9. * 和同一个Service进行多次连接,或者不同的Activity和不同的Service连接每次都会执行onBind方
  10. * 法和onServiceConnected。
  11. * 如果是同一个Activity和同一个Service进行多次连接,连接成功后只会执行onServiceConnected方法。并且
  12. * 也就是说,每次通过bindService来开启服务,不管是同一个Activity和同一个Service进行多次绑定还是其它的情况,都会执行onServiceConnected方法,相当于执行startService后,每次都会执行startCommand方法是一个道理。
  13. * 进行多次连接,都成功后,你必须要每一个连接都关闭了才会执行onUnbind方法。如果有多个连接,你只关闭了一个连接,是不会执行onUnbind方法的。
  14. *
  15. */
  16. //这里我判断connection,我的目的就是,如果Activity和Service已经连接了,我们就没有必要再次连接了。
  17. if(connection==null){
  18. Intent intent = new Intent(getApplicationContext(),LocalService.class);
  19. connection = new ServiceConnection() {
  20. /**
  21. * 当Activity和Service连接上后
  22. * 连接时机为,Service创建成功后才开始连接,连接成功后调用该方法
  23. * @param name
  24. * @param service
  25. */
  26. @Override
  27. public void onServiceConnected(ComponentName name, IBinder service) {
  28. /**
  29. * 发现这里的IBinder对象没有,该对象就是LocalService里面的onBind()方法返回的值。
  30. */
  31. Log.d(TAG, "onServiceConnected: ");
  32. }
  33. /**
  34. * 理论上:
  35. * 当Activity和Service断开连接后,调用该方法。
  36. * 但是,最佳实践告诉我们,当Activity和Service断开连接后,该方法一直都没有被调用过!!!
  37. * @param name
  38. */
  39. @Override
  40. public void onServiceDisconnected(ComponentName name) {
  41. Log.d(TAG, "onServiceDisconnected: ");
  42. }
  43. };
  44. /**
  45. * BIND_AUTO_CREATE:创建Service后自动绑定!!!绑定可以理解为连接的意思!!!
  46. */
  47. Log.d(TAG, "BindLocalService: ");
  48. bindService(intent,connection, Context.BIND_AUTO_CREATE);
  49. }else{
  50. Log.d(TAG, "BindLocalService: Service Bind already");
  51. }
  52. }
  53. /**
  54. * 对Service解绑,Activity对Service断开连接!!!并且销毁Service!!!
  55. * @param view
  56. */
  57. public void UnBindLocalService(View view) {
  58. if(connection!=null){
  59. unbindService(connection);
  60. Log.d(TAG, "UnBindLocalService: unBind Service Success");
  61. }else{
  62. Log.d(TAG, "UnBindLocalService: You have't Bind Service");
  63. }
  64. connection = null;
  65. }

实践:

当点击一次BindLocalService后。

这里写图片描述

可以知道,我们调用了MainActivity中的BindLocalService方法,之后就是创建了LocalService对象,执行了onCreate方法,到了这里我们就已经通过BindService创建了服务,和startService不同,bindService还在启动服务的基础上,对Activity和Service进行了连接。当连接开始时,执行onBind方法,连接成功后执行onServiceConnected方法。通过一个对象连接,方便Activity和Service之间通信!!!

当一次点击UnBindLocalService后:

这里写图片描述

可以发现,解绑一个服务就等于销毁一个服务;而给Activity绑定一个服务,就相当于创建一个服务,同时在Activity和Service中间放一个对象来建立连接。

解绑时,执行了LocalService中的onUnbind()方法,代表Activity和Service开始断开连接,成功断开后,服务随即被销毁。可以看到,在服务被销毁之前,都没有执行onServiceDisconnected方法,于是大牛给出解释说,Android设计的方法不一定非得执行,如果站在积极的态度上,这就是Android给自己留的后路,算是功能的扩展!!!但是不一定得执行,相信以后需要时,就会用到的!!现在不执行代表平时开发中用不到!!!

似乎感觉自己已经掌握了用法???

下面来看看这个:

当点击BindLocalService按钮,开启并绑定了Service后,点击手机的back键。

你会发现报错了!!!

这里写图片描述

错误的原因就是:在Activity和Service建立了连接后,Activity突然就消失不见了(我们看不见Activity,不代表被销毁,destroy销毁才是Activity被销毁,而back键默认就是destroy掉Activity),强制断开了Activity和Service之间的连接,并销毁了Service。

这就是报错的原因!!!那么怎么才会不报错呢???

那么我们就需要重写Activity的onDestroy生命周期方法,在里面主动把断开Activity和Service之间的连接,并且销毁Service!!!

  1. /**
  2. * 重写Activity的onDestroy方法,在里面主动解除Activity和Service之间的连接!!!并销毁Service!!!
  3. */
  4. @Override
  5. protected void onDestroy() {
  6. super.onDestroy();
  7. if(connection!=null){
  8. unbindService(connection);
  9. }
  10. connection = null;
  11. }

重写运行应用程序,通过绑定开启服务,再back键返回主界面。你会发现程序不报错了!!!并且运行得很完美!!!

Service的生命周期

这里写图片描述

执行流程:

当通过startService()来开启服务时:

执行一次startService方法:创建Service对象—>onCreate()—>onStartCommand()—>现在Service就是运行状态了—>通过stopService()或者在Service中stopSelf()都可以关闭服务—>onDestroy()–>服务被销毁了。

当已经执行了一次startService()方法后,再次执行startService()方法。只会执行startCommand()方法。而不会再执行onCreate()方法了。

当通过bindService()方法来开启服务时:

执行一次bindService()方法:创建Service对象—>onCreate()方法—>onBind()方法—>onServiceConnected—>此时Service已经被开启了,并且Activity和Service创建了一个连接!!!—>通过unbindService()方法断开连接,并且关闭服务—>当关闭了所有连接后—>执行onUnbind()方法—>执行onDestroy()方法—>服务被关闭了。

在同一个Activity和同一个Service中,已经执行过一次bindService()方法后,再次执行bindService()方法,你会发现onBind()方法没有再执行了,而onServiceConnected()方法却是会每次都执行。现在我们通过unbindService()方法断开连接,如果我们只断开了同一个Activity和同一个Service中的一个连接,那么不会执行onUnbind()方法和Service的onDestroy()方法,只有断开同一个Activity和同一个Service中的所有连接后,才会执行onUnbind()方法和Service的onDestroy()方法。

其实我们只需要探讨同一个Activity和同一个Service的开启服务情况就行了。
通过startService和bindService()一次和多次执行,生命周期该怎么走,了解这些就可以了!!!这些在开发中已经够用了!!!剩下的,如果有特殊的,在开发中慢慢实践就好了!!!

发表评论

表情:
评论列表 (有 0 条评论,279人围观)

还没有评论,来说两句吧...

相关阅读

    相关 ELK基本概念使用

    引言: 对于刚接触ES的童鞋,经常搞不明白ES的各个概念的含义。尤其对“索引”二字更是与关系型数据库混淆的不行。本文通过对比关系型数据库,将ES中常见的增、删、改、查操作进