service作为四大组件值得我们的更多的关注

在Android中,Activity主要负责前台页面的展示,Service主要负责需要长期运行的任务。例如,一个从service播放音乐的音乐播放器,应被设置为前台运行,因为用户会明确地注意它的运行.在状态栏中的通知可能会显示当前的歌曲并且允许用户启动一个activity来与音乐播放器交互。

Service的两种实现形式

1.非绑定

通过调用应用程序组件(例如Activity)的startService()方法来启动一个服务.一旦启动,服务就会在后台一直运行,即使应用程序组件此时被关闭.通常,已经启动的服务会处理一些单一功能,并且也不需要返回结果给调用者.例如,在网络上下载或上传文件.当服务的工作处理结束,才会自己关闭服务.

2.绑定(bind)

通过调用应用程序组件的bindService()方法来绑定一个服务.已绑定的服务会提供一个客户端-服务端交互接口.该接口主要用来与应用程序交互,发送请求,获取结果,甚至通过IPC来访问进程.只要一个程序组件绑定服务就会运行绑定服务,多个应用程序组件可以同时时间绑定一个服务.当所有的应用程序组件都解除绑定,该绑定服务器就会被销毁.

实现service的方法介绍

onStartCommand()

  系统在其它组件比如activity通过调用startService()请求service启动时调用这个方法.一旦这个方法执行,service就启动并且在后台长期运行.如果你实现了它,你需要负责在service完成任务时停止它,通过调用stopSelf()或stopService().(如果你只想提供绑定,你不需实现此方法).

OnBind()

  当组件调用bindService()想要绑定到service时(比如想要执行进程间通讯)系统调用此方法.在你的实现中,你必须提供一个返回一个IBinder来以使客户端能够使用它与service通讯,你必须总是实现这个方法,但是如果你不允许绑定,那么你应返回null.

OnCreate()

  系统在service第一次创建时执行此方法,来执行只运行一次的初始化工作(在调用它方法如onStartCommand()或onBind()之前).如果service已经运行,这个方法不会被调用.

OnDestroy()

  系统在service不再被使用并要销毁时调用此方法.你的service应在此方法中释放资源,比如线程,已注册的侦听器,接收器等等.这是service收到的最后一个调用.

  

  如果一个组件通过调用startService()启动一个service(最终导致onStartCommand()被调用),之后service会保持运行,直到它通过stopSelf()停止自己或另外的组件调用stopService()停止它.

service实现代码

1.非绑定

新建一个MyService继承自Service,并重写父类的onCreate()、onStartCommand()和onDestroy()方法

  1. public class MyIntentService extends IntentService {
  2. public MyIntentService() {
  3. super("MyIntentService");
  4. }
  5. @Override
  6. protected void onHandleIntent(Intent intent) {
  7. // IntentService会使用单独的线程来执行该方法的代码
  8. // 该方法内执行耗时任务,比如下载文件,此处只是让线程等待20秒
  9. long endTime = System.currentTimeMillis() + 20 * 1000;
  10. System.out.println("onStart");
  11. while (System.currentTimeMillis() < endTime) {
  12. synchronized (this) {
  13. try {
  14. wait(endTime - System.currentTimeMillis());
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }
  20. System.out.println("----耗时任务执行完成---");
  21. }
  22. }

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

然后打开或新建MainActivity作为程序的主Activity,在里面加入启动Service和停止Service的逻辑,代码如下所示:

  1. public class MainActivity extends Activity implements OnClickListener {
  2. private Button startService;
  3. private Button stopService;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.activity_main);
  8. startService = (Button) findViewById(R.id.start_service);
  9. stopService = (Button) findViewById(R.id.stop_service);
  10. startService.setOnClickListener(this);
  11. stopService.setOnClickListener(this);
  12. }
  13. @Override
  14. public void onClick(View v) {
  15. switch (v.getId()) {
  16. case R.id.start_service:
  17. Intent startIntent = new Intent(this, MyService.class);
  18. startService(startIntent);
  19. break;
  20. case R.id.stop_service:
  21. Intent stopIntent = new Intent(this, MyService.class);
  22. stopService(stopIntent);
  23. break;
  24. default:
  25. break;
  26. }
  27. }

项目中的每一个Service都必须在AndroidManifest.xml中注册才行,所以还需要编辑AndroidManifest.xml文件,代码如下所示:

  1. <application
  2. android:allowBackup="true"
  3. android:icon="@drawable/ic_launcher"
  4. android:label="@string/app_name"
  5. android:theme="@style/AppTheme" >
  6. ……
  7. <service android:name="com.example.servicetest.MyService" >
  8. </service>
  9. </application>

周期分析

onCreate()方法只会在Service第一次被创建的时候调用,如果当前Service已经被创建过了,不管怎样调用startService()方法,onCreate()方法都不会再执行。因此你可以再多点击几次Start Service按钮试一次,每次都只会有onStartCommand()方法中的打印日志。

2.绑定的service

  1. public class LocalService extends Service {
  2. // Binder given to clients
  3. private final IBinder mBinder = new LocalBinder();
  4. // Random number generator
  5. private final Random mGenerator = new Random();
  6. public class LocalBinder extends Binder {
  7. LocalService getService() {
  8. // 返回本service的实例到客户端,于是客户端可以调用本service的公开方法
  9. return LocalService.this;
  10. }
  11. }
  12. @Override
  13. public IBinder onBind(Intent intent) {
  14. return mBinder;
  15. }
  16. /**客户端所要调用的方法*/
  17. public int getRandomNumber() {
  18. return mGenerator.nextInt(100);
  19. }
  20. }

下面是一个绑定到LocalService并且在按钮按下时调用getRandomNumber()的actvity的例子:

  1. public class BindingActivity extends Activity {
  2. LocalService mService;
  3. boolean mBound = false;
  4. @Override
  5. protected void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. setContentView(R.layout.main);
  8. }
  9. @Override
  10. protected void onStart() {
  11. super.onStart();
  12. // 绑定到类LocalService的实例
  13. Intent intent = new Intent(this, LocalService.class);
  14. bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
  15. }
  16. @Override
  17. protected void onStop() {
  18. super.onStop();
  19. // 从service解除绑定
  20. if (mBound) {
  21. unbindService(mConnection);
  22. mBound = false;
  23. }
  24. }
  25. /** 当按钮按下时调用(在layout文件中定义的button并用android:onClick 属性指定响应到本方法) */
  26. public void onButtonClick(View v) {
  27. if (mBound) {
  28. // 调用LocalService的一个方法
  29. // 然而,如果这个调用中有挂起操作,那么这个请求应发
  30. // 生在另一个线程来避免拉低activity的性能.
  31. int num = mService.getRandomNumber();
  32. Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
  33. }
  34. }
  35. /** 定义service绑定的回调,传给bindService() 的*/
  36. private ServiceConnection mConnection = new ServiceConnection() {
  37. @Override
  38. public void onServiceConnected(ComponentName className,
  39. IBinder service) {
  40. //我们已经绑定到了LocalService,把IBinder进行强制类型转换并且获取LocalService实例.
  41. LocalBinder binder = (LocalBinder) service;
  42. mService = binder.getService();
  43. mBound = true;
  44. }
  45. @Override
  46. public void onServiceDisconnected(ComponentName arg0) {
  47. mBound = false;
  48. }
  49. };
  50. }

service的周期函数

1、当采用Context.startService()方法启动服务,与之有关的生命周期方法

onCreate()–> onStart()–> onDestroy()

onCreate()该方法在服务被创建时调用,该方法只会被调用一次,无论调用多少次startService()或bindService()方法,服务也只被创建一次。

onStart() 只有采用Context.startService()方法启动服务时才会回调该方法。该方法在服务开始运行时被调用。多次调用startService()方法尽管不会多次创建服务,但onStart() 方法会被多次调用。

onDestroy()该方法在服务被终止时调用。

2、 当采用Context.bindService()方法启动服务,与之有关的生命周期方法

onCreate()–> onBind() –> onUnbind() –> onDestroy()

onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。

onUnbind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务解除绑定时被调用。

如果先采用startService()方法启动服务,然后调用bindService()方法绑定到服务,再调用unbindService()方法解除绑定,最后调用bindService()方法再次绑定到服务,触发的生命周期方法如下:

onCreate()–>onStart()–>onBind()–>onUnbind()[重载后的方法需返回true–>onRebind()

那么如果我们既点击了Start Service按钮,又点击了Bind Service按钮会怎么样呢?

这个时候你会发现,不管你是单独点击Stop Service按钮还是Unbind Service按钮,Service都不会被销毁,必要将两个按钮都点击一下,Service才会被销毁。也就是说,点击Stop Service按钮只会让Service停止,点击Unbind Service按钮只会让Service和Activity解除关联,一个Service必须要在既没有和任何Activity关联又处理停止状态的时候才会被销毁。

Service和Thread的区别

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

Service又有何用呢?

其实大家不要把后台和子线程联系在一起就行了,这是两个完全不同的概念。

Android的后台就是指,它的运行是完全不依赖UI的。即使Activity被销毁,或者程序被关闭,只要进程还在,Service就可以继续运行。

比如说一些应用程序,始终需要与服务器之间始终保持着心跳连接,就可以使用Service来实现。你可能又会问,前面不是刚刚验证过Service是运行在主线程里的么?在这里一直执行着心跳连接,难道就不会阻塞主线程的运行吗?当然会,但是我们可以在Service中再创建一个子线程,然后在这里去处理耗时逻辑就没问题了。

使用service创建线程和activity直接创建线程的区别

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

IntentService介绍

IntentService是Service的子类,比普通的Service增加了额外的功能。

先看Service本身存在两个问题:

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

Service也不是专门一条新线程,因此不应该在Service中直接处理耗时的任务;

二、IntentService特征

会创建独立的worker线程来处理所有的Intent请求;

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

所有请求处理完成后,IntentService会自动停止,无需调用stopSelf()方法停止Service;

为Service的onBind()提供默认实现,返回null;

为Service的onStartCommand提供默认实现,将请求Intent添加到队列中;

实现

  1. public class MyIntentService extends IntentService {
  2. public MyIntentService() {
  3. super("MyIntentService");
  4. }
  5. @Override
  6. protected void onHandleIntent(Intent intent) {
  7. // IntentService会使用单独的线程来执行该方法的代码
  8. // 该方法内执行耗时任务,比如下载文件,此处只是让线程等待20秒
  9. long endTime = System.currentTimeMillis() + 20 * 1000;
  10. System.out.println("onStart");
  11. while (System.currentTimeMillis() < endTime) {
  12. synchronized (this) {
  13. try {
  14. wait(endTime - System.currentTimeMillis());
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. }
  19. }
  20. System.out.println("----耗时任务执行完成---");
  21. }
  22. }

activity代码

  1. public void startIntentService(View source) {
  2. // 创建需要启动的IntentService的Intent
  3. Intent intent = new Intent(this, MyIntentService.class);
  4. startService(intent);
  5. }

参考:

http://blog.csdn.net/guolin_blog/article/details/11952435

http://blog.csdn.net/u011067360/article/details/24523491

http://blog.csdn.net/p106786860/article/details/17885115

我的微信二维码如下,欢迎交流讨论

欢迎关注《IT面试题汇总》微信订阅号。每天推送经典面试题和面试心得技巧

微信订阅号二维码如下:

Android Service详解的更多相关文章

  1. Android Service 详解

    一个Service也是一种应用程序组件,它运行在后台以提供某种服务,通常不具有可见的用户界面.其它的应用程序组件可以启动一个 Service,即使在用户切换到另外一个应用程序后,这个Service还是 ...

  2. Android Notification 详解(一)——基本操作

    Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...

  3. Android Notification 详解——基本操作

    Android Notification 详解 版权声明:本文为博主原创文章,未经博主允许不得转载. 前几天项目中有用到 Android 通知相关的内容,索性把 Android Notificatio ...

  4. Android HandlerThread详解

    概述 Android HandlerThread使用,自带Looper消息循环的快捷类. 详细 代码下载:http://www.demodashi.com/demo/10628.html 原文地址: ...

  5. Android ActionBar详解

    Android ActionBar详解 分类: Android2014-04-30 15:23 1094人阅读 评论(0) 收藏 举报 androidActionBar   目录(?)[+]   第4 ...

  6. Android 签名详解

    Android 签名详解 AndroidOPhoneAnt设计模式Eclipse  在Android 系统中,所有安装 到 系统的应用程序都必有一个数字证书,此数字证书用于标识应用程序的作者和在应用程 ...

  7. Android编译系统详解(一)

    ++++++++++++++++++++++++++++++++++++++++++ 本文系本站原创,欢迎转载! 转载请注明出处: http://blog.csdn.net/mr_raptor/art ...

  8. Android布局详解之一:FrameLayout

      原创文章,如有转载,请注明出处:http://blog.csdn.net/yihui823/article/details/6702273 FrameLayout是最简单的布局了.所有放在布局里的 ...

  9. 【整理修订】Android.mk详解

    Android.mk详解 1. Android.mk 的应用范围 Android.mk文件是GNU Makefile的一小部分,它用来对Android程序进行编译. 一个Android.mk文件可以编 ...

随机推荐

  1. 如何使用 TeamViewer 配置QuickConnect按钮?

    QuickConnect作为TeamViewer中一个比较重要的部分,得到了很多用户的认可.那么在实际运用中,怎么才能设置网页或单个程序的QuickConnect呢?所以小编以此问题为例,教大家如何配 ...

  2. 阿里技术一面,Java研发岗

    之前过了个简单的简历面,过了几天后没打来以为凉了,然后昨晚又接到了电话,括号内容是回答说的,理解有限,不一定都对,欢迎纠正-加油每一个牛友们! 阿里一面: 1.学过哪些技术知识呢? 2.说说接口和抽象 ...

  3. 2018春招实习笔试面试总结(PHP)

    博主双非渣本计算机软件大三狗一枚,眼看着春招就要结束了,现将自己所经历的的整个春招做一个个人总结. 首先就是关于投递计划,博主自己整理了一份各大公司的春招信息,包括网申地址,开始时间,结束时间,以及自 ...

  4. 用一个div模拟textarea的实现

    <textarea> 标签定义一个多行的文本输入控件.但是它不能像div一样随着内容增加而自动增加,一言不合就出现滚动条,有是有为了更好的交互,可能需要使用div来模拟textarea的实 ...

  5. 利用Express模拟web安全之---xss的攻与防

    一.什么是XSS? 跨站脚本攻击(Cross Site Scripting),为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS.恶意 ...

  6. Java 并发编程——Executor框架和线程池原理

    Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...

  7. Spring-cloud (七)自定义HystrixCommand

    前提 1.在继续学习Hystrix之前,向关注本人博客的各位致歉 由于之前的项目起名以及服务之间的名称不是很规范,所以我修改了这些名称方便后来的代码管理,这些代码可以在本人github中找到,这里贴出 ...

  8. .NET中的各种池

    在.NET中,常用到的池有四个:字符串拘留池.线程池 .应用程序池.数据库连接池. 字符串拘留池 在.NET中字符串是不可变对象,修改字符串变量的值会产生新的对象.为降低性能消耗及减小程序集大小,.N ...

  9. JS中的DOM— —节点以及操作

    DOM操作在JS中可以说是非常常见了吧,很多网页的小功能的实现,比如一些元素的增删操作等都可以用JS来实现.那么在DOM中我们需要知道些什么才能完成一些功能的实现呢?今天这篇文章就先简单的带大家入一下 ...

  10. iOS不能交互的几种情况

    alpha <=0.01 hidden = YES userInteraction = NO 父试图不允许交互,子试图也不允许交互: 在父试图可见范围内,可以交互,超出部分失效,不能交互