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

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

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

一、通过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>

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

相关文章
|
8月前
|
存储 SQL 安全
Java 无锁方式实现高性能线程实战操作指南
本文深入探讨了现代高并发Java应用中单例模式的实现方式,分析了传统单例(如DCL)的局限性,并提出了多种无锁实现方案。包括基于ThreadLocal的延迟初始化、VarHandle原子操作、Record不可变对象、响应式编程(Reactor)以及CDI依赖注入等实现方式。每种方案均附有代码示例及适用场景,同时通过JMH性能测试对比各实现的优劣。最后,结合实际案例设计了一个高性能配置中心,展示了无锁单例在实际开发中的应用。总结中提出根据场景选择合适的实现方式,并遵循现代单例设计原则以优化性能和安全性。文中还提供了代码获取链接,便于读者实践与学习。
155 0
|
9月前
|
Android开发 数据安全/隐私保护 开发者
Android自定义view之模仿登录界面文本输入框(华为云APP)
本文介绍了一款自定义输入框的实现,包含静态效果、hint值浮动动画及功能扩展。通过组合多个控件完成界面布局,使用TranslateAnimation与AlphaAnimation实现hint文字上下浮动效果,支持密码加密解密显示、去除键盘回车空格输入、光标定位等功能。代码基于Android平台,提供完整源码与attrs配置,方便复用与定制。希望对开发者有所帮助。
172 0
|
5月前
|
机器学习/深度学习 传感器 算法
【无人车路径跟踪】基于神经网络的数据驱动迭代学习控制(ILC)算法,用于具有未知模型和重复任务的非线性单输入单输出(SISO)离散时间系统的无人车的路径跟踪(Matlab代码实现)
【无人车路径跟踪】基于神经网络的数据驱动迭代学习控制(ILC)算法,用于具有未知模型和重复任务的非线性单输入单输出(SISO)离散时间系统的无人车的路径跟踪(Matlab代码实现)
372 2
|
9月前
|
设计模式 运维 监控
并发设计模式实战系列(4):线程池
需要建立持续的性能剖析(Profiling)和调优机制。通过以上十二个维度的系统化扩展,构建了一个从。设置合理队列容量/拒绝策略。动态扩容/优化任务处理速度。检查线程栈定位热点代码。调整最大用户进程数限制。CPU占用率100%
571 0
|
4月前
|
Java 调度 数据库
Python threading模块:多线程编程的实战指南
本文深入讲解Python多线程编程,涵盖threading模块的核心用法:线程创建、生命周期、同步机制(锁、信号量、条件变量)、线程通信(队列)、守护线程与线程池应用。结合实战案例,如多线程下载器,帮助开发者提升程序并发性能,适用于I/O密集型任务处理。
442 0
|
6月前
|
数据采集 消息中间件 并行计算
Python多线程与多进程性能对比:从原理到实战的深度解析
在Python编程中,多线程与多进程是提升并发性能的关键手段。本文通过实验数据、代码示例和通俗比喻,深入解析两者在不同任务类型下的性能表现,帮助开发者科学选择并发策略,优化程序效率。
512 1
|
7月前
|
存储 Android开发 数据安全/隐私保护
Thanox安卓系统增加工具下载,管理、阻止、限制后台每个APP运行情况
Thanox是一款Android系统管理工具,专注于权限、后台启动及运行管理。支持应用冻结、系统优化、UI自定义和模块管理,基于Xposed框架开发,安全可靠且开源免费,兼容Android 6.0及以上版本。
828 4
|
10月前
|
数据采集 JSON 网络安全
移动端数据抓取:Android App的TLS流量解密方案
本文介绍了一种通过TLS流量解密技术抓取知乎App热榜数据的方法。利用Charles Proxy解密HTTPS流量,分析App与服务器通信内容;结合Python Requests库模拟请求,配置特定请求头以绕过反爬机制。同时使用代理IP隐藏真实IP地址,确保抓取稳定。最终成功提取热榜标题、内容简介、链接等信息,为分析热点话题和用户趋势提供数据支持。此方法也可应用于其他Android App的数据采集,但需注意选择可靠的代理服务。
436 11
移动端数据抓取:Android App的TLS流量解密方案

热门文章

最新文章