Service简单概述

Service(服务):是一个没有用户界面、可以在后台长期运行且可以执行操作的应用组件。服务可由其他应用组件启动(如:Activity、另一个service)。此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信 (IPC)。例如:服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而这一切均可在后台进行。


进程的优先级

了解进程的优先级可以帮助你理解服务~

  • 1. Foreground process(前台进程)

    • 一句话总结:当前跟用户有交互的进程就是前台进程。
  • 2. Visible process(可见进程)

    • 相当于Activity的onPause方法执行。如:Activity弹出Dialog时。
  • 3. Service process(服务进程)

    • 相当于使用startService开启一个服务,虽然做的事情用户看不到,但是又是用户所关心的事情。如:后台播放音乐,下载数据等。
  • 4. Background process(后台进程)

    • 相当于Activity的onStop方法被执行。如:按Home键退出应用。
  • 5. Empty process(空进程)

    • 应用程序没有任何的组件在活动,该应用就是一个空进程。唯一存活的原因就是为了提高下次开启的时间。

开启服务的两种方式

服务:默认运行在主线程。

服务的创建流程

  1. 自定义一个服务类继承 android.app.Service;
  2. 在清单文件中配置 service(AndroidManifest.xml);
  3. 在服务类中重写方法。
<service android:name=".service.MyCustomService"></service>

服务的两种开启方式

一:start 方式

先上代码

    private Intent startIntent;
//start方式:开启服务
public void startOpenService(View v) {
//创建一个开启服务的Intent对象
startIntent = new Intent(this, MyCustomService.class);
this.startService(startIntent); //开启一个服务
} //start方式:关闭服务
public void startCloseService(View v) {
if (startIntent != null) {
this.stopService(startIntent);
startIntent = null;
}
}

1. 通过 startService(Intent service)开启服务

  • onCreate()、onStartCommand() 两个方法在服务第一次开启的时候会被依次执行。
  • 当服务开启之后,再点击开启服务,只会执行onStartCommand()方法。

2. 通过 stopService(Intent service)关闭服务

  • onDestroy() 方法会在执行 stopService 方法(关闭服务)的时候,被执行!
  • 注意参数:Intent对象不能为null,要先进行判空(不为null的情况下,可以多次调用)。

特别说明

1. stopService传入的Intent对象,必须和startService传入的Intent对象是同一个对象,才能保证开启的服务被关闭。

2. 应用退出后(应用在后台运作,未被杀死),服务依然会运行中。

3. 当手动杀掉应用进程后,服务将会终止!且该方式不会执行服务的onDestroy()方法。

二:bind 方式

先上代码

    private Intent bindIntent;
private MyServiceConnection connection;
private boolean isSuccess;
//bind方式:开启服务
public void bindOpenService(View v) {
if (!isSuccess) {
bindIntent = new Intent(this, MyCustomService.class);
//boolean bindService(Intent service, //开启服务的意图对象
// ServiceConnection conn, //服务连接对象
// int flags) //绑定服务操作选项()
connection = new MyServiceConnection();
isSuccess = this.bindService(bindIntent, connection, Context.BIND_AUTO_CREATE);
}
} //bind方式:解绑服务
public void bindCloseService(View v) {
//void unbindService(ServiceConnection conn)
if (connection != null) {
this.unbindService(connection);
connection = null;
isSuccess = false;
}
}

1. 通过 bindService() 开启服务

  • onCreate()、onBind() 两个方法在服务第一次开启的时候,被依次执行。
  • 再次操作bindService的话,不会有什么方法被执行。若要使用该方式开启服务的话,建议获取绑定后的状态,若成功则不再操作绑定。

2. 通过 unbindService() 解绑服务

  • onUnbind()、onDestroy() 两个方法会在执行服务解绑 unbindService 方法的时候,被依次执行。所以解绑只能操作一次。

特别说明

1. 绑定和解绑传递的ServiceConnection对象要保证是同一个对象!

2. isSuccess存储服务绑定成功(true)的状态,当绑定成功之后,避免重复的绑定。因为每次bindService传递的ServiceConnection对象都是new的新对象,unbindService传递的ServiceConnection对象可能会与服务绑定时传递的对象不一致,就会抛出异常!

3. 解绑之后,isSuccess赋值false,就可以再次操作绑定了。

4. bind方式开启的服务是一个隐形的服务,在设置中无法找到(其实现在有些手机定制系统,start方式开启的服务也成了一个隐形服务了)。

5. bind方式开启服务与开启者(Activity),存在着依附关系,在开启者被销毁前,必须解绑bind方式开启的服务,不然会抛出异常!(不求同生,但求同死。)


服务模板代码

需求:在Activity中,使用 bind方式 启动一个服务,并调用服务中的方法(模拟一些业务处理)。

分析:流程步骤

  1. 创建一个服务类,继承 Service。如:MyCustomService;
  2. 自定义一个服务接口对象类,实现ServiceConnection接口。如:MyServiceConnection;
  3. 自定义一个中间帮助类,继承Binder类(IBinder实现类)。当服务中的onBind方法被执行的时候,作为返回值。如:MyBinderImpl;
  4. 自定义一个接口,封装一些中间帮助类对象共有的函数。如:MyBinderInterface。

整个流程代码如下

MyCustomService.class

import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
import android.widget.Toast;
/**
* 创建一个服务类
*/
public class MyCustomService extends Service { @Override
public IBinder onBind(Intent intent) {
//该方法:在使用【bind绑定】的方式:开启服务的时候,才会被调用
Log.e("Service生命周期", "【onBind】");
//返回值需要一个IBinder接口实现类对象(可以返回自定义实现类对象)
return new MyBinderInter();
}
@Override
public boolean onUnbind(Intent intent) {
Log.e("Service生命周期", "【onUnbind】");
return super.onUnbind(intent);
} @Override
public void onCreate() {
super.onCreate();
//服务第一次创建时调用
Log.e("Service生命周期", "【onCreate】");
//获取服务运行线程
String name = Thread.currentThread().getName();
long id = Thread.currentThread().getId();
Log.e("线程", "【Service】" + name + "-" + id);
} @Override
public int onStartCommand(Intent intent, int flags, int startId) {
//每次开启服务(Activity内调用startService()方法)时调用
Log.e("Service生命周期", "【onStartCommand】");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
//服务被销毁时调用
Log.e("Service生命周期", "【onDestroy】");
} //模拟:定义一些函数,作为业务处理
public void playPoker() {
Toast.makeText(this, "玩扑克", Toast.LENGTH_SHORT).show();
} public void playBall() {
Toast.makeText(this, "打球", Toast.LENGTH_SHORT).show();
} /**
* 中间帮助类:自定义类继承 Binder(IBinder 接口实现类)
*/
public class MyBinderInter extends Binder implements MyBinderInterface { @Override
public void callPlayPoker() {
playPoker();
} @Override
public void callPlayBall() {
playBall();
}
}
}

MyServiceConnection.class

import android.content.ComponentName;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.util.Log;
/**
* 创建一个服务接口对象类:当服务绑定成功时,可以接收一个中间帮助类对象
* 当 MyCustomService 中的 onBind 方法返回值不为null时,该服务连接对象类中的方法才会被执行
*/
public class MyServiceConnection implements ServiceConnection { private MyCustomService.MyBinderInter myBinderInter; @Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//服务绑定成功后执行(onBind执行后执行该方法) IBinder:中间帮助类对象
this.myBinderInter = (MyCustomService.MyBinderInter) iBinder;
Log.e("Service生命周期", "【onServiceConnected】");
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
//该方法在连接正常关闭的情况下不会被执行,只有在Service被破坏或杀死的情况下执行。
//如:系统资源不足,需要杀掉该服务,则会执行该方法。
} public void callPlayPoker(){
if(myBinderInter!=null){
myBinderInter.callPlayPoker();
}
} public void callPlayBall(){
if(myBinderInter!=null){
myBinderInter.callPlayBall();
}
}
}

接口:MyBinderInterface

//自定义接口:封装中间帮助类所共有的一些方法
public interface MyBinderInterface {
//随意定义两个抽象方法,由实现类重写
void callPlayPoker();
void callPlayBall();
}

ServiceActivity 内调用服务内的方法

  • 切记:在activity执行onDestroy()方法的时候,解绑服务,否则会抛出异常。
public class ServiceActivity extends BaseActivity {

    private Intent bindIntent;
private MyServiceConnection connection;
private boolean isSuccess; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_service);
} //bind方式:开启服务
public void bindOpenService(View v) {
if (!isSuccess) {
bindIntent = new Intent(this, MyCustomService.class);
connection = new MyServiceConnection();
isSuccess = this.bindService(bindIntent, connection, Context.BIND_AUTO_CREATE);
}
} public void playPoker(View v) {
if (connection != null) {
connection.callPlayPoker();
}
} public void playBall(View v) {
if (connection != null) {
connection.callPlayBall();
}
} @Override
protected void onDestroy() {
super.onDestroy();
if (connection != null) {
this.unbindService(connection);
connection = null;
isSuccess = false;
}
}
}


关于:bindService和unbindService的源码说明(英文好的小伙伴可以一起学习)

boolean bindService (Intent service, ServiceConnection conn, int flags)

  • 方法说明

    Connect to an application service, creating it if needed. This defines a dependency between your application and the service. The given conn will receive the service object when it is created and be told if it dies and restarts. The service will be considered required by the system only for as long as the calling context exists. For example, if this Context is an Activity that is stopped, the service will not be required to continue running until the Activity is resumed.

    This function will throw SecurityException if you do not have permission to bind to the given service.

    Note: this method can not be called from a BroadcastReceiver component. A pattern you can use to communicate from a BroadcastReceiver to a Service is to call startService(Intent) with the arguments containing the command to be sent, with the service calling its stopSelf(int) method when done executing that command. See the API demo App/Service/Service Start Arguments Controller for an illustration of this. It is okay, however, to use this method from a BroadcastReceiver that has been registered with registerReceiver(BroadcastReceiver, IntentFilter), since the lifetime of this BroadcastReceiver is tied to another object (the one that registered it).
  • Parameters(参数说明)

    service:Identifies the service to connect to. The Intent may specify either an explicit component name, or a logical description (action, category, etc) to match an IntentFilter published by a service.

    conn:Receives information as the service is started and stopped. This must be a valid ServiceConnection object; it must not be null.

    flags:Operation options for the binding. May be 0, BIND_AUTO_CREATE, BIND_DEBUG_UNBIND, BIND_NOT_FOREGROUND, BIND_ABOVE_CLIENT, BIND_ALLOW_OOM_MANAGEMENT, or BIND_WAIVE_PRIORITY.
  • Returns(返回值说明)

    If you have successfully bound to the service, true is returned; false is returned if the connection is not made so you will not receive the service object.

void unbindService (ServiceConnection conn)

  • 方法说明

    Disconnect from an application service. You will no longer receive calls as the service is restarted, and the service is now allowed to stop at any time.
  • Parameters(参数说明)

    conn:The connection interface previously supplied to bindService(). This parameter must not be null.

参考链接:Google官方文档:Service

PS:期待与大家有更多的交流,谢谢~

Android四大组件之服务的两种启动方式详解的更多相关文章

  1. Android四大组件之服务-Service 原理和应用开发详解

    一.Android 服务简介 Service是android 系统中的四大组件之一(Activity.Service.BroadcastReceiver.ContentProvider),它跟Acti ...

  2. Android四大组件之——Activity的生命周期(图文详解)

        转载请在文章开头处注明本博客网址:http://www.cnblogs.com/JohnTsai       联系方式:JohnTsai.Work@gmail.com       [Andro ...

  3. UEFI与 Legacy BIOS两种启动模式详解

    (1). UEFI启动模式 与 legacy启动模式 legacy启动模式: 就是这么多年来PC一直在使用的启动方式(从MBR中加载启动程序),UEFI BIOS作为一种新的BIOS自然也应该兼容这种 ...

  4. Android为TV端助力 Service 两种启动方式的区别

    服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务.这两个方法都 可以启动Service,但是它们的使用场合有所不同.使 ...

  5. Android Activity 的四种启动模式 lunchMode 和 Intent.setFlags();singleTask的两种启动方式。

    原文:Android Activity 的四种启动模式 lunchMode 和 Intent.setFlags();singleTask的两种启动方式. Android Activity 的四种启动模 ...

  6. 第一章 Mybtais的两种启动方式

    Mybatis的两种启动方式如下: 1.xml实现: xml的实现方式中,主要是通过手动创建SqlSession,然后调用session.selectOne()方法实现来实现. 首先是创建Config ...

  7. ARM的两种启动方式 (NAND FLASH. NOR FLASH)

    为什么会有两种启动方式? 这就是有两种FLASH 的不同特点决定的. NAND FLASH 容量大,存储的单位比特数据的成本要低很多,但是要按照特定的时序对NAND  FLASH  进行读写,因此CP ...

  8. Spring事务Transaction配置的五种注入方式详解

    Spring事务Transaction配置的五种注入方式详解 前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识.通过这次的学 ...

  9. python selenium 三种等待方式详解[转]

    python selenium 三种等待方式详解   引言: 当你觉得你的定位没有问题,但是却直接报了元素不可见,那你就可以考虑是不是因为程序运行太快或者页面加载太慢造成了元素不可见,那就必须要加等待 ...

随机推荐

  1. 携程PMO--如何召开卓有成效的回顾会

      话题介绍   回顾会提供团队反思迭代过程并提出改进措施的机会.回顾会是团队成员共同进行的协作活动,让团队成员跟进并落实改进措施,使团队在下一个冲刺中更高效,这是相当重要的.   我们给出了回顾会的 ...

  2. python paramiko外部传参和内部调用命令的方法

    学习了很久的python,但在工作中使用的时候,却发现不知道怎么传参进入到python中执行,所以这两天就研究 了python args怎么将外部参数传入到python中执行 1.首先使用python ...

  3. MVC整体运行流程一(进入管道)

    1.在浏览器输入 https://www.cnblogs.com/zhangmm96/发送一个HTTP到web服务器,Web服务器WIndows内核中的HTTP.SYS组件捕捉当前请求,该组件分析出是 ...

  4. Codeforces 246C

    题意略. 思路: 我们将数组中的数字从大到小排列,分别考虑取前0 + 1,1 + 1,2 + 1.....个的情况. 所谓i + 1的意思是,取前i个的时候,同时取第[i + 1],[i + 2],. ...

  5. 百度地图小Demo---获取当前地址以及拖拽显示地址

    1.效果图 2.源码 主要使用百度地图的JavaScript API文件,以及一个JQuery文件. <!doctype html> <html lang="en" ...

  6. explain详解 和 show profiles

    explain出的有下列几项: 1.select_type 2.type 1.all 全表扫描,从表头扫描到表尾: 2.index 根据索引来读取数据,如果索引已包含了查询数据,只需扫描索引树,否则执 ...

  7. win命令获取外网ip

    win命令: chcp 65001 curl https://ip.cn bat: @echo offchcp 65001 && curl https://ip.cnpause 链接: ...

  8. JavaScript 数据结构与算法之美 - 归并排序、快速排序、希尔排序、堆排序

    1. 前言 算法为王. 想学好前端,先练好内功,只有内功深厚者,前端之路才会走得更远. 笔者写的 JavaScript 数据结构与算法之美 系列用的语言是 JavaScript ,旨在入门数据结构与算 ...

  9. 通过PHP与Python代码对比浅析语法差异

    一.背景 人工智能这几年一直都比较火,笔者一直想去学习一番:因为一直是从事PHP开发工作,对于Python接触并不算多,总是在关键时候面临着基础不牢,地动山摇的尴尬,比如在遇到稍微深入些的问题时候就容 ...

  10. Spring框架介绍及使用(转载)

    原文链接 Spring框架—控制反转(IOC) 1 Spring框架概述1.1 什么是SpringSpring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod ...