《第一行代码》 第十章:服务

news2025/1/18 11:42:40

一,在子线程中更新UI

1,新建项目,修改布局代码

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
   <Button
       android:id="@+id/change_text"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="改变文本"/>
    <TextView
        android: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;
    @Override
    protected 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);
    }

    @Override
    public void onClick(View view) {
        switch(view.getId()){
            case R.id.change_text:
                //创建一个子进程
                new Thread(new Runnable() {
                    @Override
                    public 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(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            //接收到message并处理
            switch(msg.what){
                case UPDATE_TEXT:
                    //这里可以进行ui的操作
                    text.setText("NICE TO MEET YOU");
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected 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);
    }

    @Override
    public void onClick(View view) {
        switch(view.getId()){
            case R.id.change_text:
                //创建一个子进程
                new Thread(new Runnable() {
                    @Override
                    public 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() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        //唯一的抽象方法,必须在子类中进行实现
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        //服务创建时调用
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //服务启动的时候调用
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        //服务销毁的时候调用
        super.onDestroy();
    }
}

然后,AS会在menifest中自动帮我们注册:

    <application
        android: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">
        <service
            android: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">
   <Button
       android:id="@+id/start_serivce"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:text="启动服务"/>
    <Button
        android: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{
    @Override
    protected 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);
    }

    @Override
    public 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() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        //唯一的抽象方法,必须在子类中进行实现
        // TODO: Return the communication channel to the service.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public void onCreate() {
        //服务第一次创建时调用
        super.onCreate();
        Log.d("TAG", "onCreate: ");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //服务每次启动的时候调用
        Log.d("TAG", "onStartCommand: ");
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public 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() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        //唯一的抽象方法,必须在子类中进行实现
        return mBinder;
    }
 }

这样服务中的代码就写完了,接下来修改布局:

    <Button
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="绑定服务"/>
    <Button
        android: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() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder=(MyService.DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.getProgress();
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {}
    };
    @Override
    protected 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);
    }

    @Override
    public 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;
       }
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Bluetooth

GATT简介 蓝牙分为经典蓝牙和低功耗蓝牙&#xff08;BLE&#xff09;&#xff0c;我们常用的蓝牙遥控器就是低功耗蓝牙 低功耗蓝牙&#xff08;BLE&#xff09;连接都是建立在 GATT (Generic Attribute Profile) 协议之上。 GATT全称Generic Attribute Profile&#xff08;直译…

软件测试用例篇(2)

功能测试界面测试兼容性测试安全测试易用性测试性能测试 针对有需求的案例来设计测试用例:邮箱注册&#xff0c;部分测试用例 https://zay1xofb7z6.feishu.cn/mindnotes/bmncnKD5Ak6GSZl3PRlWDgF9z3g#mindmap 一)等价类: 场景需求:姓名长度是6-200位&#xff0c;那么如何进行设…

【数据结构初阶】手撕单链表

目录一.链表概念和结构二.单链表功能的实现1.打印单链表内容2.申请单链表节点3.头插和尾插4.头删和尾删5.单链表查找6.pos位置前后插入7.pos位置删除三.链表面试题剖析一.链表概念和结构 概念&#xff1a;链表是一种物理存储结构上非连续、非顺序的存储结构&#xff0c;数据元素…

5-12 SpringCloud快速开发入门:服务消费者构建Hystrix Dashboard监控端点

服务消费者构建Hystrix Dashboard监控端点 Hystrix 仪表盘工程已经创建好了&#xff0c;现在我们需要有一个服务&#xff0c;让这个服务提供一个路径为/actuator/hystrix.stream 接口&#xff0c;然后就可以使用 Hystrix 仪表盘来对该服务进行监控了&#xff1b; 我们改造消费者…

pandas常用操作

文章目录1 认识Pandas2 pandas常用数据结构2.1 Series2.1.1 Series创建2.1.2 数据类型转换2.1.3 查看Series对象的属性2.1.4 预览数据head、tail2.1.5 通过索引获取数据2.2 DataFrame2.2.1 创建DataFrame对象2.2.2 获取行、列、值2.2.3 数据预览2.2.4 通过索引获取数据2.2.5 增…

【Redis】Redis高级客户端Lettuce详解

文章目录前提Lettuce简介连接Redis定制的连接URI语法基本使用API同步API异步API反应式API发布和订阅事务和批量命令执行Lua脚本执行高可用和分片普通主从模式哨兵模式集群模式动态命令和自定义命令高阶特性配置客户端资源使用连接池几个常见的渐进式删除例子在SpringBoot中使用…

C/C++每日一练(20230304)

目录 1. 计数质数 ☆ 2. 筛选10到1000的回文数 ☆ 3. 计算位于矩阵边缘的元素之和 ★ 1. 计数质数 统计所有小于非负整数 n 的质数的数量。 示例 1&#xff1a; 输入&#xff1a;n 10 输出&#xff1a;4 解释&#xff1a;小于 10 的质数一共有 4 个, 它们是 2, 3, 5, 7…

【HomeKit】从HomeKit架构层细化到HomeKit ADK集成

前言&#xff1a;这篇文章是对于苹果协议文件《HomeKit ADK Integration Guide - Addendum for Televisions》的学习&#xff0c;针对版本为ADK 6.0电视。描述了将HomeKit ADK的电视简介集成到目标平台中所需的步骤。 总说明 此配置文件用于控制启用Airplay的电视&#xff0c;…

高通Android 13默认切换免提功能

1、测试部反馈 由于平板本身没有听筒功能 因此考虑工厂直接切换到免提功能 2、修改路径 frameworks/av/services/audiopolicy/enginedefault/src/Engine.cpp 3、编译源码ok 拨打紧急号码 可以正常切换到免提功能 其他mtk平台可能不一样 具体以项目实际为准 相关链接 构建…

ESP32编译及运行错误记录

1、打印格式不对 一般都是因为日志中某个参数打印格式不匹配造成。 ESP_LOGI(TAG, "[APP] Free memory: %lu bytes", esp_get_free_heap_size());//将之前的%d 改为%lu 2、配置载不对 这里选择了蓝牙模块需要引入蓝牙组件才能编译通过 idf.py menuconfig Component…

项目中的MD5、盐值加密

首先介绍一下MD5&#xff0c;而项目中用的是MD5和盐值来确保密码的安全性&#xff1b; 1. md5简介 md5的全称是md5信息摘要算法&#xff08;英文&#xff1a;MD5 Message-Digest Algorithm &#xff09;&#xff0c;一种被广泛使用的密码散列函数&#xff0c;可以产生一个128位…

css-盒模型

巧妙运用margin负值盒模型和怪异盒模型(border padding 包含在内)display: block 能让textarea input 水平尺寸自适应父容器? – 不能 * {box-sizing: border-box; // bs: bb }<textarea/> 是替换元素,尺寸由内部元素决定,不受display水平影响. 当然可以直接设置宽度10…

React(三):脚手架、组件化、生命周期、父子组件通信、插槽、Context

React&#xff08;三&#xff09;一、脚手架安装和创建1.安装脚手架2.创建脚手架3.看看脚手架目录4.运行脚手架二、脚手架下从0开始写代码三、组件化1.类组件2.函数组件四、React的生命周期1.认识生命周期2.图解生命周期&#xff08;1&#xff09;Constructor&#xff08;2&…

Allegro如何导入第一方网表操作指导

Allegro如何导入第一方网表操作指导 在启动PCB设计之前,网表的导入是首要的流程,第一方网表内容如下图 如何将第一方网表导入到PCB中,具体操作如下 点击File点击Import

【项目】用户管理系统

一、需求分析完成一个简单的用户信息管理系统&#xff0c;超级管理员可以登录本系统&#xff0c;查询用户信息、实现用户的管理功能。1.1功能&#xff1a;主要操作和管理的对象&#xff1a;用户。用户分为两类&#xff1a;超级管理员/普通用户。登录功能&#xff08;只针对超管…

深入理解多进程

多进程 一、进程状态 二、创建子进程 - fork 1、函数接口 #include <unistd.h>pid_t fork(void);2、基本概述 成功后&#xff0c;子进程的 PID 在父进程中返回&#xff0c;在子进程中返回 0。 失败时&#xff0c;在父进程中返回 -1&#xff0c;不创建子进程&#xff0c…

MyBatis——进阶操作(2)

标签 if标签 当提交的表单中有些为非必填项&#xff0c;用户并没有上传这些属性的值&#xff0c;那么程序可以上传NUll&#xff0c;也可以用if标签判断用户有没有上传这个值 <if test"参数!null">操作 </if>其中test中填写一条语句&#xff0c;如果得…

uniapp实现地图点聚合功能

前言 在工作中接到的一个任务&#xff0c;在app端实现如下功能&#xff1a; 地图点聚合地图页面支持tab切换&#xff08;设备、劳务、人员&#xff09;支持人员搜索显示分布 但是uniapp原有的map标签不支持点聚合功能&#xff08;最新的版本支持了点聚合功能&#xff09;&am…

爬虫碎碎念

20230304 - &#xff08;非专业人士&#xff0c;简单记录自己的需求和思考&#xff09; 0. 引言 平时看到一些网站的照片什么的&#xff0c;有那种批量下载的需求&#xff0c;当然有些也是视频网站的图片介绍什么的&#xff0c;也即是说&#xff0c;我需要把这些网站的照片批…

剑指 Offer II 013. 二维子矩阵的和

题目链接 剑指 Offer II 013. 二维子矩阵的和 mid 题目描述 给定一个二维矩阵 matrix&#xff0c;以下类型的多个请求&#xff1a; 计算其子矩形范围内元素的总和&#xff0c;该子矩阵的左上角为 (row1, col1)&#xff0c;右下角为 (row2, col2)。 实现 NumMatrix类&#xf…