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. QFramework 使用指南 2020(二):下载与版本介绍

    目前 QFramework 有两个可供安装的版本 PackageKit:QFramework 的插件平台,可以下载只感兴趣的插件,除了 Framework 模块还有一些 Shader 案例.项目模板. ...

  2. 由group by引发的sql_mode的学习

    前言 在一次使用group by查询数据库时,遇到了问题.下面先搭建环境,然后让问题复现,最后分析问题. 一 问题复现 mysql版本 建表插入数据 表的结构 现在问题来了:我想查询上面表中每个部门年 ...

  3. PostgreSQL数据库查询最近几天的数据

    pgsql语法类似mysql  ,下面总结几个pgsql工作会用到的求时间的语句 1.当前时间向前推一天\ SELECT current_timestamp - interval '1 day' 例: ...

  4. Spring源码剖析9:Spring事务源码剖析

    转自:http://www.linkedkeeper.com/detail/blog.action?bid=1045 声明式事务使用 Spring事务是我们日常工作中经常使用的一项技术,Spring提 ...

  5. 提交中文数据乱码问题---web.xml

    前端时间,做了个纯springmvc框架的一个后台系统,遇到了不少问题.特别是编码问题,让我纠结了很久.每次ajax传入数据的时候需要将form中的数据先进行编码 encodeURI(AA); 利用a ...

  6. node.js 初学 自我笔记整理 day01

     node.js   概念问题: Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境.   npm是一个node的包管理工具  ,也是一个网站  ,还是一条命令.N ...

  7. Swoole引擎原理的快速入门干货

    更多内容,欢迎关注微信公众号:全菜工程师小辉~ 过去一年使用PHP和Java两种技术栈完成了一个游戏服务器项目.由于项目中有高频的网络请求,所以PHP技术栈尝试使用Swoole引擎(基于事件的高性能异 ...

  8. BeanUtils开发包的使用

    对内省技术有了一定的了解之后,我们就可以来学习一下BeanUtils开发包的使用了. 我们先假设一个情景,有一个JSP文件,如果要将该JSP文件中表单数据封装到Servlet文件应该怎么办?此时方法显 ...

  9. Java基础之Iterable与Iterator

    Java基础之Iterable与Iterator 一.前言: Iterable :故名思议,实现了这个接口的集合对象支持迭代,是可迭代的.able结尾的表示 能...样,可以做.... Iterato ...

  10. JavaScript 防抖

    JavaScript 防抖 在前端开发中会遇到一些频繁的事件触发,比如: window 的 resize.scroll mousedown.mousemove keyup.keydown...   防 ...