Handler系列三:线程切换原理

194 阅读3分钟

Handler不是直接用来实现线程切换的, 而是用于在A线程调用B线程的实例,去处理消息或执行Runnable对象。间接完成切换。

Handler从线程A切换到线程B的底层原理

0. 线程切换的实现

  • 实际的线程切换: 首先清楚:Handler是与创建它的线程的Looper绑定的,而不是与调用它的线程的Looper绑定。 在线程A中,通过调用线程B的Handler实例来发送消息(sendMessage)或执行Runnable任务(post)。 这些消息或任务被添加到线程B的MessageQueue中等待以及后续处理。

1. 准备工作

  • 线程B的准备:在线程B中,首先需要调用Looper.prepare()方法来为该线程初始化Looper和MessageQueue。这一步是必需的,因为Looper和MessageQueue是处理消息和执行Runnable的基础设施。
  • Handler的创建:在线程B中创建Handler实例,这个Handler会与线程B的Looper和MessageQueue绑定。
class ThreadB extends Thread {  
    private Handler handler;  
  
    @Override  
    public void run() {  
        Looper.prepare(); // 初始化Looper  
        handler = new Handler() {  
            @Override  
            public void handleMessage(Message msg) {  
                // 在这里处理来自线程A的消息  
                super.handleMessage(msg);  
                // 例如,更新UI(但通常UI更新应在主线程进行,这里仅为示例)  
                Log.d("ThreadB", "Handling message in ThreadB");  
            }  
        };  
  
        Looper.loop(); // 启动Looper循环  
    }  
  
    // 提供Handler的访问方式  
    public Handler getHandler() {  
        return handler;  
    }  
}

2. 消息发送

  • 从线程A发送消息:在线程A中,通过线程B的Handler实例来发送消息(Message)或执行Runnable对象。 这可以通过调用Handler的sendMessage(Message msg)post(Runnable r)等方法来实现。

public class MainActivity extends AppCompatActivity {    
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
  
        // 启动线程B  
        ThreadB threadB = new ThreadB();  
        threadB.start();  
  
        // 发送消息到线程B  
        Message msg = Message.obtain();  
        // 假设threadBHandler是线程B的Handler实例  
        // threadBHandler.sendMessage(msg); // 这里我们省略了直接获取handler的步骤  
  
        // 由于直接获取handler的示例在真实场景中不常见,我们改用Runnable来演示  
        Runnable runnable = new Runnable() {  
            @Override  
            public void run() {  
                // 这里是将在线程B中执行的代码  
                Log.d("Runnable", "Running in ThreadB");  
            }  
        };  
  
        // 假设我们有一个方法来获取Handler(这里我们直接调用,但实际上你需要从ThreadB中获取)  
        // Handler threadBHandler = getThreadBHandlerSomehow();  
        // threadBHandler.post(runnable);  
        // 在实际应用中,你需要在ThreadB完全启动并Handler可用后,才能发送消息或Runnable  
    }  
  
    // 注意:这里并没有真正的方法getThreadBHandlerSomehow(),你需要自己实现同步机制来获取Handler  
}

3. 消息处理

  • 线程B的消息循环: 在线程B中,Looper的loop()方法会不断地从MessageQueue中取出消息并分发给相应的Handler进行处理。 由于线程B的Handler与线程B的Looper和MessageQueue绑定,因此当线程A发送的消息被取出时,它会在线程B中被处理。

注意事项

  • Handler的线程绑定:Handler是与创建它的线程的Looper绑定的,而不是与调用它的线程的Looper绑定。因此,要确保从正确的线程中获取Handler实例。
  • 线程间的通信:Handler机制实际上是一种线程间的通信方式,它允许我们在一个线程中发送消息或任务,并在另一个线程中接收并处理这些消息或任务。
  • 内存泄漏:在使用Handler时,要注意避免内存泄漏的问题。特别是在非主线程中创建的Handler,如果其持有的引用(如Activity的引用)没有在适当的时候被清除,就可能导致内存泄漏。

通过上述原理,我们可以理解Handler是如何间接地实现线程切换的。然而,需要明确的是,Handler本身并不直接负责线程的切换,而是通过与Looper和MessageQueue的协作,在目标线程中处理来自其他线程的消息或任务。