Android线程之间通信

本文介绍了三种Android平台上的线程间通信方式:使用管道流Pipes实现简单的双向通信;通过共享内存并结合synchronized关键字或BlockingQueue来同步访问;利用Handler和Message机制更新UI线程。


转载自:http://www.cnblogs.com/xuling/archive/2011/06/06/android.html

1. 使用管道流Pipes

“管道”是java.io包的一部分。它是Java的特性,而不是Android特有的。一条“管道”为两个线程建立一个单向的通道。生产者负责写数据,消费者负责读取数据。

下面是一个使用管道流进行通信的例子。

 

public class PipeExampleActivity extends Activity {

    private static final String TAG = "PipeExampleActivity";
    private EditText editText;

    PipedReader r;
    PipedWriter w;

    private Thread workerThread;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        r = new PipedReader();
        w = new PipedWriter();

        try {
            w.connect(r);
        } catch (IOException e) {
            e.printStackTrace();
        }

        setContentView(R.layout.activity_pipe);
        editText = (EditText) findViewById(R.id.edit_text);
        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
                try {
                    if(count > before) {
                        w.write(charSequence.subSequence(before, count).toString());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

            @Override
            public void afterTextChanged(Editable editable) {
            }
        });

        workerThread = new Thread(new TextHandlerTask(r));
        workerThread.start();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        workerThread.interrupt();
        try {
            r.close();
            w.close();
        } catch (IOException e) {
        }
    }

    private static class TextHandlerTask implements Runnable {
        private final PipedReader reader;

        public TextHandlerTask(PipedReader reader){
            this.reader = reader;
        }
        @Override
        public void run() {
            while(!Thread.currentThread().isInterrupted()){
                try {
                    int i;
                    while((i = reader.read()) != -1){
                        char c = (char) i;
                        
                        Log.d(TAG, "char = " + c);
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在这个例子中,对EditText设置一个TextWatcher监听,一旦EditText的内容发生改变,就向“管道”中输入字符,它就是所谓的生产者。同时,有一个工作线程负责从管道中读取字符,它就是所谓的消费者。这样,就实现了UI线程和工作线程之间的数据通信。

 

 

2. 共享内存

多个线程共享同一份内存,就是说,一个变量可以同时被多个线程所访问。这里要特别注意同步和原子操作的问题。

Java中最基本的同步例子。

 

synchronized(this) {
    while(isConditionFullfilled == false) {
        wait();
    }


    notify();
}
如果觉得使用wait/notify比较麻烦,可以使用Java提供的BlockingQueue,从名字就可以看出它是一个阻塞队列。看下面的例子。

 

 

public class ConsumerProducer {
    private final int LIMIT = 10;
    private BlockingQueue<Integer> blockingQueue = new LinkedBlockingQueue<Integer>(LIMIT);
    
    public void produce() throws InterruptedException {
        int value = 0;
        while (true) {
            blockingQueue.put(value++);
        }
    }
    
    public void consume() throws InterruptedException {
        while (true) {
            int value = blockingQueue.take();
        }
    }

}

3. 使用Hander和Message

 

Handler的机制网上有很多教程,对其不太清楚的同学可以Google一下。

我做一下简略的总结。

一个线程对应一个Looper,一个Looper持有一个MessageQueue,一个Looper可以与多个Handler绑定,一个MessageQueue中可以包含多个Message。

下面是一个使用Handler的例子。

 

public class HandlerExampleActivity extends Activity {

    private final static int SHOW_PROGRESS_BAR = 1;
    private final static int HIDE_PROGRESS_BAR = 0;
    private BackgroundThread mBackgroundThread;

    private TextView mText;
    private Button mButton;
    private ProgressBar mProgressBar;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_example);

        mBackgroundThread = new BackgroundThread();
        mBackgroundThread.start();

        mText = (TextView) findViewById(R.id.text);
        mProgressBar = (ProgressBar) findViewById(R.id.progress);
        mButton = (Button) findViewById(R.id.button);
        mButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mBackgroundThread.doWork();
            }
        });
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mBackgroundThread.exit();
    }

    private final Handler mUiHandler = new Handler() {
        public void handleMessage(Message msg) {

            switch(msg.what) {
                case SHOW_PROGRESS_BAR:
                    mProgressBar.setVisibility(View.VISIBLE);
                    break;
                case HIDE_PROGRESS_BAR:
                    mText.setText(String.valueOf(msg.arg1));
                    mProgressBar.setVisibility(View.INVISIBLE);
                    break;
            }
        }
    };

    private class BackgroundThread extends Thread {

        private Handler mBackgroundHandler;

        public void run() {
            Looper.prepare();
            mBackgroundHandler = new Handler();
            Looper.loop();
        }

        public void doWork() {
            mBackgroundHandler.post(new Runnable() {
                @Override
                public void run() {
                    Message uiMsg = mUiHandler.obtainMessage(SHOW_PROGRESS_BAR, 0,
                            0, null);
                    mUiHandler.sendMessage(uiMsg);

                    Random r = new Random();
                    int randomInt = r.nextInt(5000);
                    SystemClock.sleep(randomInt);

                    uiMsg = mUiHandler.obtainMessage(HIDE_PROGRESS_BAR, randomInt,
                            0, null);
                    mUiHandler.sendMessage(uiMsg);
                }
            });
        }

        public void exit() {
            mBackgroundHandler.getLooper().quit();
        }
    }
}

<think>我们正在讨论Android系统中的多线程通信方法。根据用户提供的引用,特别是引用[1][2][3][4][5],我们可以总结出Android平台上多线程通信的核心机制和常用方法。需要注意的是,由于Android的UI操作必须在主线程(也称为UI线程)中执行,因此多线程通信Android开发中尤为重要,以避免阻塞UI线程导致应用不响应(ANR)等问题。 下面我将系统性地介绍Android线程通信的几种主要方法,并尽量结合引用中的信息。 ### 一、Handler + Looper + MessageQueue 机制 这是Android中最经典的多线程通信方式,也是Android系统本身提供的消息传递机制的核心组件。 - **工作原理**:每个线程可以拥有一个`Looper`(消息循环),它不断地从自己的`MessageQueue`(消息队列)中取出`Message`(消息)并分发给对应的`Handler`(处理器)来处理。`Handler`负责发送和处理消息,通常绑定到创建它的线程的`Looper`上。 - **使用场景**:常用于子线程与主线程之间通信(例如,在子线程中执行耗时任务,然后通过`Handler`将结果发送到主线程更新UI)。 - **代码示例**(参考引用[2]): ```kotlin // 在主线程中创建Handler(因为主线程默认有Looper) val mainHandler = Handler(Looper.getMainLooper()) // 在子线程中发送消息(例如更新UI) thread { // 执行耗时操作... mainHandler.post { // 这段代码会在主线程执行 textView.text = "更新UI" } } ``` - **注意事项**:如果要在子线程中使用`Handler`,需要先为该子线程创建`Looper`(通过调用`Looper.prepare()`和`Looper.loop()`)。另外,使用`Handler`时要注意避免内存泄漏(例如,非静态内部类Handler隐式持有外部Activity的引用,导致Activity无法被回收)。推荐使用静态内部类+弱引用的方式,或者在Activity的`onDestroy()`中清除消息[^1][^4][^5]。 ### 二、AsyncTask(已弃用,但历史代码中常见) `AsyncTask`是Android提供的一个轻量级异步任务类,它封装了`Handler`和线程池,简化了在后台执行任务并更新UI的过程。 - **工作原理**:通过重写`doInBackground()`方法执行后台任务,在`onPostExecute()`方法中更新UI(该方法在主线程执行)。 - **示例**(参考引用[3]): ```java // 注意:在较新的Android版本中,AsyncTask已被弃用,这里仅作示例 private class MyTask extends AsyncTask<Params, Progress, Result> { @Override protected Result doInBackground(Params... params) { // 在子线程执行,返回结果 return result; } @Override protected void onPostExecute(Result result) { // 在主线程执行,更新UI } } // 执行任务 new MyTask().execute(params); ``` - **缺点**:`AsyncTask`在Android 11(API 30)后被标记为弃用。主要问题包括:容易导致内存泄漏、配置变更(如屏幕旋转)时任务管理复杂、不同Android版本线程池实现不一致等[^3][^5]。 ### 三、Executor框架与FutureTask Java的`Executor`框架(以及Android中的实现)提供了更灵活和强大的线程池管理机制。`FutureTask`可以用于获取异步任务的结果。 - **使用方法**(参考引用[3]): ```kotlin val executor: ExecutorService = Executors.newFixedThreadPool(1) val futureTask = FutureTask<Int>(object : Callable<Int> { override fun call(): Int { // 执行耗时操作 return 42 } }) executor.execute(futureTask) // 在需要结果的地方(注意:get()会阻塞当前线程) val result = futureTask.get() ``` - **适用场景**:适用于需要获取后台任务执行结果的情况,但要注意`get()`方法的阻塞特性,避免在主线程调用。 ### 四、基于回调的机制 这是一种更通用的设计模式,通过接口回调实现线程通信。 - **原理**:在后台任务执行完成后,通过回调接口将结果传递回调用方(通常是主线程)。 - **示例**: ```kotlin interface Callback { fun onResult(result: String) } // 在异步任务中 fun doAsync(callback: Callback) { thread { val result = doWork() // 切换到主线程执行回调(通过Handler或runOnUiThread) mainHandler.post { callback.onResult(result) } } } ``` ### 五、LiveData(推荐在MVVM架构中使用) `LiveData`是Android Architecture Components中的组件,它基于观察者模式,并且具有生命周期感知能力,可以确保UI更新只在Activity/Fragment处于活跃状态时进行。 - **特点**: - 数据变化时自动通知观察者(在UI线程)。 - 避免内存泄漏:当生命周期处于非活跃状态时不会接收更新,并在销毁时自动移除观察者。 - **适用场景**:在ViewModel中暴露LiveData,Activity/Fragment观察这些LiveData并更新UI。后台任务(如Repository层)更新LiveData的值(通常通过`postValue`方法在后台线程设置值,然后自动切换到主线程通知观察者)。 ### 六、Kotlin协程(现代Android开发的推荐方式) Kotlin协程提供了一种更简洁、可读性更高的异步编程方式,它通过挂起函数(suspend function)避免了回调地狱,并可以轻松切换线程。 - **核心优势**:使用同步代码风格写异步逻辑,结构化并发(减少内存泄漏风险)。 - **线程切换**:通过`withContext(Dispatchers.IO)`切换到IO线程执行耗时操作,返回主线程更新UI。 - **示例**: ```kotlin // 在ViewModel或Activity中使用 viewModelScope.launch(Dispatchers.Main) { // 在主线程启动协程 val result = withContext(Dispatchers.IO) { // 在IO线程执行耗时操作 fetchFromNetwork() } // 自动切换回主线程 updateUI(result) } ``` ### 总结与选择建议 - **经典方法**:`Handler`机制是基础,但在现代开发中通常被更高层次的抽象所封装。 - **现代推荐**: - 对于简单的异步任务:使用Kotlin协程。 - 在MVVM架构中:结合ViewModel和LiveData(或StateFlow)进行通信。 - **避免使用**:`AsyncTask`(已弃用)和裸线程(Thread)直接操作UI。 - **线程安全**:无论使用哪种通信方式,当多个线程访问共享数据时,务必使用同步机制(如`synchronized`、`Mutex`或并发集合)。 §§相关问题§§ 1. Kotlin协程如何简化Android线程通信? 2. 如何避免Handler导致的内存泄漏? 3. LiveData与RxJava在Android线程通信中的优缺点比较? </think>### Android线程通信的核心方法 根据引用资料[1][2][3][4][5],Android系统提供了多种线程通信机制,主要解决UI线程与工作线程的协作问题。以下是五种核心方法及其实现原理: #### 1. **Handler + Looper + MessageQueue(经典机制)** [^1][^2][^4] ```kotlin // 主线程创建Handler val mainHandler = Handler(Looper.getMainLooper()) // 子线程发送消息 thread { val msg = Message.obtain().apply { what = 1 obj = "数据" } mainHandler.sendMessage(msg) // 发送到主线程消息队列 } // 主线程处理消息 mainHandler = object : Handler(Looper.getMainLooper()) { override fun handleMessage(msg: Message) { when(msg.what) { 1 -> textView.text = msg.obj.toString() // UI更新 } } } ``` - **原理**:基于生产者-消费者模型 - `Looper`循环从`MessageQueue`取消息 - `Handler`作为消息处理器绑定特定线程 - 子线程通过`sendMessage()`或`post(Runnable)`发送消息 - **特点**:精准控制消息类型和执行顺序,但需注意内存泄漏风险[^5] #### 2. **Executor框架 + FutureTask(异步结果获取)** [^3] ```kotlin val executor = Executors.newSingleThreadExecutor() val future = executor.submit(Callable<String> { // 子线程执行耗时操作 return@Callable "处理结果" }) // 获取结果(可设置超时) try { val result = future.get(2, TimeUnit.SECONDS) runOnUiThread { textView.text = result } // 更新UI } catch (e: TimeoutException) { // 超时处理 } ``` - **优势**: - 通过`Future.get()`阻塞获取结果 - 线程池管理避免频繁创建线程 - 支持超时控制和任务取消 #### 3. **AsyncTask(简化版后台任务)** [^3][^5] ```java private class MyTask extends AsyncTask<URL, Integer, String> { @Override protected String doInBackground(URL... urls) { // 后台线程执行(非UI线程) return downloadData(urls[0]); } @Override protected void onPostExecute(String result) { // 自动切换回UI线程 textView.setText(result); } } // 启动任务 new MyTask().execute(url); ``` - **生命周期**: $$ \text{onPreExecute()} \rightarrow \text{doInBackground()} \rightarrow \text{onPostExecute()} $$ - **注意**:Android 11后已弃用,推荐替代方案[^5] #### 4. **子线程通信(自定义Looper)** [^4] ```kotlin class WorkerThread : Thread() { lateinit var handler: Handler override fun run() { Looper.prepare() // 初始化Looper handler = object : Handler(Looper.myLooper()!!) { override fun handleMessage(msg: Message) { // 处理其他线程发来的消息 } } Looper.loop() // 启动消息循环 } } // 其他线程发送消息 workerThread.handler.sendMessage(msg) ``` - **关键步骤**: 1. 调用`Looper.prepare()`初始化 2. 创建绑定当前Looper的Handler 3. 启动`Looper.loop()`消息循环 #### 5. **LiveData / Flow(架构组件推荐)** [^1] ```kotlin // ViewModel中 private val _data = MutableLiveData<String>() val data: LiveData<String> = _data fun fetchData() { viewModelScope.launch(Dispatchers.IO) { val result = repo.loadData() // IO线程 _data.postValue(result) // 自动切主线程 } } // Activity中观察 viewModel.data.observe(this) { value -> textView.text = value // UI更新 } ``` - **优势**: - 生命周期感知避免内存泄漏 - `postValue()`自动线程切换 - 支持数据持久化(SavedStateHandle) ### 方法对比与选择建议 | **方法** | **适用场景** | **线程切换方式** | **维护性** | |--------------------|----------------------------------|------------------------|------------| | Handler/Looper | 精准控制消息类型和顺序 | 手动指定Handler | ★★☆☆☆ | | Executor+Future | 需要获取异步任务结果 | Future.get()阻塞获取 | ★★★★☆ | | AsyncTask | 简单后台任务(已弃用) | 自动切换 | ★☆☆☆☆ | | 自定义Looper | 工作线程通信 | 绑定自定义Looper | ★★☆☆☆ | | LiveData/Flow | MVVM架构数据驱动UI | postValue自动切换 | ★★★★★ | > **最佳实践**:现代Android开发推荐组合使用`ViewModel + LiveData/Flow + 协程`[^1][^3],既保证线程安全又避免回调地狱。对于跨进程通信,需结合Binder或Messenger实现。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值