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. 纯数据结构Java实现(5/11)(Set&Map)

    纯数据结构Java实现(5/11)(Set&Map) Set 和 Map 都是抽象或者高级数据结构,至于底层是采用树还是散列则根据需要而定. 可以细想一下 TreeMap/HashMap, T ...

  2. 那些必会用到的 ES6 精粹

    前言 最新的 ECMAScript 都已经到发布到 2019 版了. 我们应该有的态度是: Stay hungry ! Stay young ! 从接触 vue 到工作中用到 vue 将近 2 年了, ...

  3. 本地项目上传到github上最直接步骤

    1.首先得有一个git账号(本地安装git) 2.git上创建一个project 3.回到本地你要提交文件夹位置 4.按住shift + 鼠标右键 选择在此处打开命令窗口 5.输入命令  git in ...

  4. UVA 10699 Count the factors 题解

    Time limit 3000 ms OS Linux Write a program, that computes the number of different prime factors in ...

  5. mysql迁移mpp数据库Greenplum

    1. 场景描述 因兄弟项目中mysql有点扛不住了,要做sql优化,但是业务有点小复杂,优化起来有点麻烦(sql嵌套有点多),便想着用Mpp数据库Greenplum测试下,看性能和复杂度怎么样,趟趟水 ...

  6. Java8新特性_lambda表达式和函数式接口最详细的介绍

    Lambda表达式 在说Lambda表达式之前我们了解一下函数式编程思想,在数学中,函数就是有输入量.输出量的一套计算方案,也就是“拿什么东西做什么事情”. 相对而言,面向对象过分强调“必须通过对象的 ...

  7. JMeter更改语言为英文

    1. 进入目录apache-jmeter-2.13\bin 2. 打开jmeter.properties 3. 取消“language=en”前的注释 4. 重新打开JMeter,即可看到语言已经变成 ...

  8. D-Big Integer_2019牛客暑期多校训练营(第三场)

    题意 设A(n) = n个1,问有多少对i,j使得\(A(i^j)\equiv0(modp)\) 题解 \(A(n) = \frac{10^n-1}{9}\) 当9与p互质时\(\frac{10^n- ...

  9. BZOJ3170 [Tjoi2013]松鼠聚会 切比雪夫距离 - 曼哈顿距离 - 前缀和

    BZOJ3170 题意: 有N个小松鼠,它们的家用一个点x,y表示,两个点的距离定义为:点(x,y)和它周围的8个点即上下左右四个点和对角的四个点,距离为1.现在N个松鼠要走到一个松鼠家去,求走过的最 ...

  10. CodeM 美团资格赛 思维 dfs

    链接:https://www.nowcoder.com/acm/contest/138/C来源:牛客网 世界杯就要开始啦!真真正正的战斗从淘汰赛开始,现在我们给出球队之间的胜负概率,来预测每支球队夺冠 ...