《第一行代码》 第十章:服务
一,在子线程中更新UI
1,新建项目,修改布局代码
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/change_text"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="改变文本"/><TextViewandroid:id="@+id/text"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true"android:text="hello world"android:textSize="20sp"/>
</RelativeLayout>
2,修改活动中的代码
public class MainActivity extends AppCompatActivity implements View.OnClickListener{private TextView text;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);text=(TextView) findViewById(R.id.text);Button changeText=(Button) findViewById(R.id.change_text);changeText.setOnClickListener(this);}@Overridepublic void onClick(View view) {switch(view.getId()){case R.id.change_text://创建一个子进程new Thread(new Runnable() {@Overridepublic void run() {//run中执行的就是子进程中的代码text.setText("hellosdfsdfs");}}).start();break;default:break;}}
}
这样在子线程中直接修改ui,是不被允许的。
那有的时候,我们又必须在子线程中执行操作,然后再一依据结果来更新ui呢?
3,安卓异步消息处理
public class MainActivity extends AppCompatActivity implements View.OnClickListener{public static final int UPDATE_TEXT=1;private TextView text;private Handler handler=new Handler(){@Overridepublic void handleMessage(@NonNull Message msg) {//接收到message并处理switch(msg.what){case UPDATE_TEXT://这里可以进行ui的操作text.setText("NICE TO MEET YOU");break;default:break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);text=(TextView) findViewById(R.id.text);Button changeText=(Button) findViewById(R.id.change_text);changeText.setOnClickListener(this);}@Overridepublic void onClick(View view) {switch(view.getId()){case R.id.change_text://创建一个子进程new Thread(new Runnable() {@Overridepublic void run() {Message message=new Message();message.what=UPDATE_TEXT;//将message对象发送出去handler.sendMessage(message);}}).start();break;default:break;}}
}
使用message就可以更新成功了。这就是安卓异步消息处理的基本方法。
二,解析异步消息处理机制
Android 中的异步消息处理主要由4个部分组成:Message Handler、MessageQueue 和 Looper。其中 Message 和 Handler 在上一小节中我们已经接触过了,而 MessageQueue 和 Looper 对于你来说还是全新的概念,下面我就对这 4个部分进行一下简要的介绍。
1,Message
Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。上一小节中我们使用到了 Message的what 字段,除此之外还可以使用arg1和arg2字段来携带一些整型数据,使用 obi 字段携带一个 object 对象。
2. Handler
Handler 顾名思义也就是处理者的意思,它主要是用于发送和处理消息的。发送消息一般是使用 Handler 的 sendMessage()方法,而发出的消息经过一系列地辗转处理后,最终会传递到Handler的 handleMessage()方法中。
3.MessageQueue
MessageQueue 是消息队列的意思,它主要用于存放所有通过 Handler 发送的消息。这部分消息会一直存在于消息队列中,等待被处理。每个线程中只会有一个MessageQueue 对象。
4. Looper
Looper 是每个线程中的 MessageQueue 的管家,调用 Looper 的 Loop()方法后,就会进入到一个无限循环当中,然后每当发现MessageQueue 中存在一条消息,就会将它取出,并传递到Handler的 handleMessage()方法中。每个线程中也只会有一个 Looper 对象。
首先需要在主线程当中创建一个 Handler 对象,并重写handleMessage()方法。然后当子线程中需要进行UI操作时,就创建一个Message 对象,并通过 Handler将这条消息发送出去。之后这条消息会被添加到 MessageQueue 的队列中等待被处理而 Looper 则会一直尝试从 MessageQueue 中取出待处理消息,最后分发回 Handler 的handleMessage()方法中。由于 Handler 是在主线程中创建的,所以此时 handleMessage()方法中的代码也会在主线程中运行,于是我们在这里就可以安心地进行 UI操作了。

三,使用AsynTask
首先来看一下AsyncTask 的基本用法,由于AsyncTask 是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它。在继承时我们可以为 AsyncTask类指定3个泛型参数,这3个参数的用途如下。
Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
progress。后台任务执行时,如果需要在界面上示当前的的进度。则使用这里指定的泛型1作为进度单位。
Resut:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为哦返回值类型。
因此,一个最简单的自定义的AsynTask可以写成如下样子:
class DownLoadTask extends AsynTask<void,Integer,Boolean>{...
}
目前我们自定义的DownLoadTask还是一个空任务,并不能进行任何的实际操作,我们还需要重写AsynTask中的几个方法才能完成对任务的定制。常用过的有以下4个:
1.onPreExecute()
这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
2.doInBackground(Params...)
这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。任务旦完成就可以通过return 语来将任务的执行结果返回,如果AsyncTask 的第三个泛型参数指定的是 Void,就可以不返回任务执行结果。注意,在这个方法中是不可以进行 UI操作的,如果需要更新 UI 元素,比如说反馈当前任务的执行进度,可以调用 publishProgress(Progress...)方法来完成。
3.onProgressUpdate(Progress...)
当在后台任务中调用了 publishProgress(Progress...)方法后,onProgressUpdate(Progress...)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对 UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
4.onPostExecute(Result)
当后台任务执行完毕并通过 return 语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些 UI操作,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。
简单来说,使用AsyncTask 的诀窍就是,在 doInBackground()方法中执行具体的耗时任务在onProgressUpdate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作。
如果想要启动这个任务,只需编写以下代码即可:
new DownloadTask().execute();
四,服务的基本用法
1,定义一个服务
新建一个ServiceTest项目,然后右击com.example.servicetest-new-Service-Service,点击确定,创建服务:
public class MyService extends Service {public MyService() {}@Overridepublic IBinder onBind(Intent intent) {//唯一的抽象方法,必须在子类中进行实现// TODO: Return the communication channel to the service.throw new UnsupportedOperationException("Not yet implemented");}@Overridepublic void onCreate() {//服务创建时调用super.onCreate();}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {//服务启动的时候调用return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {//服务销毁的时候调用super.onDestroy();}
}
然后,AS会在menifest中自动帮我们注册:
<applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true"android:theme="@style/Theme.AndroidThreadTest"><serviceandroid:name=".MyService"android:enabled="true"android:exported="true"></service></application>
这样一个服务就创建好了。
2,启动和停止服务
启动和停止服务主要时利用Intent来实现。
修改布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/start_serivce"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="启动服务"/><Buttonandroid:id="@+id/stop_service"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="停止服务"/>
</LinearLayout>
修改对应的活动代码,利用Intent来启停服务:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button startService=(Button) findViewById(R.id.start_serivce);Button stopService=(Button) findViewById(R.id.stop_service);startService.setOnClickListener(this);stopService.setOnClickListener(this);}@Overridepublic void onClick(View view) {switch(view.getId()){case R.id.start_serivce:Intent startIntent=new Intent(this,MyService.class);startService(startIntent);break;case R.id.stop_service:Intent stopIntent=new Intent(this,MyService.class);stopService(stopIntent);break;default:break;}}
}
然后修改服务代码:
public class MyService extends Service {public MyService() {}@Overridepublic IBinder onBind(Intent intent) {//唯一的抽象方法,必须在子类中进行实现// TODO: Return the communication channel to the service.throw new UnsupportedOperationException("Not yet implemented");}@Overridepublic void onCreate() {//服务第一次创建时调用super.onCreate();Log.d("TAG", "onCreate: ");}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {//服务每次启动的时候调用Log.d("TAG", "onStartCommand: ");return super.onStartCommand(intent, flags, startId);}@Overridepublic void onDestroy() {//服务销毁的时候调用super.onDestroy();Log.d("TAG", "onDestroy: ");}
}
3,活动和服务进行通讯
上文中,我们虽然在活动中对服务进行了启停处理,但是我们并没有真正地在活动中和服务进行交互。
比如说,目前我们希望在MyService里提供一个下载功能,然后在活动中可以决定何时开始下载,以及随时查看下载进度。实现这个地思路是创建一个专门的Binder对象来对下载功能进行管理,修改MyService中的代码,如下:
public class MyService extends Service {private DownloadBinder mBinder=new DownloadBinder();class DownloadBinder extends Binder {public void startDownload(){Log.d("TAG", "startDownload: ");}public int getProgress(){Log.d("TAG", "getProgress: ");return 0;}}public MyService() {}@Overridepublic IBinder onBind(Intent intent) {//唯一的抽象方法,必须在子类中进行实现return mBinder;}}
这样服务中的代码就写完了,接下来修改布局:
<Buttonandroid:id="@+id/bind_service"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="绑定服务"/><Buttonandroid:id="@+id/unbind_service"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="取消绑定服务"/>
接下来要在活动中绑定服务,当一个活动和服务绑定之后,就可以调用该服务里面的Binder提供的方法了。修改MainActiviyty中的代码:
public class MainActivity extends AppCompatActivity implements View.OnClickListener{private MyService.DownloadBinder downloadBinder;private ServiceConnection connection =new ServiceConnection() {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {downloadBinder=(MyService.DownloadBinder) service;downloadBinder.startDownload();downloadBinder.getProgress();}@Overridepublic void onServiceDisconnected(ComponentName componentName) {}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);Button startService=(Button) findViewById(R.id.start_serivce);Button stopService=(Button) findViewById(R.id.stop_service);startService.setOnClickListener(this);stopService.setOnClickListener(this);//新增通信服务Button bindService=(Button) findViewById(R.id.bind_service);Button unbindService=(Button) findViewById(R.id.unbind_service);bindService.setOnClickListener(this);unbindService.setOnClickListener(this);}@Overridepublic void onClick(View view) {switch(view.getId()){case R.id.start_serivce:Intent startIntent=new Intent(this,MyService.class);startService(startIntent);break;case R.id.stop_service:Intent stopIntent=new Intent(this,MyService.class);stopService(stopIntent);break;case R.id.bind_service:Intent bindIntent=new Intent(this,MyService.class);bindService(bindIntent,connection,BIND_AUTO_CREATE);//绑定服务break;case R.id.unbind_service:unbindService(connection);//解绑服务break;default:break;}}
}
相关文章:
《第一行代码》 第十章:服务
一,在子线程中更新UI 1,新建项目,修改布局代码 <RelativeLayout xmlns:android"http://schemas.android.com/apk/res/android"android:layout_width"match_parent"android:layout_height"match_parent"&g…...
简单介绍编程进制
十进制 十进制的位权为 10,比如十进制的 123,123 1 * 10 ^ 2 2 * 10 ^ 1 3 * 10 ^ 0。 二进制 二进制的位权为 2,比如十进制的 4,二进制为 100,4 1 * 2 ^ 2 0 * 2 ^ 1 0 *2 ^ 0。 Java7 之前,不支…...
windows忘记开机密码怎么办
windows忘记开机密码怎么办 清除windows登录密码 清除windows登录密码简单方法 开机到欢迎界面时,按CtrlAltDelete两次,跳出帐号窗口,输入用户名:administrator,回车, 或者启动时按F8 选“带命令行的安全…...
SpringCloud:Eureka
目录 一、eureka的作用 二、搭建Eureka服务端 三、添加客户端 四、服务发现 提供者与消费者 服务提供者:一次业务中,被其它微服务调用的服务。(提供接口给其它微服务) 服务消费者:一次业务中,调用其它微服务的服…...
如何获取或设置CANoe以太网网卡信息(SET篇)
CAPL提供了一系列函数用来操作CANoe网卡。但是,但是,首先需要明确一点,不管是获取网卡信息,还是设置网卡信息,只能访问CAPL程序所在的节点下的网卡,而不是节点所在的以太网通道下的所有网卡 关于第一张图中,Class节点下,有三个网卡:Ethernet1、VLAN 1.100、VLAN 1.200…...
【软件测试面试题】项目经验?资深测试 (分析+回答) 我不信你还拿不到offer......
目录:导读前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜)前言 在面试过程中&#…...
tensorflow lite简介-移动设备端机器学习
TensorFlow Lite 是一组工具,可帮助开发者在移动设备、嵌入式设备和 loT 设备上运行模型,以便实现设备端机器学习。 支持多平台 支持多种平台,涵盖 Android 和 iOS 设备、嵌入式 Linux 和微控制器。 原理/流程 工作原理或者使用流程就是上面…...
Node.js常用知识
1、什么是 Node.js 【】Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境。浏览器是 js 的前端运行环境,node.js 是 js 的后端运行环境。他们都有 V8 引擎,有各自的内置 API 2、fs 文件系统模块 【】fs 模块是 Node.js 官方提供的、用来操作文件…...
踩坑:maven打包失败的解决方式总结
Maven打包失败原因总结如下: 失败原因1:无法使用spring-boot-maven-plugin插件 使用spring-boot-maven-plugin插件可以创建一个可执行的JAR应用程序,前提是应用程序的parent为spring-boot-starter-parent。 需要添加parent的包spring-boot…...
【C++】位图
文章目录位图概念位图操作位图代码位图应用位图概念 boss直接登场: 给40亿个不重复的无符号整数,没排过序。给一个无符号整数,如何快速判断一个数是否在这40亿个数中❓ 40亿个整数,大概就是16GB。40亿个字节大概就是4GB。 1Byt…...
蓝桥杯-考勤刷卡
蓝桥杯-考勤刷卡1、问题描述2、解题思路3、代码实现1、问题描述 小蓝负责一个公司的考勤系统, 他每天都需要根据员工刷卡的情况来确定 每个员工是否到岗。 当员工刷卡时, 会在后台留下一条记录, 包括刷卡的时间和员工编号, 只 要在一天中员工刷过一次卡, 就认为他到岗了。 现在…...
如何利用站内推广和站外推广提高转化率?
在如今的网络时代,拥有一个好的网站是非常重要的。但是,光有一个好的网站是不够的,为了达到我们的目标,需要不断地提高网站的转化率。而在实现这个目标的过程中,站内推广和站外推广是两个非常关键的因素。 站内推广是…...
Java多线程(三)——线程池及定时器
线程池就是一个可以复用线程的技术。前面三种多线程方法就是在用户发起一个线程请求就创建一个新线程来处理,下次新任务来了又要创建新线程,而创建新线程的开销是很大的,这样会严重影响系统的性能。线程池就相当于预先创建好几个线程…...
Linux命令行安装Oracle19c教程和踩坑经验
安装 下载 从 Oracle官方下载地址 需要的版本,本次安装是在Linux上使用yum安装,因此下载的是RPM。另外,需要说明的是,Oracle加了锁的下载需要登录用户才能安装,而用户是可以免费注册的,这里不做过多说明。 …...
Linux常用命令等
目录 1.Linux常用命令 (1)系统命令 (2)文件操作命令 2.vim编辑器 3.linux系统中,软件安装 (1) rpm 安装,RedHat Package Manager (2)yum 安装 (3)源代码编译安装 1.Linux常用命令 Linux命令是非常多的,对于像嵌入式开发工程师,运维工程师需要掌握的命令是非常多的.对于…...
CEC2014:鱼鹰优化算法(Osprey optimization algorithm,OOA)求解CEC2014(提供MATLAB代码
一、鱼鹰优化算法简介 鱼鹰优化算法(Osprey optimization algorithm,OOA)由Mohammad Dehghani 和 Pavel Trojovsk于2023年提出,其模拟鱼鹰的捕食行为。 鱼鹰是鹰形目、鹗科、鹗属的仅有的一种中型猛禽。雌雄相似。体长51-64厘米…...
MyBatis底层原理【源码运行时序图】
MyBatis初始化流程🛷 以下代码为例🎉 🎇可对应源码阅读 MyBatis初始化流程✨ #mermaid-svg-yoG1e8Dnp3UIAOUW {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-yoG1e8Dnp3UIAOU…...
k8s 系列之 CoreDNS 解读
k8s 系列之 CoreDNS CoreDNS工作原理 kuberntes 中的 pod 基于 service 域名解析后,再负载均衡分发到 service 后端的各个 pod 服务中,如果没有 DNS 解析,则无法查到各个服务对应的 service 服务 在 Kubernetes 中,服务发现有几…...
从测试鸡蛋硬度到跳表的设计
我回忆起六七年前的一道题鸡蛋掉落问题,有幸在leetCode上找到题目了 原题是2枚鸡蛋 leetCode有拓展,k枚鸡蛋 具体的思路是这样的。 以2枚鸡蛋验证100层为例 不能直接二分查找,因为你在50层测试时,如果直接鸡蛋碎了,那…...
3D立体视觉成像原理介绍【一 】
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言什么是基线?基线是如何影响3D图像质量激光三角测量飞行时间结构光相机时间编码结构光前言 本文将介绍3D立体视觉的成像原理,包括【激光三…...
【Linux】C语言执行shell指令
在C语言中执行Shell指令 在C语言中,有几种方法可以执行Shell指令: 1. 使用system()函数 这是最简单的方法,包含在stdlib.h头文件中: #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...
QMC5883L的驱动
简介 本篇文章的代码已经上传到了github上面,开源代码 作为一个电子罗盘模块,我们可以通过I2C从中获取偏航角yaw,相对于六轴陀螺仪的yaw,qmc5883l几乎不会零飘并且成本较低。 参考资料 QMC5883L磁场传感器驱动 QMC5883L磁力计…...
2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面
代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口(适配服务端返回 Token) export const login async (code, avatar) > {const res await http…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
代码规范和架构【立芯理论一】(2025.06.08)
1、代码规范的目标 代码简洁精炼、美观,可持续性好高效率高复用,可移植性好高内聚,低耦合没有冗余规范性,代码有规可循,可以看出自己当时的思考过程特殊排版,特殊语法,特殊指令,必须…...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...
鸿蒙(HarmonyOS5)实现跳一跳小游戏
下面我将介绍如何使用鸿蒙的ArkUI框架,实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
