运行有问题或需要源码请点赞关注收藏后评论区留言私信~~~
一、通过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>
创作不易 觉得有帮助请点赞关注收藏~~~