【Android】安卓四大组件之Service用法

文章目录

  • 使用Handler更新UI
  • Service
    • 基本特点
    • 启动方式
      • 非绑定式服务
        • 使用步骤
      • 绑定式服务
        • 步骤
    • 生命周期
      • 非绑定式
        • 启动阶段
        • 结束阶段
      • 绑定式
        • 启动阶段
        • 结束阶段
    • 前台Service
      • 使用步骤
      • 结束
        • 结束Service本身
        • 降级为普通Service
        • 降级为普通Service

使用Handler更新UI

  1. 主线程创建Handler对象,重写handlerMessage方法
  2. 子线程创建Message对象,使用Handler对象调用sendMessage方法发送消息,发到MessageQueue
  3. Looper一直尝试从MessageQueue中取出待处理消息,分发给Handler的handlerMessage方法中

image-20240806194619542

1722944819734

public class MainActivity extends AppCompatActivity {// 创建一个 Handler 实例,用于在主线程处理消息private Handler handler = new Handler(Looper.myLooper()) {@Overridepublic void handleMessage(@NonNull Message msg) {super.handleMessage(msg);// 当接收到的消息的 what 属性为 1 时执行以下代码if (msg.what == 1) {// 获取消息中的数据并转换为字符串String data = (String) msg.obj;// 将 TextView 的文本设置为接收到的数据tv.setText(data);}}};private TextView tv;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);// 设置活动的布局setContentView(R.layout.activity_main);// 获取布局文件中的 TextView 控件tv = findViewById(R.id.tv_response);// 获取布局文件中的 Button 控件Button button = findViewById(R.id.btn_send);// 为按钮设置点击事件监听器button.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {// 创建一个新的 Message 对象Message message = new Message();// 设置消息的内容message.obj = "数据";// 设置消息的标识符message.what = 1;// 通过 handler 发送消息handler.sendMessage(message);}});}
}

Service

基本特点

  • 后台执行: Service 主要用于在后台执行一些长时间运行的操作,比如音乐播放、文件下载等,而不会影响用户界面的交互。
  • 没有界面: Service 不像 Activity 那样有界面。它运行在后台,通常没有用户界面。
  • 主线程: Service 运行在主线程中,因此在 Service 中执行耗时操作(如网络请求或文件操作)会阻塞主线程,可能会导致应用无响应。为了避免这种情况,通常需要使用异步机制,如 AsyncTaskHandlerExecutorService 等。

启动方式

非绑定式服务

(Started Service)

  • 启动方式:通过 startService() 方法启动。
  • 生命周期:服务的生命周期与启动它的组件无关。服务一旦启动,即使启动它的组件被销毁,服务仍会继续在后台运行,直到它自己被停止(通过 stopSelf()stopService())。
  • 交互:启动的服务不能被组件直接调用其方法。如果需要与服务进行交互,需要通过广播、MessengerAIDL 等方式。
  • 用途:适用于需要在后台执行长时间操作的情况,例如,下载文件、播放音乐等。
使用步骤
  1. 定义服务

定义一个继承自 Service 的类,并实现它的生命周期方法。

public class MyService extends Service {@Nullable@Overridepublic IBinder onBind(Intent intent) {return null;}@Overridepublic void onCreate() {super.onCreate();Log.d("MyService", "onCreate()");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d("MyService", "onStartCommand()");return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {super.onDestroy();Log.d("MyService", "onDestroy()");}
}
  1. AndroidManifest.xml 中声明服务

确保在你的 AndroidManifest.xml 文件中注册服务:

<service android:name=".MyBoundService" />
  1. 启动服务
public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}public void startService(View view) {Intent intent = new Intent(this, MyService.class);startService(intent);}
}
  1. 关闭服务

  2. 由启动者(通常是Activity)调用 stopService 方法。

  3. 服务内部调用 stopSelf 方法。

  • 由启动者关闭服务

在Activity中,可以通过调用 stopService 来停止服务:

Intent intent = new Intent(this, MyService.class);
stopService(intent);
  • 服务内部关闭自己

服务可以在内部调用 stopSelf 方法来停止自己:

public class MyService extends Service {@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {// 某些逻辑操作完成后,停止服务stopSelf();return START_NOT_STICKY;}
}

绑定式服务

(Bound Service)

  • 启动方式:通过 bindService() 方法启动。
  • 生命周期:服务的生命周期与绑定它的组件(如 Activity)有关。绑定服务的存在时间是基于组件的生命周期,如果所有绑定它的组件都被销毁,服务也会被销毁。
  • 交互:允许组件与服务进行交互,组件可以通过 ServiceConnection 接口获取到服务的引用,从而调用服务中的方法。
  • 用途:适用于需要与服务进行长期交互的情况,例如,组件需要获取服务的数据或调用服务中的方法。
步骤
  1. 定义服务

定义一个继承自 ServiceMyBindService类,并实现 onBind() 方法以返回一个实现IBinder 接口的类的对象。这个 IBinder 对象将用于与客户端进行通信。我们定义一个MyBinder类继承自Binder类,Binder类实现了IBinder接口

public class MyBindService extends Service {private static final String TAG = "MyBindService";// 测试服务的方法public void testService() {Log.d(TAG, "服务的测试");}@Overridepublic void onCreate() {Log.d(TAG, "onCreate executed");super.onCreate();// 在服务创建时初始化一些资源}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {Log.d(TAG, "onStartCommand executed");// 这里可以处理服务启动时的逻辑return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {Log.d(TAG, "onDestroy executed");super.onDestroy();// 在服务销毁时释放资源}@Nullable@Overridepublic IBinder onBind(Intent intent) {Log.d(TAG, "onBind executed");return new MyBinder(this);}@Overridepublic boolean onUnbind(Intent intent) {Log.d(TAG, "onUnbind executed");return super.onUnbind(intent);// 可以在这里处理服务解绑时的逻辑}// Binder类实现了IBinder接口public class MyBinder extends Binder {private final MyBindService mMyBindService;// 构造函数,用于初始化服务实例public MyBinder(MyBindService myBindService) {mMyBindService = myBindService;}// 提供一个方法来获取服务实例public MyBindService getService() {return mMyBindService;}// 测试Binder的方法public void test() {Log.d(TAG, "MyBinder测试");mMyBindService.testService();}}
}
  1. AndroidManifest.xml 中声明服务
<service android:name=".MyBoundService" />
  1. 绑定服务

在你的组件(如 Activity)中,通过 bindService() 方法绑定到服务。你需要实现 ServiceConnection 接口来处理与服务的连接状态。

public class MainActivity extends AppCompatActivity {// 定义一个MyBinder对象,用于绑定服务后与服务交互public MyBindService.MyBinder myBinder = null;// 定义一个ServiceConnection对象,用于管理服务的连接和断开private ServiceConnection coon = new ServiceConnection() {// 当服务成功连接时回调该方法@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {// 将传入的IBinder对象转换为MyBindermyBinder = (MyBindService.MyBinder) service;// 调用MyBinder的test方法,测试服务功能myBinder.test();}// 当服务断开连接时回调该方法@Overridepublic void onServiceDisconnected(ComponentName name) {// 可以在这里处理服务断开时的逻辑}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);}// 绑定服务的方法public void bindService(View view) {Intent intent = new Intent(this, MyBindService.class);bindService(intent, coon, BIND_AUTO_CREATE);}
}
  1. 解除绑定

通过 unbindService() 方法来完成解除绑定的操作

@Override
protected void onStop() {super.onStop();if (isBound) {unbindService(connection);isBound = false;}
}

服务的绑定和解除绑定的注意事项

  • 绑定后:一旦绑定成功,组件可以通过服务提供的方法进行交互。
  • 解除绑定后:如果没有其他组件绑定该服务,服务会被销毁(如果服务是绑定服务),并调用 onDestroy() 方法进行清理。
  • 线程处理:如果服务需要执行长时间运行的任务,最好在服务中使用后台线程或异步任务来避免阻塞主线程。

生命周期

Service 的生命周期主要包括以下几个阶段:

  • onCreate(): 当服务第一次被创建时调用。通常在这里进行服务初始化。
  • onStartCommand(Intent intent, int flags, int startId): 当服务被 startService() 方法启动时调用。在这个方法中可以处理服务的任务。返回的 int 值决定了服务被系统杀掉后如何重启。
  • onBind(Intent intent): 当 bindService() 方法被调用时触发。用于提供与 Service 的绑定接口。若 Service 不需要绑定,可以返回 null
  • onDestroy(): 当服务被销毁时调用。通常在这里进行清理工作。

非绑定式

启动阶段
  • onCreate()
    • 只会在服务首次创建时调用一次。适合在这里做一些一次性的初始化工作。
  • onStartCommand(Intent intent, int flags, int startId)
    • 每次调用 startService 时都会执行。可以在这里处理传入的 Intent。
结束阶段

启动者(Activity)调用 stopService 或服务内部调用 stopSelf

  • 当 Activity 调用 stopService(Intent intent) 或服务内部调用 stopSelf() 方法时,服务会停止并触发 onDestroy 方法。

  • onDestroy()

    • 服务销毁前的最后一个方法。适合在这里做一些资源释放的收尾工作。

      @Override
      public void onDestroy() {super.onDestroy();// 释放资源
      }
      

绑定式

启动阶段

**启动者(Activity)**调用 bindService(Intent intent, ServiceConnection conn, int flags)

  • onCreate()
    • 只会在服务首次创建时调用一次。适合在这里做一些一次性的初始化工作。
  • onBind(Intent intent)
    • 只会在首次绑定时执行一次。返回一个 IBinder 对象,供客户端与服务进行通信。
结束阶段

启动者(Activity)销毁或调用 unbindService 方法

  • 当 Activity 销毁或调用 unbindService(ServiceConnection conn) 方法时,会解除与服务的绑定。当没有绑定者时,服务会销毁。

  • onUnbind(Intent intent)

    • 解除绑定:当所有客户端都解除绑定时,会调用此方法。可以在这里处理一些解绑相关的逻辑。
  • onDestroy()

    • 销毁:服务销毁前的最后一个方法。适合在这里做一些资源释放的收尾工作。

注意事项

  • 服务绑定和解除绑定:确保在 Activity 的生命周期中正确绑定和解除绑定服务,以避免内存泄漏。
  • 资源释放:在 onDestroy 中释放所有资源,确保服务完全终止时不留下任何资源占用。

onCreate->onBind->onUnbind->onDestory

前台Service

前台服务 (Foreground Service) 是一种能够持续运行并与用户进行交互的服务。相比于后台服务,前台服务具有更高的优先级,通常不会被系统回收。

使用步骤

  1. 创建通知 (Notification)

前台服务必须显示一个通知来告知用户该服务正在运行。可以使用 NotificationCompat.Builder 来创建通知。

Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID).setContentTitle("服务正在运行").setContentText("前台服务示例").setSmallIcon(R.drawable.ic_service_icon).build();
  1. 调用startForeground方法

在服务的 onCreateonStartCommand 方法中调用 startForeground(notificationId, notification) 方法,将服务提升到前台状态。

@Override
public int onStartCommand(Intent intent, int flags, int startId) {Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID).setContentTitle("服务正在运行").setContentText("前台服务示例").setSmallIcon(R.drawable.ic_service_icon).build();startForeground(1, notification);// 其他业务逻辑return START_STICKY;
}
  1. 申请权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

结束

  • 完全停止ServicestopServicestopSelf
  • 降级Service状态stopForeground(true) 将Service从前台状态退出并移除通知,但Service仍然在运行,只是优先级降低。
结束Service本身
  • 正常的结束Service方式:外部调用 stopService 方法或自身调用 stopSelf 方法。这两种方式都会完全停止Service。当Service结束后,所有与之相关的通知也会被移除。

示例:

// 通过外部调用
stopService(new Intent(context, MyService.class));// 通过Service自身调用
stopSelf();
降级为普通Service

退出Service的前台状态,降级为普通Service

  • 调用 stopForeground(true) 方法:这会将Service从前台状态退出,变成普通的后台Service。这样做会降低Service的优先级,在系统内存不足时可能会被回收销毁。参数 true 表示同时移除通知。

示例:

// 将Service退出前台状态,同时移除通知
stopForeground(true);

elf` 方法。这两种方式都会完全停止Service。当Service结束后,所有与之相关的通知也会被移除。

示例:

// 通过外部调用
stopService(new Intent(context, MyService.class));// 通过Service自身调用
stopSelf();
降级为普通Service

退出Service的前台状态,降级为普通Service

  • 调用 stopForeground(true) 方法:这会将Service从前台状态退出,变成普通的后台Service。这样做会降低Service的优先级,在系统内存不足时可能会被回收销毁。参数 true 表示同时移除通知。

示例:

// 将Service退出前台状态,同时移除通知
stopForeground(true);


感谢您的阅读
如有错误烦请指正


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/399628.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

房产中介小程序

本文来自&#xff1a;ThinkPHPFastAdmin房产中介小程序 - 源码1688 应用介绍 产中介小程序是一款基于ThinkPHPFastAdmin开发的原生微信小程序&#xff0c;为房地产中介提供房源管理、发布、报备客户、跟踪客户以及营销推广获客等服务的系统。 前端演示&#xff1a; 后台演示&am…

冷数据归档(历史库),成本与性能如何兼得?| OceanBase应用实践

随着数据量的迅猛增长&#xff0c;企业和组织在数据库管理方面遭遇的挑战愈发凸显。数据库性能逐渐下滑、存储成本节节攀升&#xff0c;以及数据运维复杂性的增加&#xff0c;这些挑战使得DBA和开发者在数据管理上面临更大的压力。 为了应对这些挑战&#xff0c;对数据生命周期…

奇异值分解(SVD)

1 奇异值分解(SVD)简介 Beltrami 和 Jordan 被认为是奇异值分解&#xff08;Singular Value Decomposition&#xff0c;SVD&#xff09;的共同开创者&#xff0c;二人于19世纪70年代相继提出了相关理论。奇异值分解主要解决的问题是数据降维。在高维度的数据中&#xff0c;数据…

Tied and Anchored Stereo Attention Network for Cloud Removal in Optical

论文名称 基于固定锚定立体注意力网络的光学遥感图像去云方法代码运行 论文代码 https://github.com/ningjin00/TASANet?tabreadme-ov-file 论文地址 1环境创建 模型环境给了这几个包&#xff0c;如果你自带环境 那就运行代码 提示缺哪个装哪个 python 3.12rasterio 1.3.10…

【AI人工智能】文心智能体 - 你的专属车牌设计师

引言 自AI盛行以来&#xff0c;不断有各种各样的人工智能产品崭露头角。我们逐步跟着不断产生的人工智能来使自己的工作和生活变得更加智能化&#xff01;那么我们是否能够创造一款专属于自己的人工智能产品呢&#xff1f; 文心智能体平台就给我们提供了这样的机会&#xff0c…

数值微分求梯度、计算图求梯度,实现单层线性回归 模型速度差异及损失率比对

文章目录 简述测试结果完整代码 简述 先将前面两篇文章的代码重构一下&#xff0c;抽离共同函数到utils.py。 重构后结构&#xff1a; ComputationGraphLinearNet.py&#xff1a; 使用计算图&#xff08;forward、backward&#xff09;求梯度构建的线性模型&#xff0c;代码…

分库分表的使用场景和中间件

文章目录 一、为什么要分库分表&#xff1f;分库分表的使用场景&#xff1f;二、分库分表常用中间件1、Cobar2、TDDL3、Atlas4、Sharding-jdbc5、Mycat6、总结 一、为什么要分库分表&#xff1f;分库分表的使用场景&#xff1f; 场景1&#xff1a;注册用户就 20 万&#xff0c…

<数据集>集装箱缺陷识别数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;3793张 标注数量(xml文件个数)&#xff1a;3793 标注数量(txt文件个数)&#xff1a;3793 标注类别数&#xff1a;4 标注类别名称&#xff1a;[DAMAGE - DEFRAME, DENT, DAMAGE - RUST, DAMAGE - HOLE] 序号类别名…

飞睿智能8km无人机WiFi图传模块,高清、稳定、超远距!实时传输新高度

在数字化飞速发展的今天&#xff0c;无人机已经从一个遥不可及的科幻概念&#xff0c;变成了我们日常生活中的得力助手。无论是航拍美景、农业植保&#xff0c;还是紧急救援、物流配送&#xff0c;无人机都展现出了其独特的优势。而在这背后&#xff0c;一个至关重要的技术支撑…

ThinkPHP教程

thinkPHP笔记 01. phpEnv配置安装 主讲老师 - 李炎恢 1. 学习基础 ThinkPHP8.x: 前端基础:HTML5/CSS(必须)、JavaScript(可选、但推荐有);后端基础:PHP基础,版本不限,但不能太老,至少PHP5.4以上语法,TP8是兼容PHP8.x的;数据库基础:MySQL数据库,掌握了常规的SQL…

再谈表的约束

文章目录 自增长唯一键外键 自增长 auto_increment&#xff1a;当对应的字段&#xff0c;不给值&#xff0c;会自动的被系统触发&#xff0c;系统会从当前字段中已经有的最大值1操作&#xff0c;得到一个新的不同的值。通常和主键搭配使用&#xff0c;作为逻辑主键。 自增长的…

面向服务架构(SOA)介绍

在汽车电子电气架构还处于分布式时代时&#xff0c;汽车软件的开发方式主要是采用嵌入式软件进行开发&#xff0c;而随着汽车智能化程度的加深&#xff0c;更加复杂且多样的功能需求让汽车软件在复杂度上再上一层。在整车的自动驾驶方面&#xff0c;由于未来高阶自动驾驶能力的…

《Unity3D网络游戏实战》正确收发数据流

TCP数据流 系统缓冲区 当收到对端数据时&#xff0c;操作系统会将数据存入到Socket的接收缓冲区中 操作系统层面上的缓冲区完全由操作系统操作&#xff0c;程序并不能直接操作它们&#xff0c;只能通过socket.Receive、socket.Send等方法来间接操作。当系统的接收缓冲区为空&…

RCE绕过技巧

目录 EVAL长度限制突破技巧 1.使用反引号 2.file_put_contents写入文件 3.php5.6变长参数usort回调后门 命令长度限制突破技巧 1.拼接文件名 无字母数字的webshell命令执行 1.取反码 2.上传临时文件 EVAL长度限制突破技巧 分析代码&#xff1a;首先传递一个param参数&…

OceanBase V4.3 列存引擎之场景问题汇总

在OceanBase 4.3版本发布后&#xff08;OceanBase社区版 V4.3 免费下载&#xff09;&#xff0c;其新增的列存引擎&#xff0c;及行列混存一体化的能力&#xff0c;可以支持秒级实时分析&#xff0c;引发了用户、开发者及业界人士的广泛讨论。本文选取了这些讨论中较为典型的一…

企业应该如何准备 EcoVadis 审核?

企业准备 EcoVadis 审核可以参考以下步骤&#xff1a; 注册&#xff1a;在网上注册并提供公司的相关信息&#xff0c;包括法律实体名称、国家和地区、企业规模和行业等。如果是受客户邀请参加评估&#xff0c;需按照邀请邮件中的链接进行注册&#xff0c;并确保客户能随时获知评…

安卓默认混淆规则文件的区别

在 Android 项目中&#xff0c;ProGuard 是一个优化和混淆代码的工具。proguard-android-optimize.txt 和 proguard-android.txt 是两个用于配置 ProGuard 的默认规则文件&#xff0c;如图下 它们有以下区别&#xff1a; proguard-android-optimize.txt: 优化&#xff1a;这个配…

Django中事务的基本使用

1. Django事务处理 事务(Transaction): 是一种将多个数据库操作组合成一个单一工作单元的机制. 如果事务中的所有操作都成功完成, 则这些更改将永久保存到数据库中. 如果事务中的某个操作失败, 则整个事务将回滚到事务开始前的状态, 所有的更改都不会被保存到数据库中. 这对于…

系统编程 day10 进程2

进程创建之后&#xff1a; 1.任务-----子进程与父进程干的活差不多 2.父进程创建出子进程之后&#xff0c;子进程做的与父进程完全不同 shell程序-----bash----- 以上为进程运行的过程中&#xff0c;典型的两种应用场景 能够改变子进程的执行效果的函数是exec函数族 l和v&a…

【网盘系统3.0版本】百度云盘手动cookie获取,添加到扫码系统管理平台。

一.获取cookie步骤 1.谷歌浏览器选择开发者模式。 2.选择网路&#xff0c;过滤接口main 3.选择request head&#xff0c;cookie列表里面可查看二.添加到管理平台。 1.登录管理平台&#xff0c;输入账户和密码 2.选择账户设置&#xff0c;添加cookie。 4.复制卡密链接&#xf…