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>

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

相关文章
|
21天前
|
设计模式 安全 Java
Java并发编程实战:使用synchronized关键字实现线程安全
【4月更文挑战第6天】Java中的`synchronized`关键字用于处理多线程并发,确保共享资源的线程安全。它可以修饰方法或代码块,实现互斥访问。当用于方法时,锁定对象实例或类对象;用于代码块时,锁定指定对象。过度使用可能导致性能问题,应注意避免锁持有时间过长、死锁,并考虑使用`java.util.concurrent`包中的高级工具。正确理解和使用`synchronized`是编写线程安全程序的关键。
|
4天前
|
安全 Java 调度
Java线程:深入理解与实战应用
Java线程:深入理解与实战应用
23 0
|
30天前
|
Python
Python学习之路 02 之分支结构
Python学习之路 02 之分支结构
47 0
Python学习之路 02 之分支结构
|
30天前
|
Java Python 开发者
Python 学习之路 01基础入门---【Python安装,Python程序基本组成】
线程池详解与异步任务编排使用案例-xian-cheng-chi-xiang-jie-yu-yi-bu-ren-wu-bian-pai-shi-yong-an-li
78 2
Python 学习之路 01基础入门---【Python安装,Python程序基本组成】
|
30天前
|
存储 算法 Java
【C/C++ 线程池设计思路】 深入探索线程池设计:任务历史记录的高效管理策略
【C/C++ 线程池设计思路】 深入探索线程池设计:任务历史记录的高效管理策略
74 0
|
1天前
|
人工智能 安全 Java
Python 多线程编程实战:threading 模块的最佳实践
Python 多线程编程实战:threading 模块的最佳实践
10 5
|
2天前
|
消息中间件 缓存 NoSQL
Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
Java多线程实战-CompletableFuture异步编程优化查询接口响应速度
|
5天前
|
测试技术 Android开发
Android App获取不到pkgInfo信息问题原因
Android App获取不到pkgInfo信息问题原因
14 0
|
11天前
|
监控 Java 关系型数据库
JVM工作原理与实战(十三):打破双亲委派机制-线程上下文类加载器
JVM作为Java程序的运行环境,其负责解释和执行字节码,管理内存,确保安全,支持多线程和提供性能监控工具,以及确保程序的跨平台运行。本文主要介绍了打破双亲委派机制的方法、线程上下文类加载器等内容。
14 2
|
12天前
|
JavaScript Java 测试技术
基于Java的网络游戏交易系统的设计与实现(源码+lw+部署文档+讲解等)
基于Java的网络游戏交易系统的设计与实现(源码+lw+部署文档+讲解等)
26 0