59. 【Android教程】多线程

简介: 59. 【Android教程】多线程

多线程可以让你同时异步执行多种任务,是各种编程语言里很重要的一个概念。合理的采用多线程可以让你的 App 拥有更好的运行性能,但是如果使用不当可能会让你的程序非常混乱,出现很多令人费解且难以定位的问题。

1. 多线程初探

当用户打开一个 App 时,Android 系统会创建一个 Linux 进程,同时在进程中创建一个执行线程,我们称之为“主线程”,因为 Andfoid 规定只能在主线程更新 UI,所以又叫“UI线程”。

系统在创建主线程的时候帮我们创建好了一套消息处理机制,包含了第 38 节提到的 Handler、Looper、MessageQueue 等模块,主线程就利用这一套消息机制来实现 Actvity、Fragment 的生命周期回调以及和其他 App 之间的通信。所有需要在 UI 线程执行的任务都要首先被 push 到任务队列中,然后等待主线程 Looper 来轮询。如果我们将所有的任务都放到主线程的任务队列,那么可能需要等很久才能执行到,所以一个比较好的选择就是将耗时任务单独放到一个子线程中,这样就可以独享一个 MessageQuene,并且不再占用主线程的资源。

2. 多线程注意事项

  • Android 规定刷新 UI 的操作必须在主线程执行;
  • 网络请求、数据库或者文件 I / O 等都属于耗时操作,非常容易导致主线程的阻塞造成 App 卡顿;
  • 由于主线程的 Looper 是按顺序轮询 MessageQueue 的,所以主线程的所有任务都是同步执行。这样如果有耗时操作那么会阻塞主线程,后面的任务都需要等待耗时操作的执行;
  • 除了 I / O 操作外,开发人员需要自行评估任务的耗时情况,合理采用多线程避免主线程的阻塞;
  • Android 提供了多种创建和管理线程的方法,当然如果有高并发的场景还有一些第三方库可以使用,但是系统的线程、线程池可以应对大部分常见场景。
  • 接下来我们来看看具体怎么使用 Android 多线程。

3. 线程的使用方法

Java 虚拟机支持多线程并发编程,并发意味着同时执行多个任务。在 Android 中常见的多线程常见就是在子线程执行耗时操作,然后将结果通过线程间通信传递给主线程,主线程仅仅拿到结果进行 UI 的刷新。

3.1 线程的创建

我们有两种方式进行线程的创建

  1. 继承Thread实现一个线程类:
class TestThread extends Thread {
    @Override
    public void run() {
        Log.d("Threading", "继承 Thread 的线程:"+Thread.currentThread().getName());
    }
}
  1. 实现Runnable接口
class TestRunnable implements Runnable {
    @Override
    public void run() {
        Log.d("Runable", "实现 Runable 的线程>"+Thread.currentThread().getName());
    }
}

无论是哪种方式,都需要在类中实现一个无参的run()方法,然后将线程的实际执行任务放在run()方法中,在要用多线程的类中需要创建出一个Runnable接口实例。

3.2 启动进程

对于第一种创建方式,直接创建 TestThread 实例调用start()即可:

new TestThread().start()

而对于第二种方式,在创建 Thread 的同时传入 Runnable 接口实例,然后调用start()

new Thread(new TestRunnable()).start()

调用了 Thread 对象的 start()之后,run()方法就会在我们的子线程中执行了。

3.3 线程生命周期

和 Activity 一样,Thread 在执行过程中也有自己的生命周期,一共有 5 种状态:


  • **New:**刚创建好,还未执行
  • **Runnable:**已经调用了start(),等待 CPU 分配时间片
  • **Running:**正在运行
  • **Blocked:**由于某些原因(等待、睡眠、CPU暂时回收资源等)线程进入阻塞
  • **Dead:**线程任务执行结束,或者主动关闭
  • 各个生命周期的切换如下图:

4. 多线程示例

本节创建两个耗时子线程,在线程的开始和结束分别打上日志,然后观察两个线程任务是同时执行,还是需要等待其中一个线程的耗时任务执行结束才能执行第二个。

MainActivity 代码很简单,在里面创建两个线程,为了方便演示我们用“继承自 Thread”和“实现 Runnable”两种方式来创建两个线程:

 
package com.emercy.myapplication;
 
import android.app.Activity;
import android.content.pm.PackageManager;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
 
import androidx.annotation.Nullable;
 
import java.io.IOException;
import java.util.Random;
 
import static android.Manifest.permission.RECORD_AUDIO;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
 
public class MainActivity extends Activity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
 
        new Thread() {
            @Override
            public void run() {
                Log.d("ThreadTest", "Thread1 start");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d("ThreadTest", "Thread1 end");
 
            }
        }.start();
 
        new Thread(new Runnable() {
            @Override
            public void run() {
                Log.d("ThreadTest", "Thread2 start");
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Log.d("ThreadTest", "Thread2 end");
            }
        }).start();
    }
 
    private void task() {
        for (int i = 0; i < 10; i++) {
            Log.d("Thread", Thread.currentThread().getName() + " 当前i = " + i);
        }
    }
}

在两个线程中通过sleep()来模拟 500 毫秒的耗时任务,在任务的开始和结束都打上日志,观察结果如下:

可以看到首先会同时开启两个子线程,然后分别同时执行 500 毫秒的任务,在执行结束打上结束的 Log,可以证明两个 Thread 是同时执行的。

5. 小结

本节学习了一个能让你的 App 并发高效执行任务的方式,多线程可以帮助你提升 App 的整体性能,但用之不当可能会造成一定的资源浪费,所以一定要谨记本节所提到的注意事项。然后按照步骤去创建、运行子线程,了解线程执行的生命周期,让程序更好的为用户服务。

相关文章
|
16天前
|
Java 调度 Android开发
安卓与iOS开发中的线程管理差异解析
在移动应用开发的广阔天地中,安卓和iOS两大平台各自拥有独特的魅力。如同东西方文化的差异,它们在处理多线程任务时也展现出不同的哲学。本文将带你穿梭于这两个平台之间,比较它们在线程管理上的核心理念、实现方式及性能考量,助你成为跨平台的编程高手。
|
20天前
|
API Android开发 iOS开发
深入探索Android与iOS的多线程编程差异
在移动应用开发领域,多线程编程是提高应用性能和响应性的关键。本文将对比分析Android和iOS两大平台在多线程处理上的不同实现机制,探讨它们各自的优势与局限性,并通过实例展示如何在这两个平台上进行有效的多线程编程。通过深入了解这些差异,开发者可以更好地选择适合自己项目需求的技术和策略,从而优化应用的性能和用户体验。
|
26天前
|
Android开发 数据安全/隐私保护 虚拟化
安卓手机远程连接登录Windows服务器教程
安卓手机远程连接登录Windows服务器教程
55 4
|
1月前
|
Android开发
布谷语音软件开发:android端语音软件搭建开发教程
语音软件搭建android端语音软件开发教程!
|
2月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
61 4
|
3月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
147 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
3月前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
118 5
|
3月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
93 10
|
3月前
|
API Android开发 iOS开发
安卓与iOS开发中的线程管理对比
【9月更文挑战第12天】在移动应用的世界中,安卓和iOS平台各自拥有庞大的用户群体。开发者们在这两个平台上构建应用时,线程管理是他们必须面对的关键挑战之一。本文将深入探讨两大平台在线程管理方面的异同,通过直观的代码示例,揭示它们各自的设计理念和实现方式,帮助读者更好地理解如何在安卓与iOS开发中高效地处理多线程任务。
|
3月前
|
Java Android开发 开发者
安卓应用开发中的线程管理优化技巧
【9月更文挑战第10天】在安卓开发的海洋里,线程管理犹如航行的风帆,掌握好它,能让应用乘风破浪,反之则可能遭遇性能的暗礁。本文将通过浅显易懂的语言和生动的比喻,带你探索如何优雅地处理安卓中的线程问题,从基础的线程创建到高级的线程池运用,让你的应用运行更加流畅。