Android四大组件之服务的两种启动方式详解
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(空进程)
- 应用程序没有任何的组件在活动,该应用就是一个空进程。唯一存活的原因就是为了提高下次开启的时间。
开启服务的两种方式
服务:默认运行在主线程。
服务的创建流程
- 自定义一个服务类继承 android.app.Service;
- 在清单文件中配置 service(AndroidManifest.xml);
- 在服务类中重写方法。
<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方式 启动一个服务,并调用服务中的方法(模拟一些业务处理)。
分析:流程步骤
- 创建一个服务类,继承 Service。如:MyCustomService;
- 自定义一个服务接口对象类,实现ServiceConnection接口。如:MyServiceConnection;
- 自定义一个中间帮助类,继承Binder类(IBinder实现类)。当服务中的onBind方法被执行的时候,作为返回值。如:MyBinderImpl;
- 自定义一个接口,封装一些中间帮助类对象共有的函数。如: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四大组件之服务的两种启动方式详解的更多相关文章
- Android四大组件之服务-Service 原理和应用开发详解
一.Android 服务简介 Service是android 系统中的四大组件之一(Activity.Service.BroadcastReceiver.ContentProvider),它跟Acti ...
- Android四大组件之——Activity的生命周期(图文详解)
转载请在文章开头处注明本博客网址:http://www.cnblogs.com/JohnTsai 联系方式:JohnTsai.Work@gmail.com [Andro ...
- UEFI与 Legacy BIOS两种启动模式详解
(1). UEFI启动模式 与 legacy启动模式 legacy启动模式: 就是这么多年来PC一直在使用的启动方式(从MBR中加载启动程序),UEFI BIOS作为一种新的BIOS自然也应该兼容这种 ...
- Android为TV端助力 Service 两种启动方式的区别
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务.这两个方法都 可以启动Service,但是它们的使用场合有所不同.使 ...
- Android Activity 的四种启动模式 lunchMode 和 Intent.setFlags();singleTask的两种启动方式。
原文:Android Activity 的四种启动模式 lunchMode 和 Intent.setFlags();singleTask的两种启动方式. Android Activity 的四种启动模 ...
- 第一章 Mybtais的两种启动方式
Mybatis的两种启动方式如下: 1.xml实现: xml的实现方式中,主要是通过手动创建SqlSession,然后调用session.selectOne()方法实现来实现. 首先是创建Config ...
- ARM的两种启动方式 (NAND FLASH. NOR FLASH)
为什么会有两种启动方式? 这就是有两种FLASH 的不同特点决定的. NAND FLASH 容量大,存储的单位比特数据的成本要低很多,但是要按照特定的时序对NAND FLASH 进行读写,因此CP ...
- Spring事务Transaction配置的五种注入方式详解
Spring事务Transaction配置的五种注入方式详解 前段时间对Spring的事务配置做了比较深入的研究,在此之间对Spring的事务配置虽说也配置过,但是一直没有一个清楚的认识.通过这次的学 ...
- python selenium 三种等待方式详解[转]
python selenium 三种等待方式详解 引言: 当你觉得你的定位没有问题,但是却直接报了元素不可见,那你就可以考虑是不是因为程序运行太快或者页面加载太慢造成了元素不可见,那就必须要加等待 ...
随机推荐
- Oracle中的通用函数
1.nvl(列,默认值)函数处理null select nvl(null,3),nvl(4,3) from dual 结果显示为3,4.因为nvl中的第一个为null时,返回结果为第二个值,第一 ...
- php opcodes(vld)翻译教程
一.php opcodes的由来(如果你只想知道如何解php opcodes就直接跳过这步) 1.PHP内核-Zend引擎的详解:https://www.php.cn/php-weizijiaoche ...
- Nacos(二):SpringCloud项目中接入Nacos作为注册中心
前言 通过上一篇文章:Nacos介绍简单了解了Nacos的发展历程和现状,本文我们开始Nacos试水的第一步: 使用Nacos做注册中心 上周末(7.6)Nacos发布了V1.1.0版本,这次更新支持 ...
- sparksession创建DataFrame方式
spark创建dataFrame方式有很多种,官方API也比较多 公司业务上的个别场景使用了下面两种方式 1.通过List创建dataFrame /** * Applies a schema to a ...
- idea快速生成实体类
1.打开idea的视图,选择Database 2.选择对应的数据库[这里是mysql为例] 3.输入自己对应的内容,输入完成可点击Test Connection进行测试,成功SUCCESS 4.点击确 ...
- JavaScript数组方法大全(第一篇)
数组方法大全(第一篇) 注意:第一次写博客有点小紧张,如有错误欢迎指出,如有雷同纯属巧合,本次总结参考书籍JavaScript权威指南,有兴趣的小伙伴可以去翻阅一下哦 join()方法 该方法是将数组 ...
- Math和Date
Math和Date 一.对象 1.对象的概念 对象的本质:键值对,属性名和属性值 对象的意义:存储数据,编程 对象中的变量:属性 对象中的函数:方法 2.对象的赋值 var obj = {}; var ...
- SpringBoot_@valid_参数校验
SpringBoot @valid 参数校验 空检查 @Null 验证对象是否为null @NotNull 验证对象是否不为null, 无法查检长度为0的字符串 @NotBlank 检查约束字符串是不 ...
- 可以穿梭时空的实时计算框架——Flink对时间的处理
Flink对于流处理架构的意义十分重要,Kafka让消息具有了持久化的能力,而处理数据,甚至穿越时间的能力都要靠Flink来完成. 在Streaming-大数据的未来一文中我们知道,对于流式处理最重要 ...
- Mybatis关联查询<association> 和 <collection>
一.背景 1.在系统中一个用户存在多个角色,那么如何在查询用户的信息时同时把他的角色信息查询出来啦? 2.用户pojo: public class SysUser { private Long id; ...