Service组件

Service 和Activity 一样同为Android 的四大组件之一,并且他们都有各自的生命周期,要想掌握Service 的用法,那就要了解Service 的生命周期有哪些方法,并且生命周期中各个方法回调的时机和作用

什么是service?service的基本概念

Service是Android中实现程序后台运行的解决方案,非常适合用于去执行哪些不需要和用户交互而且还要求长期运行的任务。不能运行在一个独立的进程当中,而是依赖与创建服务时所在的应用程序进程。只能在后台运行,并且可以和其他组件进行交互。

Service可以在很多场合使用,比如播放多媒体的时候用户启动了其他Activity,此时要在后台继续播放;比如检测SD卡上文件的变化;比如在后台记录你的地理信息位置的改变等等,总之服务是藏在后台的。

定义(启动)一个Service

Service 有两种启动方式,并且它的两种启动方式的生命周期是不一样的。

1.startService方式启动Service

当应用组件通过startService方法来启动Service 时,Service 则会处于启动状态,一旦服务启动,它就会在后台无限期的运行,生命周期独立于启动它的组件,即使启动它的组件已经销毁了也不受任何影响,由于启动的服务长期运行在后台,这会大量消耗手机的电量,因此,我们应该在任务执行完成之后调用stopSelf()来停止服务,或者通过其他应用组件调用stopService 来停止服务。

startService 启动服务后,会执行如下生命周期:onCreate() -> onStartCommand() -> onStart()(现在已经废弃) -> onDestroy() 。具体看一下它的几个生命周期方法:

  • onCreate() :首次启动服务的时候,系统会调用这个方法,在onStartCommand 和 onBind 方法之前,如果服务已经启动起来了,再次启动时,则不会调用此方法,因此可以在onCreate 方法中做一些初始化的操作,比如要执行耗时的操作,可以在这里创建线程,要播放音乐,可以在这里初始化音乐播放器。

  • onStartCommand(): 当通过startService 方法来启动服务的时候,在onCreate 方法之后就会回调这个方法,此方法调用后,服务就启动起来了,将会在后台无限期的运行,直到通过stopService 或者 stopSelf 方法来停止服务。

  • onDestroy():当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。  

了解了这几个生命周期方法后,就来写一个简单Service 。

要使用Service 就要通过继承Service类(或者继承IntentService ,后文会讲)来实现,代码如下:

package com.example.servicetest;import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;public class MyService extends Service {  public static final String TAG = "MyService";  //创建服务时调用
    @Override  public void onCreate() {  super.onCreate();  Log.d(TAG, "onCreate");  }  //服务执行的操作
    @Override  public int onStartCommand(Intent intent, int flags, int startId) {  Log.d(TAG, "onStartCommand");  return super.onStartCommand(intent, flags, startId);  }  //销毁服务时调用
    @Override  public void onDestroy() {  super.onDestroy();  Log.d(TAG, "onDestroy");  }  @Override  public IBinder onBind(Intent intent) {  return null;  }
}

可以看到,我们只是在onCreate()、onStartCommand()和onDestroy()方法中分别打印了一句话,并没有进行其它任何的操作,注意代码注释中这三个方法的作用。

onBind()方法是Service中唯一的一个抽象方法,所以必须要在子类里实现。我们知道,Service可以有两种启动方式:一种是startService(),另一种是bindService()。第二种启动方式才会用到onBind()方法。我们这先用第一种方式启动Service,所以暂时忽略onBind()方法。

(2)在清单文件中声明:(和Activity标签并列)

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

(3)修改activity_main.xml代码,如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  android:layout_width="match_parent"  android:layout_height="match_parent"  android:orientation="vertical" >  <Button  android:id="@+id/button1_start_service"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:text="Start Service" />  <Button  android:id="@+id/button2_stop_service"  android:layout_width="match_parent"  android:layout_height="wrap_content"  android:text="Stop Service" />  </LinearLayout>

我们在布局文件中加入了两个按钮,一个用于启动Service,一个用于停止Service。

(4)在MainActivity作为程序的主Activity,在里面加入启动Service和停止Service的逻辑,代码如下:

package com.example.servicetest;import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;public class MainActivity extends Activity implements OnClickListener {  private Button button1_start_service;  private Button button2_stop_service;  @Override  protected void onCreate(Bundle savedInstanceState) {  super.onCreate(savedInstanceState);  setContentView(R.layout.activity_main);  button1_start_service = (Button) findViewById(R.id.button1_start_service);  button2_stop_service = (Button) findViewById(R.id.button2_stop_service);  button1_start_service.setOnClickListener(this);  button2_stop_service.setOnClickListener(this);  }  @Override  public void onClick(View v) {  switch (v.getId()) {  case R.id.button1_start_service:  Intent startIntent = new Intent(this, MyService.class);  startService(startIntent);  break;  case R.id.button2_stop_service:  Intent stopIntent = new Intent(this, MyService.class);  stopService(stopIntent);  break;  default:  break;  }  }  }

代码所示,在Start Service按钮的点击事件里,我们构建出了一个Intent对象,并调用startService()方法来启动MyService。然后在Stop Serivce按钮的点击事件里,我们同样构建出了一个Intent对象,并调用stopService()方法来停止MyService。代码的逻辑非常简单。

小结:通过startService 方式启动的服务,服务会无限期的在后台运行,直到通过stopService 或 stopSelf 来终止服务。服务独立于启动它的组件,也就是说,当组件启动服务后,组件和服务就再也没有关系了,就算启动它的组件被销毁了,服务照样在后台运行。通过这种方式启动的服务不好与组件之间通信。

bindService 方式启动服务

除了startService 来启动服务之外,另外一种启动服务的方式就是通过bindService 方法了,也就是绑定服务,其实通过它的名字就容易理解,绑定即将启动组件和服务绑定在一起。前面讲的通过startService 方式启动的服务是与组件相独立的,即使启动服务的组件被销毁了,服务仍然在后台运行不受干扰。但是通过bindSerivce 方式绑定的服务就不一样了,它与绑定组件的生命周期是有关的。如下:

多个组件可以绑定到同一个服务上,如果只有一个组件绑定服务,当绑定的组件被销毁时,服务也就会停止了。如果是多个组件绑定到一个服务上,当绑定到该服务的所有组件都被销毁时,服务才会停止。

bindService 绑定服务 和startService 的生命周期是不一样,bindServie 的生命周期如下:onCreate -> onBind -> onUnbind ->onDestroy。其中重要的就是onBind 和onUnbind 方法。

  • onBind(): 当其他组件想通过bindService 与服务绑定时,系统将会回调这个方法,在实现中,你必须返回一个IBinder接口,供客户端与服务进行通信,必须实现此方法,这个方法是Service 的一个抽象方法,但是如果你不允许绑定的话,返回null 就可以了。

  • onUnbind(): 当所有与服务绑定的组件都解除绑定时,就会调用此方法。

了解了这2个方法后,我们来看一下怎么绑定一个服务。
1,首先,添加一个类 继承 Binder ,在Binder 类中添加其他组件要与服务交互的方法,并在onBind() 方法中返回IBinder 实例对象:

public class SimpleService extends Service {public static final String TAG = "SimpleService";@Nullable@Overridepublic IBinder onBind(Intent intent) {Log.i(TAG,"call onBind...");//返回IBinder 接口对象return new MyBinder();}@Overridepublic boolean onUnbind(Intent intent) {Log.i(TAG,"call onUnbind...");return super.onUnbind(intent);}@Overridepublic void onCreate() {Log.i(TAG,"call onCreate...");}@Overridepublic void onStart(Intent intent, int startId) {Log.i(TAG,"call onStart...");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i(TAG,"call onStartCommand...");return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {Log.i(TAG,"call onDestroy...");}// 添加一个类继承Binderpublic  class MyBinder extends Binder{// 添加要与外界交互的方法public String  getStringInfo(){return "调用了服务中的方法";}}}

2, 绑定服务的时候,需要提供一个ServiceConnection 接口,在接口回调中获取Binder 对象,与服务进行通信。

 private SimpleService.MyBinder mMyBinder;// 绑定/解除绑定 Service 回调接口private ServiceConnection mConnection = new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 绑定成功后回调//1 ,获取Binder接口对象mMyBinder = (SimpleService.MyBinder) service;//2, 从服务获取数据String content = mMyBinder.getStringInfo();// 3,界面提示Toast.makeText(ServiceSimpleActivity.this,content,Toast.LENGTH_LONG).show();}@Overridepublic void onServiceDisconnected(ComponentName name) {// 解除绑定后回调mMyBinder = null;}};

3,绑定和解除绑定服务

            case R.id.bind_service:Intent intent = new Intent(this,SimpleService.class);// 绑定服务
                bindService(intent,mConnection, Context.BIND_AUTO_CREATE);break;case R.id.unbind_service:// 解除绑定服务
                unbindService(mConnection);break;

可以看到,绑定服务的生命周期内依次调用了onCreate ,onBind,onUnbind 和 onDestroy 方法,只有中间两个生命周期方法与startService 启动服务是不同的。

两种方式的生命周期的异同:

         Service生命周期.png

started服务与bind服务的区别:

区别一:生命周期

  • 通过started方式的服务会一直运行在后台,需要由组件本身或外部组件来停止服务才会以结束运行
  • bind方式的服务,生命周期就要依赖绑定的组件

区别二:参数传递

  • started服务可以给启动的服务对象传递参数,但无法获取服务中方法的返回值
  • bind服务可以给启动的服务对象传递参数,也可以通过绑定的业务对象获取返回结果

实际开发中的技巧;

  • 第一次先使用started方式来启动一个服务
  • 之后可以使用bind的方式绑定服务,从而可以直接调用业务方法获取返回值

IntentService

IntentService 是Service 的子类,它使用工作线程逐一处理所有启动请求,果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现 onHandIntent方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。

IntentService 示例

IntentService 默认为我们开启了一个工作线程,在任务执行完毕后,自动停止服务,因此在我们大多数的工作中,使用IntentService 就够了,并且IntentService 比较简单,只要实现一个方法OnHandleIntent,接下来看一下示例:

(1)新建一个MyIntentService类,继承自IntentService,并重写父类的onHandleIntent()方法,代码如下:

 1 package com.example.servicetest;2 3 import android.app.IntentService;4 import android.content.Intent;5 import android.util.Log;6 7 public class MyIntentService extends IntentService{8 9     public MyIntentService() {
10         super("MyIntentService");//调用父类有参构造函数。这里我们手动给服务起个名字为:MyIntentService
11         // TODO Auto-generated constructor stub
12     }
13
14     //该方法在会在一个单独的线程中执行,来完成工作任务。任务结束后,该Service自动停止
15     @Override
16     protected void onHandleIntent(Intent intent) {
17         // TODO Auto-generated method stub
18         for(int i = 0;i<3;i++) {
19             //打印当前线程的id
20             Log.d("MyIntentService","IntentService线程的id是:"+Thread.currentThread().getId());
21             try {
22                 Thread.sleep(1000);
23             } catch (InterruptedException e) {
24                 // TODO Auto-generated catch block
25                 e.printStackTrace();
26             }
27         }
28     }
29
30     @Override
31     public void onDestroy() {
32         // TODO Auto-generated method stub
33         super.onDestroy();
34         Log.d("MyIntentService","onDestroy");
35     }
36 }

这里首先要提供一个无参的构造方法,并且必须在其内部调用父类的有参构造方法(9至12行),我们在第10行手动将服务的名字改为“MyIntentService”。

然后在子类中实现onHandleIntent()这个抽象方法,可以在这个方法里去处理一些具体的逻辑,我们就用三次for循环,打印当前线程的id,每次延时1秒。

因为这个服务在运行结束后会自动停止,所以我们在onDestroy()方法中打印日志验证一下。

(2)在清单文件中对服务进行注册服务:

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

(3)在activity_main.xml中添加一个按钮button3_stop_intentservice,用于启动MyIntentService服务,代码略。

(4)在MainActivity里面加入启动IntentService的逻辑,核心代码如下:

1       case R.id.button3_stop_intentservice:
2             Log.d("MainActivity","主线程的id是:"+Thread.currentThread().getId());
3             Intent intentService = new Intent(this,MyIntentService.class);
4             startService(intentService);
5         default:

我们在第02行中,打印主线程的id。

运行程序,点击按钮button3_stop_intentservice,显示如下:

由此可见,启动一个IntentService和启动一个普通的Service,步骤是一样的。

4、Service和Thread的关系:

不少Android初学者都可能会有这样的疑惑,Service和Thread到底有什么关系呢?什么时候应该用Service,什么时候又应该用Thread?答案可能会有点让你吃惊,因为Service和Thread之间没有任何关系!

之所以有不少人会把它们联系起来,主要就是因为Service的后台概念。Thread我们大家都知道,是用于开启一个子线程,在这里去执行一些耗时操作就不会阻塞主线程的运行。而Service我们最初理解的时候,总会觉得它是用来处理一些后台任务的,一些比较耗时的操作也可以放在这里运行,这就会让人产生混淆了。但是,如果我告诉你Service其实是运行在主线程里的,你还会觉得它和Thread有什么关系吗?

其实,后台和子线程是两个完全不同的概念:

Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,Service既然是运行在主线程里,在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。

既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例;而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。

所以说,一个比较标准的Service,就可以写成本段中第1节的样子。

IntentService总结:IntentService是Service 的子类,默认给我们开启了一个工作线程执行耗时任务,并且执行完任务后自 动停止服务。扩展IntentService比较简单,提供一个构造方法和实现onHandleIntent 方法就可了,不用重写父类的其他方法。但是如果要绑定服务的话,还是要重写onBind 返回一个IBinder 的。使用Service 可以同时执行多个请求,而使用IntentService 只能同时执行一个请求。

Service 与应用组件通信的几种方式

1,BroadcastReceiver
通过前文我们知道,startService方式启动的服务在后台,无限期地运行,并且与启动它的组件是独立的,启动Service 之后也就与启动它的组件没有任何关系了。因此它是不能与启动它的组件之间相互通信的。虽然Service 没有提供这种启动方式的通信方法,我们还是可以通过其他方式来解决的,这就用到了BroadcastReceiver。

场景描述:通过startService 启动一个长期在后台运行的下载图片服务,然后在界面上点击下载按钮,通过intent 传递一个下载链接给Service,在下载完成后,通过BroadcastReceiver 通知Activity 界面显示图片。看一下代码实现:

Service代码如下:

public class DownloadService extends Service {public static final String IMAGE = "iamge_url";public static final String RECEIVER_ACTION = "com.zhouwei.simpleservice";private static final String TAG = "DownloadService";public static final String ACTION_START_SERVICER = "com.zhouwei.startservice";public static final String ACTION_DOWNLOAD = "com.zhouwei.startdownload";private Looper mServiceLooper;private ServiceHandler mServiceHandler;private final class ServiceHandler extends Handler {public ServiceHandler(Looper looper){super(looper);}@Overridepublic void handleMessage(Message msg) {// 工作线程做耗时下载String url = (String) msg.obj;Bitmap bitmap = null;try {bitmap = Picasso.with(getApplicationContext()).load(url).get();Intent intent = new Intent();intent.putExtra("bitmap",bitmap);intent.setAction(RECEIVER_ACTION);// 通知显示
                LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent);} catch (IOException e) {e.printStackTrace();}//工作完成之后,停止服务
            stopSelf();}}@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {// 开启一个工作线程做耗时工作HandlerThread thread = new HandlerThread("ServiceHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);thread.start();// 获取工作线程的LoopermServiceLooper = thread.getLooper();// 创建工作线程的HandlermServiceHandler = new ServiceHandler(mServiceLooper);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.i(TAG,"call onStartCommand...");if(intent.getAction().equals(ACTION_DOWNLOAD)){handleCommand(intent);}else if(intent.getAction().equals(ACTION_START_SERVICER)){//do nothing
        }return START_STICKY;}private void handleCommand(Intent intent){String url = intent.getStringExtra(IMAGE);// 发送消息下载Message message = mServiceHandler.obtainMessage();message.obj = url;mServiceHandler.sendMessage(message);}
}

新建了一个DownloadService ,在里面启动了一个工作线程,在线程里下载图片,然后通过BroadcastReceiver 通知Activity显示。

Activity的代码很简单,注册BroadcastReceiver,在onReceiver中显示图片就好了,代码如下:

private ImageView mImageView;private BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {// 显示图片Bitmap bitmap = intent.getParcelableExtra("bitmap");mImageView.setImageBitmap(bitmap);}};/*** 启动下载*/private void startDownload(){Intent intent = new Intent(this,DownloadService.class);// 启动服务intent.putExtra(DownloadService.IMAGE,"http://www.8kmm.com/UploadFiles/2012/8/201208140920132659.jpg");intent.setAction(DownloadService.ACTION_DOWNLOAD);startService(intent);}

声明

https://blog.csdn.net/qq_34115898/article/details/83347882

文章来源

转载于:https://www.cnblogs.com/ythzxxq/p/9978158.html

Android Service、IntentService,Service和组件间通信相关推荐

  1. 源码分析Android Handler是如何实现线程间通信的

    源码分析Android Handler是如何实现线程间通信的 Handler作为Android消息通信的基础,它的使用是每一个开发者都必须掌握的.开发者从一开始就被告知必须在主线程中进行UI操作.但H ...

  2. arouter跨module传递消息_利用ARouter实现组件间通信,解决子模块调用主模块问题...

    一年之前我写过一篇组件间通信的博客Android模块间通信(不使用三方库),当时用的是反射,自己去维护一套"对应关系"(分别给每个模块命名,分别给每个类命名帮助反射找到对应的类), ...

  3. calces组件化与ARouter组件间通信

    calces组件化与ARouter组件间通信 calces介绍: 属性介绍 ARouter(路由) https://github.com/Tangpj/calces-gradle-plugin cal ...

  4. 容器间通信_Vue组件间通信的6种方式,前端工程师人人都会,网友:太简单了...

    Vue 组件间通信只要指以下 3 类通信:父子组件通信.隔代组件通信.兄弟组件通信,下面分别介绍每种通信方式且会说明此种方法可适用于哪类组件间通信. (1)props / $emit 适用 父子组件通 ...

  5. 深入解析Vue组件间通信

    React的基本组件元素是一个个组件,组件之间可能存在关联.组合等关系.不同的组件之间,经常会发生数据传递或者交换,我们称之为组件间通信. 根据传递的复杂程度,可以分为三种情况: 父子间通信,兄弟间通 ...

  6. Vue 组件间通信方法汇总

    Vue 组件间通信方法汇总 ⭐️ 更多前端技术和知识点,搜索订阅号 JS 菌 订阅 除了使用 Vuex 方法外,vue 提供了各种各样的组件间通信的方案.文章整理一下父子组件.兄弟组件.祖先后代组件间 ...

  7. 微信小程序组件间通信(二)

    2019独角兽企业重金招聘Python工程师标准>>> 一.微信小程序中通过事件,实现子组件向父组件中传递数据或操作 注:子组件向父组件中传递通过事件传递操作 通过事件参数对象det ...

  8. React中组件间通信的方式

    React中组件间通信的方式 React中组件间通信包括父子组件.兄弟组件.隔代组件.非嵌套组件之间通信. Props props适用于父子组件的通信,props以单向数据流的形式可以很好的完成父子组 ...

  9. Vue中组件间通信的方式

    Vue中组件间通信的方式 Vue中组件间通信包括父子组件.兄弟组件.跨级组件.非嵌套组件之间通信. props $emit props $emit适用于父子组件的通信,这种组件通信的方式是我们运用的非 ...

  10. vue组件穿方法_vue组件间通信六种方式(完整版)

    [51CTO.com原创稿件] 前言 组件是 vue.js强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用.一般来说,组件可以有以下几种关系: 如上图所示,A ...

最新文章

  1. Linux中java项目环境部署,简单记录一下
  2. linux查看新挂上的磁盘
  3. centos7离线安装oracle11g,CentOS 7.5离线安装Oracle 11gR2
  4. JDK源码学习之Arraylist与LinkedList
  5. springmvc+swagger构建Restful风格文档
  6. v-html可能导致的问题
  7. 焦作一中高考成绩查询2021,焦作高中学校排名2021最新排名,焦作高中排名前十
  8. 访问地图http://clustrmaps.com/zh/admin/action.php
  9. oracle c6,redhat6.8系统下安装oracle数据库
  10. asp.net html5 input提交,Render ASP.NET TextBox as HTML5 Input type “Number
  11. 用FreeBSD与memcached建立分布式缓存服务器全程记录之memcached使用与安装
  12. iostat 输出CPU、磁盘IO的使用情况统计信息
  13. 社交网络影响力最大化基础知识总结
  14. 打印时出现“错误!未找到引用源”“未定义书签”怎么办?
  15. 码绘VS手绘(二)动态绘图
  16. html a标签发微信,a标签的特殊和文本的样式
  17. 【腾讯开发者大会】天刀手游开发历程(笔记)
  18. AE中文破解版软件屏蔽_破解教程
  19. 2021年危险化学品经营单位主要负责人考试题库及危险化学品经营单位主要负责人免费试题
  20. ibm电脑服务器郑州维修,郑州IBM ThinkPad笔记本芯片级维修中心

热门文章

  1. 初进JAVA职场面试小技巧:一个老学长的吐血之作!
  2. BZOJ 4517 组合数+错排
  3. 数学图形(1.43)贝壳形曲线与鱼形曲线
  4. 通过NAT转发实现私网对外发布信息
  5. response.sendRedirect使用注意事项 .
  6. (转)ZwQuerySystemInformation枚举内核模块及简单应用
  7. [转]在资源管理器中使鼠标右键增加一个命令,运行cmd,同时使得当前路径为资源管理器当前的目录...
  8. [你必须知道的.NET] 第一回:恩怨情仇:is和as
  9. python读取csv某些行_【Python】Python 读取csv的某行或某列数据
  10. 前端和后端哪个工资高_新媒体运营和网络运维哪个好,哪个工资待遇高,门槛低?...