Android App网络通信中通过runOnUiThread快速操纵界面以及利用线程池Executor调度异步任务实战(附源码 简单易懂)

news2025/2/24 6:44:06

运行有问题或需要源码请点赞关注收藏后评论区留言私信~~~

一、通过runOnUiThread快速操纵界面

因为Android规定分线程不能够直接操纵界面,所以它设计了处理程序工具,由处理程序负责在主线程和分线程之间传递数据,如果分线程想刷新界面,就得向处理程序发送消息,由处理程序在handleMessage方法中操作控件

测试效果如下 可观察到新闻播报效果 可手动点击按钮控制新闻播报的开始与暂停

 

 

 代码如下

Java类

package com.example.network;

import android.os.Bundle;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;

import com.example.network.util.DateUtil;

import java.util.Random;

public class ThreadUiActivity extends AppCompatActivity {
    private TextView tv_message; // 声明一个文本视图对象
    private boolean isPlaying = false; // 是否正在播放新闻
    private String[] mNewsArray = { "北斗导航系统正式开通,定位精度媲美GPS",
            "黑人之死引发美国各地反种族主义运动", "印度运营商禁止华为中兴反遭诺基亚催债",
            "贝鲁特发生大爆炸全球紧急救援黎巴嫩", "日本货轮触礁毛里求斯造成严重漏油污染"
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread_ui);
        tv_message = findViewById(R.id.tv_message);
        findViewById(R.id.btn_start).setOnClickListener(v -> {
            if (!isPlaying) { // 如果不在播放就开始播放
                isPlaying = true;
                new Thread(() -> broadcastNews()).start(); // 启动新闻播放线程
            }
        });
        findViewById(R.id.btn_stop).setOnClickListener(v -> isPlaying = false);
    }

    // 播放新闻
    private void broadcastNews() {
        // 回到主线程(UI线程)操纵界面
//        runOnUiThread(new Runnable() {
//            @Override
//            public void run() {
//                String desc = String.format("%s\n%s %s", tv_message.getText().toString(),
//                        DateUtil.getNowTime(), "开始播放新闻");
//                tv_message.setText(desc);
//            }
//        });
        String startDesc = String.format("%s\n%s %s", tv_message.getText().toString(),
                DateUtil.getNowTime(), "开始播放新闻");
        // 回到主线程(UI线程)操纵界面
        runOnUiThread(() -> tv_message.setText(startDesc));
        while (isPlaying) { // 正在播放新闻
            try {
                Thread.sleep(2000); // 睡眠两秒(2000毫秒)
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            String runDesc = String.format("%s\n%s %s", tv_message.getText().toString(),
                    DateUtil.getNowTime(), mNewsArray[new Random().nextInt(5)]);
            // 回到主线程(UI线程)操纵界面
            runOnUiThread(() -> tv_message.setText(runDesc));
        }
        String endDesc = String.format("%s\n%s %s", tv_message.getText().toString(),
                DateUtil.getNowTime(), "新闻播放结束,谢谢观看");
        // 回到主线程(UI线程)操纵界面
        runOnUiThread(() -> tv_message.setText(endDesc));
        isPlaying = false;
    }

}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/btn_start"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="开始播放新闻"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Button
            android:id="@+id/btn_stop"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:text="停止播放新闻"
            android:textColor="@color/black"
            android:textSize="17sp" />
    </LinearLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="180dp"
        android:orientation="vertical" >

        <TextView
            android:id="@+id/tv_message"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:paddingLeft="5dp"
            android:scrollbars="vertical"
            android:gravity="left|bottom"
            android:maxLines="9"
            android:textColor="@color/black"
            android:textSize="15sp" />
    </LinearLayout>

</LinearLayout>

二、利用线程池Executor调度异步任务

线程池中的线程数量最好由开发者分配,这时需要使用ThreadPoolExecutor的构造方法创建线程池对象,下面是构造方法的参数说明

int corePoolSize 线程池的最小线程个数

int maximumPoolSize 线程池的最大线程个数

long keepAliveTime 非核心线程在无任务时的等待时长 若超过该时间仍未分配任务 则该线程自动结束

TimeUnit unit 时间单位 包括秒 毫秒和微秒

下面是它的常用方法

execute 向执行队列添加指定的任务

remove 移除指定任务

shutdown 关闭线程池

isTerminated 判断线程池是否关闭

setCorePoolSize 设置线程池的最小线程个数

getPoolSize 获取当前的线程个数

getActiveCount 获取当前的活动线程个数 

各种线程池的执行结果如下图  单线程则每隔两秒打印一行日志  多线程则每秒打印四行日志 无限制个数的则一秒内把所有线程打印出来 

 

 

 

 代码如下

Java类

package com.example.network;

import android.os.Build;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.example.network.util.DateUtil;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ThreadExecutorActivity extends AppCompatActivity {
    private final static String TAG = "ThreadExecutorActivity";
    private TextView tv_desc; // 声明一个文本视图对象
    private String mDesc = "";
    private ExecutorService mThreadPool; // 声明一个线程池对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_thread_executor);
        tv_desc = findViewById(R.id.tv_desc);
        initPoolSpinner(); // 初始化线程池下拉框
    }

    // 初始化线程池下拉框
    private void initPoolSpinner() {
        ArrayAdapter<String> poolAdapter = new ArrayAdapter<>(this,
                R.layout.item_select, poolArray);
        Spinner sp_pool = findViewById(R.id.sp_pool);
        sp_pool.setPrompt("请选择线程池类型");
        sp_pool.setAdapter(poolAdapter);
        sp_pool.setOnItemSelectedListener(new PoolSelectedListener());
        sp_pool.setSelection(0);
    }

    private String[] poolArray = {"无线程池", "单线程线程池", "多线程线程池", "无限制线程池", "自定义线程池", "主线程"};

    class PoolSelectedListener implements AdapterView.OnItemSelectedListener {
        public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
            if (mThreadPool != null) {
                //mThreadPool.shutdown(); // 停止接收新任务,原来的任务继续执行
                mThreadPool.shutdownNow(); // 停止接收新任务,原来的任务停止执行
            }
            mDesc = poolArray[arg2] + "正在处理";
            if (arg2 == 1) { // 单线程线程池
                mThreadPool = Executors.newSingleThreadExecutor();
                startPoolTask(); // 开始执行线程池处理
            } else if (arg2 == 2) { // 多线程线程池
                mThreadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(4);
                startPoolTask(); // 开始执行线程池处理
            } else if (arg2 == 3) { // 无限制线程池
                mThreadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool();
                startPoolTask(); // 开始执行线程池处理
            } else if (arg2 == 4) { // 自定义线程池
                mThreadPool = new ThreadPoolExecutor(
                        2, 5, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(19));
                startPoolTask(); // 开始执行线程池处理
            } else if (arg2 == 5) { // 主线程。注意耗时任务会堵塞主线程
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                    for (int i = 0; i < 20; i++) {
                        // 创建一个新的消息分发任务
                        MessageRunnable task = new MessageRunnable(i);
                        getApplication().getMainExecutor().execute(task); // 命令主线程执行该任务
                    }
                } else {
                    Toast.makeText(ThreadExecutorActivity.this,
                            "主线程需要Android9或更高版本", Toast.LENGTH_SHORT).show();
                }
            }
        }

        public void onNothingSelected(AdapterView<?> arg0) {}
    }

    // 开始执行线程池处理
    private void startPoolTask() {
        for (int i = 0; i < 20; i++) {
            // 创建一个新的消息分发任务
            MessageRunnable task = new MessageRunnable(i);
            mThreadPool.execute(task); // 命令线程池执行该任务
        }
    }

    // 定义一个消息分发任务
    private class MessageRunnable implements Runnable {
        private int mIndex;
        public MessageRunnable(int index) {
            mIndex = index;
        }

        @Override
        public void run() {
            runOnUiThread(() -> {
                mDesc = String.format("%s\n%s 当前序号是%d", mDesc, DateUtil.getNowTime(), mIndex);
                tv_desc.setText(mDesc);
            });
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="40dp" >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:paddingLeft="5dp"
            android:gravity="center"
            android:text="线程池类型:"
            android:textColor="@color/black"
            android:textSize="17sp" />

        <Spinner
            android:id="@+id/sp_pool"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="left|center"
            android:spinnerMode="dialog" />
    </LinearLayout>

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical" >

            <TextView
                android:id="@+id/tv_desc"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:paddingLeft="5dp"
                android:textColor="@color/black"
                android:textSize="17sp" />
        </LinearLayout>
    </ScrollView>

</LinearLayout>

创作不易 觉得有帮助请点赞关注收藏~~~

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

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

相关文章

精心整理16条MySQL使用规范,减少80%问题

1. 禁止使用select * 阿里开发规范中&#xff0c;有这么一句话&#xff1a; **select *** 会查询表中所有字段&#xff0c;如果表中的字段有更改&#xff0c;必须修改SQL语句&#xff0c;不然就会执行错误。 查询出非必要的字段&#xff0c;徒增磁盘IO和网络延迟。 2. 用小表…

小学生python游戏编程arcade----敌人精灵上方显示方框及子弹显示问题

小学生python游戏编程arcade----敌人精灵上方显示方框及子弹显示问题前言1、敌人精灵上方显示方框1.1 修改enemy_tank类1.2 引用1.3 效果图2、调整方法2.1 类方法2.2 类的引用2.3 效果图2.4 大小位置调整后3、子弹过线自动消失3.1 子弹的更新中3.2 原因查到&#xff0c;把以下代…

day11 多级缓存

day11 多级缓存 1、什么是多级缓存 传统的缓存策略一般是请求到达Tomcat后&#xff0c;先查询Redis&#xff0c;如果未命中则查询数据库&#xff0c;如图&#xff1a; 存在下面的问题&#xff1a; 请求要经过 Tomcat 进行处理&#xff0c;Tomcat 的性能成为整个系统的瓶颈Red…

数字孪生助力轨道交通安保可视化应用

截至2020年12月31日&#xff0c;全国&#xff08;不含港澳台&#xff09;共有44个城市开通运营城市轨道交通线路233条&#xff0c;运营里程7545.5公里&#xff0c;车站4660座&#xff0c;完成客运量175.9亿人次&#xff0c;进站量109.1亿人次。针对轨道交通地铁站内日常监测、事…

牛客网语法篇练习分支控制(一)

1.据说智商140以上者称为天才&#xff0c;KiKi想知道他自己是不是天才&#xff0c;请帮他编程判断。输入一个整数表示一个人的智商&#xff0c;如果大于等于140&#xff0c;则表明他是一个天才&#xff0c;输出“Genius”。 while True:try:a int(input())if a >140:print…

云原生加速器企业维格表创始人陈霈霖:提供人人可用的数字化转型全新方案,真正驱动组织创新

看上去是像Excel一样的在线协同表格&#xff0c;却能把文件、表格、图片、视频、填表单等变换出各种视图&#xff0c;它能帮助你高效方便的管理各种零碎的信息和数据&#xff1b;也能根据你的想法DIY各种功能&#xff0c;5分钟即可搭建一个适合自己的文档管理系统&#xff0c;实…

C#上位机系列(1)—项目的建立

本文是讲解C#.net平台的Winform框架下的第一个内容&#xff0c;手把手介绍项目的创建方式以及一些写软件时常用的功能。之前写过一篇关于示波器的比较抽象&#xff0c;本文讲解从零开始的每一个步骤。 VS2022以及C#.net平台的Winform框架自行百度下载。 1.创建一个新的项目 …

智慧经营| 物业数字化管理系统

无论小区、公寓还是豪华区&#xff0c;总是少不了物业的身影&#xff0c;通过物业可以为住户解决许多事情&#xff0c;比如物业报事、物业维修、便民服务、送水上门、房屋租赁、投诉等&#xff0c;但小区内公告栏没人看、挨家挨户的去通知效率低、且无法全面完善管理区域内的所…

Linux操作系统~尝试自己制作并使用动静态库

目录 1.动态库和静态库到底是什么 &#xff08;1&#xff09;.静态库 vs 动态库 &#xff08;2&#xff09;.动态链接和静态链接的优劣 &#xff08;3&#xff09;.ldd指令 2.自己制作静态库 &#xff08;1&#xff09;.打包静态库 &#xff08;2&#xff09;.ar指令 3…

Java基础之《netty(4)—NIO之Channel》

一、基本介绍 1、NIO的通道类似于流&#xff0c;但有些区别 &#xff08;1&#xff09;通道可以同时进行读写&#xff0c;而流只能读或者只能写 &#xff08;2&#xff09;通道可以实现异步读写数据 &#xff08;3&#xff09;通道可以从缓冲读数据&#xff0c;也可以写数据到…

C++模板基础和STL之string

泛型编程之模板 使用实际调用的函数不是同一个&#xff0c;因为不同类型参数&#xff0c;函数栈帧中开辟的空间不一样。参数不一样&#xff0c;所以调用的函数也不一样。使用模板速度必重载速度更快&#xff0c;因为是编译器直接生成。而STL就叫标准模板库。 template<cla…

Activity的生命周期

文章目录Activity的生命周期一.返回栈二.Activity状态1.运行状态2.暂停状态3.停止状态4.销毁状态三.Activity的生存期onCreate()onStart()onResume()onPause()onStop()onDestroy()onRestart()完整生存期可见生存期前台生存期Activity的生命周期图体验Activity的生命周期编写三个…

PTA题目 阅览室

天梯图书阅览室请你编写一个简单的图书借阅统计程序。当读者借书时&#xff0c;管理员输入书号并按下S键&#xff0c;程序开始计时&#xff1b;当读者还书时&#xff0c;管理员输入书号并按下E键&#xff0c;程序结束计时。书号为不超过1000的正整数。当管理员将0作为书号输入时…

机器人工程考研难易主观感受和客观数据

简易版&#xff08;共性&#xff09;&#xff1a; 主观反馈&#xff1a;越来越难 客观数据&#xff1a;研究生录取人数越来越多&#xff0c;比例也越来越高 看起来矛盾&#xff0c;其实并非如此。 详细数据&#xff1a; 80后是指1980年至1989年出生的人口&#xff0c;对应2…

MySQL_07:单行函数

文章目录一、函数的基本理解1.函数的理解1.1内置函数的基本理解1.2不同DBMS函数的差异1.3MySQL的内置函数的分类2.图解多行函数和单行函数二、单行函数1.数值函数1.1基本函数1.2角度与弧度互换函数1.3三角函数1.4指数与对数1.5进制间转换2.字符串函数3.日期和时间函数3.1获取日…

python常用操作之使用多个界定符(分隔符)分割字符串

本系列文章会总结python中各种常见及常用的内置方法、对不同数据结构的操作&#xff0c;参考书籍《python cookbook》第三版 分割字符串单个界定符分割字符串代码演示多个界定符分割字符串代码演示注意总结在日常学习及工作中&#xff0c;不论是在解析数据还是在产出输出&#…

【LeetCode】No.98. Validate Binary Search Tree -- Java Version

题目链接&#xff1a;https://leetcode.com/problems/validate-binary-search-tree/ 1. 题目介绍&#xff08;Validate Binary Search Tree&#xff09; Given the root of a binary tree, determine if it is a valid binary search tree (BST). 【Translate】&#xff1a; 给…

Qt 堆栈窗体QStackedWidget使用

QStackedWidget控件相当于一个容器&#xff0c;提供一个空间来存放一系列的控件&#xff0c;并且每次只能有一个控件是可见的&#xff0c;即被设置为当前的控件。QStackedWidget可用于创建类似于QTabWidget提供的用户界面。 它是一个构建在QStackedLayout类之上的方便布局小部件…

dolphinscheduler 2.0.6 负载均衡源码

目录&#x1f42c;官网介绍&#x1f42c;负载均衡&#x1f420;加权随机&#xff08;random&#xff09;&#x1f420;平滑轮询&#xff08;roundrobin&#xff09;&#x1f420;线性负载&#xff08;lowerweight&#xff09;&#x1f435;其它&#x1f42c;官网介绍 官网资料&…

【计算机网络】网络层:虚拟专用网

由于IP地址的紧缺&#xff0c;一个机构能够申请到的IP地址数往往远小于本机构拥有的主机数。 如果一个机构内部的计算机通信也采用TCP/IP协议&#xff0c;那么这些仅字机构内部使用的计算机就可以由本机构自行分配其IP地址。 本地地址&#xff08;专用地址&#xff0c;互联网对…