传统多线程开发

news/2024/7/7 14:49:25

Android开发高级进阶

第一章学习


传统多线程开发

概要:

之前的文章里写过了AsyncTask的一些坑,这次就不讲它了,使用传统的 Handler和Message来进行线程的使用,并且第一次添加了CallBack方式的接口进行回调操作


多线程 这概念并不需要多余的介绍了,用法跟Java里没什么不同

new Thread(new Runnable() {
@Override
    public void run() {
    // 处理具体的逻辑
    }
}).start();
复制代码

什么是UI线程,什么是工作线程: Android中,将其他线程和主线程(UI线程)进行了区分,由于Android的图形界面总是伴随着各种动画效果,所以Android特地为UI自动开启了主线程,用于持续不断的计算,且UI的操作必须在主线程里进行,如果在主线程里进行了耗时操作,那就会出现ANR (Application Not Responding),此时,多线程就非常必要了。


从子线程回调数据进行操作:

简单的线程操作,只能控制他start,一旦运行完毕了,无法进行返回,或者无法进行UI的操,接下来为解决这两种问题,提供一些方法。

标准的线程间通讯是使用Handler+Message进行通讯的,当然这种通讯毕竟能够传递的参数非常有限,大致上只有int和object,非常少,某些时候用得并不顺手。

比如网络连接的时候,想要在联网获得Json文件后,立即调用另一个方法对此Json文件进行处理,此时可以引入回调的机制。

1.定义接口,是一个抽象方法

    public interface HttpCallbackListener {
        void onFinish(String response);
        void onError(Exception e);
    }
复制代码

2.在联网方法的传入参数中,定义此接口

public static void sendHttpRequest(final String address, final HttpCallbackListener
            listener) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                HttpURLConnection connection = null;
                try {
                    URL url = new URL(address);
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    connection.setConnectTimeout(8000);
                    connection.setReadTimeout(8000);
                    connection.setDoInput(true);
                    connection.setDoOutput(true);
                    InputStream in = connection.getInputStream();
                    BufferedReader reader = new BufferedReader(new
                            InputStreamReader(in));
                    StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = reader.readLine()) != null) {
                        response.append(line);
                    }
                    if (listener != null) {
// 回调onFinish()方法
                        listener.onFinish(response.toString());
                    }
                } catch (Exception e) {
                    if (listener != null) {
// 回调onError()方法
                        listener.onError(e);
                    }
                } finally {
                    if (connection != null) {
                        connection.disconnect();
                    }
                }
            }
        }).start();
    }
复制代码

3.此时,其它地方调用此联网方法时,将会需要重写这个抽象方法

HttpUtil.sendHttpRequest(address, new HttpCallbackListener() {
        @Override
        public void onFinish(String response) {
// 在这里根据返回内容执行具体的逻辑
        }
        @Override
        public void onError(Exception e) {
// 在这里对异常情况进行处理
        }
    });
复制代码

完成了在不同的地方,都能在子线程中执行到onFinish()方法时,进行回调,立即取得子线程的计算结果并执行想要进行的操作。 但是回调的同时,仍然还是子线程中,并不允许进行UI操作。


从子线程进行UI操作:

Android为子线程中进行UI操作提供了一些封装方法:

  • Activity.runOnUiThread(Runnable action) 如同字面意思般在工作线程中跳转到UI线程进程操作
  • View.post(Runnable action) 直接给控件添加线程操作,此处可以更新UI
  • View.postDelayed(Runnable action, long delayMillis) 方法2的延时版本
  • new Handler(Looper.getMainLooper()).post(Runnable action) 获取了主线程的Looper进行线程操作,当然可以更新UI

举个栗子:

new Thread(new Runnable() {
    @Override
    public void run() {
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                mTestTextView.setText("赚钱好难");
            }
        });
    }
}).start();
复制代码

3个方法使用起来很类似,不一一说明了。


多线程管理

线程池的操作

  • new Thread()的缺点

  • 每次new Thread()耗费性能

  • 调用new Thread()创建的线程缺乏管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。

  • 不利于扩展,比如如定时执行、定期执行、线程中断

  • 采用线程池的优点

  • 重用存在的线程,减少对象创建、消亡的开销,性能佳

  • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞

  • 提供定时执行、定期执行、单线程、并发数控制等功能

Executor

android中线程池的概念来源于java中的Executor, 线程池真正的实现类是ThreadPoolExecutor,它间接实现了Executor接口。 Executor接口只有一个方法execute(),该方法用来执行线程中的操作。

Executor executor = new MyExecutor();
executor.execute(new RunnableTaskOne());
executor.execute(new RunnableTaskTwo());
复制代码

ThreadPoolExecutor提供了一系列参数来配置线程池,通过不同的参数配置实现不同功能特性的线程池,android中的Executors类提供了4个工厂方法用于创建4种不同特性的线程池给开发者用.

newFixedThreadPool

创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。

ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 20; i++) {
   Runnable syncRunnable = new Runnable() {
       @Override
       public void run() {
            Log.e(TAG, Thread.currentThread().getName());
       }
    };
    executorService.execute(syncRunnable);
}
复制代码

运行结果:总共只会创建5个线程, 开始执行五个线程,当五个线程都处于活动状态,再次提交的任务都会加入队列等到其他线程运行结束,当线程处于空闲状态时会被下一个任务复用

特点:只有核心线程数,并且没有超时机制,因此核心线程即使闲置时,也不会被回收,因此能更快的响应外界的请求.

newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程

ExecutorService executorService = Executors.newCachedThreadPool(5);
for (int i = 0; i < 100; i++) {
   Runnable syncRunnable = new Runnable() {
       @Override
       public void run() {
            Log.e(TAG, Thread.currentThread().getName());
       }
    };
    executorService.execute(syncRunnable);
}
复制代码

运行结果:缓存线程池大小是不定值,可以需要创建不同数量的线程,在使用缓存型池时,先查看池中有没有以前创建的线程,如果有,就复用.如果没有,就新建新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务

特点:没有核心线程,非核心线程数量没有限制, 超时为60秒.

newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行 schedule(Runnable command,long delay, TimeUnit unit)创建并执行在给定延迟后启用的一次性操作

ExecutorService executorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 200; i++) {
   Runnable syncRunnable = new Runnable() {
       @Override
       public void run() {
            Log.e(TAG, Thread.currentThread().getName());
       }
    };
    executorService.schedule(syncRunnable, 5000, TimeUnit.MILLISECONDS);
}
复制代码

特点:核心线程数是固定的,非核心线程数量没有限制, 没有超时机制.主要用于执行定时任务和具有固定周期的重复任务.

SingleThreadExecutor

创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行

ExecutorService executorService = Executors.newScheduledThreadPool(5);
for (int i = 0; i < 200; i++) {
   Runnable syncRunnable = new Runnable() {
       @Override
       public void run() {
            Log.e(TAG, Thread.currentThread().getName());
       }
    };
    executorService.execute(syncRunnable);
}
复制代码

特点:只有一个核心线程,并没有超时机制.意义在于统一所有的外界任务到一个线程中, 这使得在这些任务之间不需要处理线程同步的问题.

转载于:https://juejin.im/post/5a5c5f5f5188257329146946


http://www.niftyadmin.cn/n/2133344.html

相关文章

Linux 中 Vi 的使用

vi —— 终端中的编辑器 目标 vi 简介打开和新建文件三种工作模式常用命令分屏命令常用命令速查图 01. vi 简介 1.1 学习 vi 的目的 在工作中&#xff0c;要对 服务器 上的文件进行 简单 的修改&#xff0c;可以使用 ssh 远程登录到服务器上&#xff0c;并且使用 vi 进行快…

Jupyterlab 插件安装后侧边栏找不到的解决

Jupyterlab 插件重新安装后侧边栏找不到的解决 截止发帖时间&#xff0c;JupyterLab 有这样一个 bug&#xff0c;在官方文档找不到解决方案&#xff0c;我找了好几天找到了一个 issue 才解决&#xff1a; JupyterLab 安装 extension &#xff08;插件&#xff09;时&#xff0…

那些web前端经典面试题大全及答案

阅读目录JavaScript部分JQurey部分HTML/CSS部分正则表达式开发及性能优化部分本篇收录了一些面试中经常会遇到的经典面试题以及自己面试过程中遇到的一些问题&#xff0c;并且都给出了我在网上收集的答案。马上就要过春节了&#xff0c;开年就是崭新的一年&#xff0c;相信很多…

ABP框架系列之三十七:(Navigation-导航)

Every web application has some menu to navigate between pages/screens. ASP.NET Boilerplate provides a common ifrastructure to create and show menu to users. 每个Web应用程序都有一些菜单在页面/屏幕之间导航。ASP.NET提供了一个通用的ifrastructure样板文件创建和显…

深度学习大概率用到的Pytorch内容基础

Pytorch 基础 文章目录(1) 基本数据类型1.Pytorch 用的比较多 tensor2.类型的检测3.不同维度的数据及用途(2) 创建Tensor1.从numpy中创建Tensor2.从list中创建Tensor3.设定默认type4.随机产生进行初始化5.采用特殊生成进行初始化(3) 索引与切片(4) 维度变换(1) 基本数据类型 1…

61. 搜索区间

给定一个包含 n 个整数的排序数组&#xff0c;找出给定目标值 target的起始和结束位置。 如果目标值不在数组中&#xff0c;则返回[-1, -1] 样例 给出[5, 7, 7, 8, 8, 10]和目标值target8, 返回[3, 4] 挑战 时间复杂度 O(log n) 应该要第一时间反应到二分&#xff0c;就算没有…

深度学习大概率用到的Pytorch内容进阶

Pytorch进阶 文章目录(1) 拼接与拆分(2) 基本运算(3) 统计属性 (4) 高阶操作(1) 拼接与拆分 torch.cat(tensors, dim0)torch.stack(tensors, dim0)注意区别 torch.cat 和 torch.stack&#xff0c;前者是不会产生新的维度的&#xff0c;后者是会产生新的维度&#xff0c;而且注…

outlook/foxmail发邮件退信

Microsoft Outlook向以下收件人或组传递邮件失败: liuyangxxxx.com 电子邮件系统在处理此邮件时遇到问题。该系统不会尝试重新传递此邮件。 供管理员使用的诊断信息: 生成服务器: EXCSE.xxxx.com liuyangxxxx.com #554 5.6.0 STOREDRV.Submit.Exception:PropertyTooBigExceptio…