android多线程与线程间通信

一、核心概念:为什么需要多线程?

1. 主线程(UI 线程)

  • 负责处理用户交互(点击、触摸)、绘制UI、更新界面。
  • 黄金法则:绝对不能在主线程执行耗时操作!
  • 如果主线程被阻塞超过 5秒,系统会抛出 Application Not Responding (ANR) 错误,导致应用崩溃。

2. 工作线程(后台线程)
用于执行所有可能阻塞主线程的任务,例如:

  • 网络请求(Retrofit, Volley)
  • 数据库操作(Room)
  • 读写文件
  • 解析大型数据(JSON, XML)
  • 复杂计算

二、Android线程间通信

Android 线程间通信是一个核心话题。由于 Android 的 UI 操作必须在主线程(UI线程)执行,而耗时工作必须在后台线程执行,因此线程间通信是必不可少的。

1.Handler & Looper & MessageQueue (最基础的底层机制)

这是 Android 线程间通信的基石,几乎所有其他高级方案都基于此。

  • Looper (循环器): 一个不断从 MessageQueue 中取出 Message 或 Runnable 的循环。线程默认没有 Looper,需要调用 Looper.prepare() 和 Looper.loop() 来创建和启动。主线程(UI线程)自带一个 Looper。
  • MessageQueue (消息队列): 一个单向链表结构的消息队列,用于存放 Handler 发送来的 Message。
  • Handler (处理器): 用于发送和处理 Message 或 Runnable 对象。每个 Handler 都会绑定到一个特定的线程(即该线程的 Looper)上。
    工作流程:
  • 工作线程使用 Handler 将 Message 或 Runnable 发送到主线程的 MessageQueue 中。
  • 主线程的 Looper 不断循环,从 MessageQueue 中取出这些消息。
  • 取出的消息最终会由主线程的 Handler 的 handleMessage() 方法处理,或者直接执行 Runnable 的 run() 方法。此时,代码就在主线程中执行了。

代码示例:

// 在主线程中创建Handler,并关联主线程的Looper
Handler mainHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        // 在这里处理消息,此方法运行在主线程
        if (msg.what == 1) { // 根据消息标识判断
            String data = (String) msg.obj; // 获取消息内容
            textView.setText(data); // 安全更新UI
        }
    }
};

// 在工作线程中发送消息
new Thread(() -> {
    // ... 执行耗时操作,比如网络请求
    String result = "Data from network";

    // 方式1:发送Message
    Message message = mainHandler.obtainMessage();
    message.what = 1; // 自定义的消息标识
    message.obj = result; // 要传递的数据
    mainHandler.sendMessage(message);

    // 方式2:更简单的,发送一个Runnable
    mainHandler.post(() -> {
        // 这个Runnable里的代码会在主线程执行
        textView.setText(result);
    });
}).start();

2.AsyncTask (已废弃,但需了解)

AsyncTask 内部封装了 Handler 和线程池,提供了 onPreExecute, doInBackground, onProgressUpdate, onPostExecute 等回调方法,使得“后台工作-更新进度-UI更新”的流程非常清晰。
由于其内存泄漏、生命周期管理等问题,官方已正式废弃它。但它的设计思想值得了解。

3.HandlerThread

一个自带 Looper 的 Thread 子类。它为你准备好了 Looper,你只需要为其创建 Handler 即可与之通信。
适用场景:需要一个长时间运行、且有自己消息循环的后台线程。

// 1. 创建并启动HandlerThread
HandlerThread handlerThread = new HandlerThread("MyBackgroundThread");
handlerThread.start();

// 2. 获取它的Looper,并创建与之绑定的Handler
Handler backgroundHandler = new Handler(handlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        // 这个handleMessage运行在handlerThread这个后台线程中!
        // 可以在这里执行耗时任务
        doHeavyWork();
    }
};

// 3. 在主线程向这个后台线程发送任务
backgroundHandler.post(() -> {
    // 这个Runnable会在HandlerThread中执行
});

// 4. 退出时记得释放资源
handlerThread.quitSafely();

4.LiveData (生命周期感知的数据持有者)

LiveData 是一种可观察的数据存储器类,具有生命周期感知能力。它通常和 ViewModel 一起使用。

工作原理:当 LiveData 所持有的数据发生变化时,它会通知处于活跃生命周期状态(如 STARTED, RESUMED)的观察者。
线程通信:LiveData 的 postValue() 方法可以在后台线程调用,它会自动将值派发到主线程,然后通知观察者。setValue() 必须在主线程调用。

// 在ViewModel中
public class MyViewModel extends ViewModel {
    private MutableLiveData<String> mData = new MutableLiveData<>();

    public LiveData<String> getData() {
        return mData;
    }

    public void fetchData() {
        new Thread(() -> {
            // 在后台线程工作
            String newData = expensiveOperation();
            // 使用postValue从后台线程更新LiveData
            mData.postValue(newData); // 自动切到主线程通知Observer
        }).start();
    }
}

// 在Activity/Fragment中观察
myViewModel.getData().observe(this, newData -> {
    // 这个Observer回调在主线程执行,可以安全更新UI
    textView.setText(newData);
});

5. Kotlin Coroutines (协程) + 调度器 (Dispatchers)

这是当前 最主流、最简洁 的异步处理和线程通信方案。它用看似同步的代码写出了异步操作。

核心是通过挂起函数(suspend) 和协程上下文(CoroutineContext) 中的调度器(Dispatchers) 来切换线程。

// 在ViewModel中
fun fetchData() {
    viewModelScope.launch { // 在主线程启动协程
        // 1. 在主线程更新UI:显示加载框
        loadingIndicator.value = true

        // 2. 切换到IO线程执行耗时操作(不会阻塞主线程)
        val result = withContext(Dispatchers.IO) {
            // 这个代码块在IO线程执行
            networkRequest() // 模拟网络请求
        }
        // 3. withContext结束后,自动切回原来的线程(主线程)

        // 4. 在主线程更新UI
        textView.text = result
        loadingIndicator.value = false
    }
}

Dispatchers 的类型:

  • Dispatchers.Main: 用于更新UI。
  • Dispatchers.IO: 用于网络、磁盘操作。
  • Dispatchers.Default: 用于CPU密集型计算。

6.RxJava (响应式扩展)

一个基于事件流的异步编程库,功能极其强大,但学习曲线较陡。它通过 observeOn 和 subscribeOn 操作符来精确控制线程切换。

Observable.fromCallable(() -> {
    // 在IO线程执行耗时任务 (因为subscribeOn)
    return doHeavyWorkInBackground();
})
.subscribeOn(Schedulers.io()) // 指定上游执行的线程
.observeOn(AndroidSchedulers.mainThread()) // 指定下游观察者的线程
.subscribe(result -> {
    // 在主线程接收结果,更新UI
    textView.setText(result);
});

三、基础java方案

7.Thread 和 Runnable

最基础的创建线程的方式。

// 方式一:实现 Runnable 接口
Thread thread = new Thread(new Runnable() {
    @Override
    public void run() {
        // 在后台线程执行耗时任务
        doHeavyWork();
        
        // 不能在这里直接更新UI!
        // runOnUiThread(() -> textView.setText("Done")); // 需要特殊方法
    }
});
thread.start();

// 方式二:使用Lambda表达式(Java 8+)
new Thread(() -> {
    // 后台工作
}).start();

缺点:难以管理、复用;需要自己处理线程间通信。

8.ExecutorService(线程池)

管理和复用线程的最佳Java实践。避免了频繁创建和销毁线程的开销。

// 1. 创建固定大小的线程池(推荐)
ExecutorService executor = Executors.newFixedThreadPool(4);

// 2. 提交任务(Runnable 或 Callable)
executor.execute(new Runnable() {
    @Override
    public void run() {
        doHeavyWork();
    }
});

// 3. 如果需要获取后台任务的结果,使用 Callable 和 Future
Future<String> future = executor.submit(new Callable<String>() {
    @Override
    public String call() throws Exception {
        doHeavyWork();
        return "Result";
    }
});

// 在需要结果的时候获取(会阻塞当前线程)
try {
    String result = future.get();
    runOnUiThread(() -> updateUI(result));
} catch (Exception e) {
    e.printStackTrace();
}

// 4. 关闭线程池(通常在应用退出时)
executor.shutdown();

四、总结与对比

机制特点适用场景现代性
Handler/Looper底层基础,灵活但稍显繁琐需要精细控制消息、自定义后台循环传统,但必不可少
HandlerThread自带Looper的Thread需要一个常驻的、有消息循环的后台线程传统
LiveData生命周期感知,与ViewModel搭配基于生命周期的UI数据更新现代 (推荐)
Kotlin Coroutines代码简洁,结构化并发,易于管理几乎所有异步场景的首选最现代 (强烈推荐)
RxJava功能强大,流操作符丰富复杂的异步事件流处理现代,但复杂度高

最佳实践建议:
对于新项目:毫不犹豫地选择 Kotlin 协程 + LiveData/StateFlow。这是 Google 官方主推的现代方案。
理解底层:理解 Handler/Looper 机制有助于你更好地理解其他高级框架的原理。
避免使用:AsyncTask 和直接使用裸 Thread 进行复杂通信,因为它们难以维护且容易出错。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值