后台默默付出的劳动者,四大组件之服务(Service)

news2024/10/6 14:34:19

后台默默付出的劳动者,四大组件之服务Service

  • 前言
  • 十、后台默默付出的劳动者,四大组件之服务(Service)
    • 10.1 服务是啥?
    • 10.2 Android异步消息处理机制
      • 10.2.1 Android异步消息处理机制介绍
      • 10.2.2 基于Android异步消息处理机制实现“子线程中更新UI操作”
    • 10.3 Android子线程如何切换到主线程?AsyncTask横空出世!
    • 10.4 服务的基本用法
      • 10.4.1 自定义服务类继承自Service
      • 10.4.2 在AndroidManifest.xml中配置服务
      • 10.4.3 在活动中启动和停止服务
      • 10.4.4 在活动中操作服务的方法
    • 10.5 服务的生命周期
    • 10.6 有时候服务露个面才能让人心安,前台服务的使用方法
    • 10.7 服务不占用主线程,IntentService用法
  • 参考书籍:第一行代码

前言

本文讲解Android异步消息处理机制,同时讲解Android四大组件中的最后一种组件–服务,讲解服务的基本使用方法及生命周期,最后讲解一种占用主线程的服务IntentService。

十、后台默默付出的劳动者,四大组件之服务(Service)

10.1 服务是啥?

服务是Android中实现程序后台运行的解决方案,简单点说服务就是处理那些不用露面,但又要一直在运行的任务。

注意:
服务并不是运行在一个独立的进程当中的,而是依赖于创建服务时所在的应用程序进程,并且服务默认运行在主线程中。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。

10.2 Android异步消息处理机制

10.2.1 Android异步消息处理机制介绍

Android异步消息处理机制,简单来说就是一个线程处理着任务,处理着任务,忽然就把一个任务丢给另一个线程去处理了,而自己直接继续走,也不管丢的那个任务是否完成。

Android异步消息处理机制流程如下图所示,主要由4个部分组成:Handler、Message、MessageQueue、Looper。Handler发送Message到MessgeQueue中,Looper一直无休无止的在MessageQueue中取Message,并将取到的Message传递到Handler的handleMessage方法中处理。

因为Handler可以在A线程中实例化,而发送消息的操作可以在B线程中执行,继而就可以实现将B线程的任务发送到A线程进行处理。
在这里插入图片描述

10.2.2 基于Android异步消息处理机制实现“子线程中更新UI操作”

我们知道Android中是不能再子线程中操作UI控件的,必须在主线程中操作UI,那么如何实现在子线程中更新UI呢?我们就可以利用异步消息处理机制,子线程处理任务,处理完要更新UI内容,这时我们可以把更新UI内容的部分再丢给主线程去做就可以了,本质还是在主线程中更新的UI。

核心代码实现如下:

public static final int UPDATE_TEXT=1;//要发送的Message内容

    TextView text;//要更新内容的UI控件

    @SuppressLint("HandlerLeak")
    //主线程实例化Handler
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            switch (msg.what)
            {
                case UPDATE_TEXT:
                    text.setText("你也好呀");
                    break;
                default:
                    break;
            }
        }
    };
    
public class MainActivity extends AppCompatActivity {
    public static final int UPDATE_TEXT=1;//要发送的Message内容

    TextView text;//要更新内容的UI控件

    @SuppressLint("HandlerLeak")
    //主线程中实例化Handle
    private Handler handler=new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            //主线程根据Message中的what值,判断做什么操作
            switch (msg.what)
            {
                case UPDATE_TEXT:
                    text.setText("你也好呀");//更新UI控件内容
                    break;
                default:
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        text=findViewById(R.id.text);
        //开启子线程处理任务
        new Thread(new Runnable() {
                    @Override
                    public void run() {
                        //处理一堆任务
                        //......
                        //封住Message
                        Message message = new Message();
                        message.what=UPDATE_TEXT;
                        //子线程处理完任务,发送Message到Hadler的handleMessage方法
                        handler.sendMessage(message);
                    }
                }).start();
    }
}

10.3 Android子线程如何切换到主线程?AsyncTask横空出世!

Android为了方便处理这种子线程切换到主线程的情况,又设计了一个AsyncTask类,该类封装了异步消息处理机制的详细细节,使得用户在不清楚异步消息处理机制的情况下也能轻松实现子线程到主线程的切换。

AsyncTask是一个抽象类,开发者需要定义一个类继承AsyncTask,并且指定AsyncTask的三个泛型类型,然后重写四个主要的方法即可。

  1. onPreExecute方法在任务开始前调用,一般进行一些界面的初始化操作者,比如显示进度条
  2. doInBackground方法在任务开始后调用,该方法中的所有代码都在子线程中运行,因此一般耗时的任务都在这处理
  3. onProgressUpdate方法在publishProgress方法被调用后,就会执行这个方法.publishProgress方法一般在doInBackground中调用,多用于更新进度条
  4. onPostExecute方法在doInBackground任务执行完,会调用该方法,处理一些收尾的任务

AsyncTask使用示例代码框架

package com.xiaomi.androidthreadtest;

import android.os.AsyncTask;

// 三个泛型解释:
// 泛型1:代表执行AsyncTask时需要传入的参数;
// 泛型2:代表任务执行进度的单位类型
// 泛型3:代表任务执行完成后的返回类型
public class MyAsyncTask  extends AsyncTask<Void,Integer,Boolean> {

    @Override
    //任务开始前调用该方法,一般进行一些界面的初始化操作者,比如显示进度条
    protected void onPreExecute() {
        super.onPreExecute();
    }

    @Override
    //任务开始后调用,该方法中的所有代码都在子线程中运行,因此一般耗时的任务都在这处理
    //参数类型由泛型1决定
    protected Boolean doInBackground(Void... voids) {
        try {
            while (true){
                int downloadPercent=0;
                //做一些耗时的任务
                downloadPercent=doDownlaod();
                //调用该方法publishProgress,让onProgressUpdate去更新进度条
                publishProgress(downloadPercent);
                if (downloadPercent>=100)
                    break;
            }
            
        }catch(Exception e) 
        {
            return false;
        }
        return true;
    }

    @Override
    //publishProgress方法被调用后,就会执行这个方法.publishProgress方法一般在doInBackground中调用,多用于更新进度条
    //参数类型由泛型2决定
    protected void onProgressUpdate(Integer... values) {
        //更新进度条
        //....
    }

    @Override
    //当doInBackground任务执行完,会调用该方法,处理一些收尾的任务
    //参数类型由泛型3决定
    protected void onPostExecute(Boolean aBoolean) {
        //处理一些收尾工作,如关闭进度条
        //......
    }
}

10.4 服务的基本用法

10.4.1 自定义服务类继承自Service

public class MyService1 extends Service {

    //服务第一次启动时调用该方法
    @Override
    public void onCreate() {
        super.onCreate();
    }

    //服务每次启动都会调用该方法
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
 
    //绑定服务,可以使得该服务与活动进行交互
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    
    //停止服务后会调用此方法
    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

10.4.2 在AndroidManifest.xml中配置服务

至此Android的四大组件:活动、广播机制,内容提供器,服务我们都接触到了,它们有一个共性,那就是必须要在AndroidManifest.xml文件中配置。

服务配置方法,是在application标签下添加service子标签,示例配置如下

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.xiaomi.servicetest">

    <application>
        //.....
        <service
            android:name=".MyService"
            android:enabled="true"
            android:exported="true"/>
        //....
    </application>

</manifest>

10.4.3 在活动中启动和停止服务

  1. 启动服务核心代码
Intent intent = new Intent(this, MyService.class);
startService(intent);
  1. 停止服务核心代码
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);

10.4.4 在活动中操作服务的方法

  1. 自定义一个类继承自Binder
    示例代码如下:
class DownloadBinder extends Binder
    {
        public void startDownload()
        {
            Log.d(TAG, "startDownload: executed");
        }

        public int getProgress()
        {
            Log.d(TAG, "getProgress: executed");
            return 0;
        }
    }
  1. 在自定义服务类的OnBind中返回自动Binder对象
    示例代码如下
@Override
public IBinder onBind(Intent intent) {
        // TODO: Return the communication channel to the service.
        return mBinder;
    }
  1. 在活动中实现ServiceConnection匿名类,在该类中获取服务中绑定的对象,通过获得的绑定对象,就可以对服务中的方法进行操作。
    示例代码如下:
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 name) {

        }
    };
  1. 绑定服务的示例代码
Intent bindIntent =new Intent(MainActivity.this,MyService.class);
bindService(bindIntent,connection,BIND_AUTO_CREATE);
  1. 解绑服务的示例代码
unbindService(connection);

10.5 服务的生命周期

服务和活动及碎片一样,也有自己的生命周期,服务的生命周期内可能回调的方法包括我们上面提到的onCreate,onStartCommand,onBind,onDestory等等。

  • 服务经典生命周期情况一:Context.startService->[onCreate,服务首次创建的时候调用]->onStartCommand->Context.stopService/stopSelf->onDestory
  • 服务经典生命周期情况二:Context.bindService->[onCreate,服务首次创建的时候调用]->onBind->Context.unbindService->onDestory

注意:
若对一个服务既调用了Context.startService和Context.bindService,如何才能销毁这个服务呢?此时我们必须对应的调用Context.stopService和Context.unbindService后才能销毁这个服务。

10.6 有时候服务露个面才能让人心安,前台服务的使用方法

服务一直在后台默默的干活,但是服务的系统优先级比较低,当系统出现内存不足的时候,就有可能回收正在运行的服务。那么如果需要一个服务一直保持运行,或者想要在系统的状态栏看到正在运行的服务,就可以使用前台服务

前台服务和普通服务最大的区别就在于,它会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。

普通服务如何转为前台服务呢?实现方法非常简单,只需要在服务的onCreate方法中调用startForeground方法即可,该方法需要传入一个非0的标识id,和一个通知Notification对象。

示例代码如下:

public class MyService extends Service {

    //......
   
    
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "onCreate: executed");

        //将服务变为前台服务
        //1. 构建通知对象
        String CHANNEL_ONE_ID = "CHANNEL_ONE_ID";
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent= PendingIntent.getActivity(this, 0, intent, PendingIntent.FLAG_IMMUTABLE);
        Notification notification= new Notification.Builder(this, CHANNEL_ONE_ID)
                .setChannelId(CHANNEL_ONE_ID)
                .setTicker("Nature")
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("这是一条前台服务标题")
                .setContentIntent(pendingIntent)
                .setContentText("这是一条前台服务内容")
                .build();
        notification.flags|= Notification.FLAG_NO_CLEAR;
        //2. 调用startForeground方法
        startForeground(1, notification);
    }

   //......
}

10.7 服务不占用主线程,IntentService用法

我们知道服务默认运行在主线程中,因此,如果服务处理的一些耗时的逻辑,就会出现ANR(Application Not Responding)的情况。

为了避免ANR,我们可以在服务的具体方法中开子线程去处理耗时的逻辑,示例代码如下:

public class MyService1 extends Service {

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {

        new Thread(new Runnable() {
            @Override
            public void run() {
                //处理逻辑
                //...
                //处理完成,主动停止服务
                stopSelf();
            }
        }).start();

        return super.onStartCommand(intent,flags,startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

Android为了更方便开发者,在子线程中运行服务的耗时逻辑,也提供了一个IntentService类,示例代码如下:

//该服务中的onHandleIntent在子线程中运行,并且任务完成会自动停止服务
public class MyIntentService extends IntentService {
    private static final String TAG = "MyIntentService";
    
    public MyIntentService() {
        super(TAG);
    }
    //服务启动后,会执行该方法,该方法在子线程中运行,并且执行完成会自动停止服务
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.d(TAG, "MyIntentService Thread id is "+ Thread.currentThread().getId());
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "onDestroy: executed");
    }
}

参考书籍:第一行代码

链接:https://pan.baidu.com/s/1aXtOQCXL6qzxEFLBlqXs1Q?pwd=n5ag
提取码:n5ag

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

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

相关文章

Packet Tracer - 排除多区域 OSPFv2 故障

地址分配表 设备 接口 IP 地址 子网掩码 默认网关 ISP GigabitEthernet0/0 209.165.200.17 255.255.255.240 不适用 ASBR GigabitEthernet0/0 209.165.200.18 255.255.255.240 不适用 Serial0/0/0 10.1.1.2 255.255.255.252 不适用 Serial0/0/1 10.2.2…

制作一个企业网站——html华为官网购物商城项目的设计与实现

常见网页设计作业题材有 个人、 美食、 公司、 学校、 旅游、 电商、 宠物、 电器、 茶叶、 家居、 酒店、 舞蹈、 动漫、 服装、 体育、 化妆品、 物流、 环保、 书籍、 婚纱、 游戏、 节日、 戒烟、 电影、 摄影、 文化、 家乡、 鲜花、 礼品、 汽车、 其他等网页设计题目, A…

【Redis】使用 Java 客户端连接 Redis

一、三种客户端比较 Jedis : 学习成本低&#xff0c;以 Redis 命令作为方法名称&#xff0c;但是其线程不安全 lettuce&#xff1a;基于 Netty 实现&#xff0c;支持同步、异步、响应式编程&#xff08;SpringBoot&#xff09;&#xff0c;并且线程安全。支持 Redis 的哨兵模…

【附源码】计算机毕业设计JAVA幼儿健康管理系统

【附源码】计算机毕业设计JAVA幼儿健康管理系统 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA my…

Nacos入门

文章目录一、安装nacos二、nacos项目环境配置一、安装nacos 压缩包下载地址&#xff1a; https://github.com/alibaba/nacos/releases 压缩包解压&#xff1a; nacos 中修改端口(8848 端口被占用需要修改)&#xff1a; 进入 conf 进入 application.properties 端口位置 启…

【springboot进阶】摆脱 if/else 的高级应用 - 策略模式

目录 一、策略模式的介绍 二、策略模式的使用场景 三、策略模式的应用 1、入参和出参类 2、策略接口 3、策略具体实现 4、策略测试 三、一些使用技巧 四、总结 对于一个逻辑相对复杂的功能应用中&#xff0c;难免需要做很多的逻辑判断&#xff0c;需要写一堆的 if/els…

Flink常用Sink(elasticsearch(es)Sink、RedisSink、KafkaSink、MysqlSink、FileSink)

flink输出到es、redis、mysql、kafka、file 文章目录配置pom文件公共实体类KafkaSInkElasticsearchSink(EsSink)RedisSinkMysqlSink(JdbcSink)FileSink自己先准备一下相关环境 配置pom文件 <properties><maven.compiler.source>8</maven.compiler.source>&l…

【概率论与数理统计】第四章知识点复习与习题

思维导图 基础知识 数学期望 定义 数学期望其实很好理解&#xff0c;就是均值&#xff0c;当然这里并不是直接计算样本的均值&#xff0c;而是考虑到样本对应的概率。我们分离散和连续两类来讨论数学期望。 离散型 对随机变量X的分布律为 若级数 绝对收敛&#xff0c;则称该…

BaGet搭建Nuget私仓(window10docker)

文章目录一、搭建背景二、框架简介三、私仓搭建1、环境2、win10上部署2.1安装SDK2.2下载和解压BaGet包2.3运行项目2.4类库项目2.5将包发布到私有Nuget中2.6使用BaGetFirstLib2.7使用密码增加安全性3、Docker上部署3.1创建相关文件3.2拉取镜像3.3运行3.4访问四、结束一、搭建背景…

微服务入门

文章目录一、微服务大概认识二、单体架构架构和分布式架构三、微服务架构特征四、微服务技术对比五、SpringCloud 与 SpringBoot版本兼容关系如下&#xff1a;一、微服务大概认识 二、单体架构架构和分布式架构 单体架构&#xff1a;将业务的所有功能集中在一个项目中开发&…

“加密上海·喜玛拉雅Web3.0数字艺术大展”落幕,AIGC和数字艺术衍生品是最大赢家?...

图片来源&#xff1a;由无界版图 AI 绘画工具生成11月11日&#xff0c;为期一个月的第一届“加密上海喜玛拉雅3eb3.0数字艺术大展”在喜玛拉雅美术馆拉开帷幕。这无疑是当下中国最盛大、最集中的一次数字艺术展览。艺术展吸引了像Soul 、小红书、网易星球、bilibili、酷天下、无…

mysql实战操作总结

1、问题描述 关于mysql操作&#xff0c;记录下&#xff1b; 2、问题说明 1.停止正在执行的sql 数据量太大&#xff0c;数据库没反应&#xff0c;用的navicat&#xff0c;就在查询页面&#xff0c;执行&#xff1a; show processlist;---会显示对应的查询sql找到最前面是id…

vue js实现文件上传压缩优化处理

vue js实现文件上传压缩优化处理 两种方法 &#xff1a; 第1种是借助canvas的封装的文件压缩上传第2种&#xff08;扩展方法&#xff09;使用compressorjs第三方插件实现 目录 vue js实现文件上传压缩优化处理 借助canvas的封装的文件压缩上传 1.新建imgUpload.js 2.全局引…

高清免费壁纸网站推荐

本期内容&#xff0c;为大家整理了6个相当不错的免费壁纸网站&#xff0c;访问量极大、活跃度极高。 无需登录、注册&#xff0c;打开右键就可以下载&#xff0c;而且壁纸图片的尺寸大小&#xff0c;可以选择&#xff0c;从手机、平板、再到电脑壁纸&#xff0c;全部都是高清。…

Windows/Ubuntu安装frida和objection

​Windows/Ubuntu安装frida和objection 1.Windows环境使用管理员权限安装frida,Ubuntu使用普通或Root权限安装均可 https://github.com/frida/frida (1).安装frida(Python2.7.8及以上版本) pip install numpy matplotlib -i https://mirrors.aliyun.com/pypi/simplepip insta…

imx6ull pro BSP 工具链

BSP&#xff0c;Board Support Package&#xff0c;指板级支持包&#xff0c;是构建嵌入式操作系统所 需的引导程序(Bootload)、内核(Kernel)、根文件系统(Rootfs)和工具链 (Toolchain)。 每种开发板的 BSP 都不一样&#xff0c;并且这些源码都非常庞大。我们把这些源码都 放在…

BI-SQL丨JOB

JOB 在SQL Server中&#xff0c;JOB属于常用功能&#xff0c;我们经常需要通过JOB来执行一些定时的作业任务&#xff0c;例如数据备份、存储过程、SSIS任务、SSAS刷新等等。 通常情况下&#xff0c;我们都是在SSMS中对JOB进行创建、删除、维护等任务的。 前置条件 使用JOB功…

基于Mxnet实现实例分割-MaskRCNN【附部分源码】

文章目录前言一、什么是实例分割二、数据集的准备1.数据集标注2.VOC数据集转COCO数据集三、基于Mxnet搭建MaskRCNN1.引入库2.CPU/GPU配置3.获取训练的dataset1.coco数据集2.自定义数据集4.获取类别标签5.模型构建6.数据迭代器7.模型训练1.优化器设置2.loss计算3.acc计算4.循环训…

堆 堆排序 TopK问题

堆一&#xff0c;堆的相关函数接口实现1&#xff0c;堆的初始化2&#xff0c;堆的销毁3&#xff0c;插入4&#xff0c;向上调整5&#xff0c;删除6&#xff0c;向下调整7&#xff0c;建堆8&#xff0c;取堆顶9&#xff0c;判空10&#xff0c;堆的大小二&#xff0c;向上建堆与向…

用DIV+CSS技术设计的鲜花网站(web前端网页制作课作业)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…