更新UI的四种方式分析

在Android开发中,更新UI有以下四种常用方式。

方式一:

Handler类的sendEmptyMessage()或sendMessage()方法。

private Handler mHandler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
	if (msg.what == MSG_REFRESH) {
	    mTextView.setText("blog.csdn.net/ruancoder");
	}
    }
};

private void refresh1() {
    mHandler.sendEmptyMessage(MSG_REFRESH);
}

方式二:
Handler类的post()方法。

private void refresh2() {
    mHandler.post(new Runnable() {
        @Override
        public void run() {
            mTextView.setText("blog.csdn.net/ruancoder");
        }
    });
}

方式三:
View类的post()方法。

private void refresh3() {
    mTextView.post(new Runnable() {
        @Override
        public void run() {
            mTextView.setText("blog.csdn.net/ruancoder");
        }
    });
}

方式四:
Activity类的runOnUiThread()方法。

private void refresh4() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mTextView.setText("blog.csdn.net/ruancoder");
        }
    });
}

先来看一下方式一sendEmptyMessage()的实现。

public final boolean sendEmptyMessage(int what)
{
    return sendEmptyMessageDelayed(what, 0);
}

 再来看一下方式二Handler类的post()方法。 

public final boolean post(Runnable r)
{
    return  sendMessageDelayed(getPostMessage(r), 0);
}

我们会发现,这两种方式内部都是调用了sendEmptyMessageDelayed()方法,即都是采用Handler类发送消息的方式。
 
接着看方式三View类的post()方法。
public boolean post(Runnable action) {
    final AttachInfo attachInfo = mAttachInfo;
    if (attachInfo != null) {
        return attachInfo.mHandler.post(action);
    }
    // Assume that post will succeed later
    ViewRootImpl.getRunQueue().post(action);
    return true;
}
其内部调用的是Handler类的post()方法,即上述方式二。


再看Activity类的runOnUiThread()方法。

public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}
其内部调用的是也是Handler类的post()方法,即上述方式二。

可以看到,上述四种方式都会执行到sendEmptyMessageDelayed()方法。

也就是说,即使采用后三种方式,最终都会执行第一种方式的实现。即Handler发送消息,然后在Handler类的handlerMessage()中接收消息。


我们接着sendEmptyMessageDelayed()方法继续往下走。

public final boolean sendMessageDelayed(Message msg, long delayMillis)
{
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

可以看到,最终会执行到下面两行代码。
msg.target = this;

mQueue.enqueueMessage(msg, uptimeMillis);
(1).将当前发送msg的Handler对象,赋值给msg的target属性。
(2).将msg添加到消息队列mQueue。


此时,在Handler类中,我们已经无法进行接下来的执行流程了。那么最终消息是如何到达Handler类的handleMessage(Message msg)方法的呢?
下面我们来进行分析。

在Handler机制中涉及到四个重要的类,Handler、Looper、MessageQueue、Message。

Handler类中定义了成员变量mQueue和mLooper,分别是MessageQueue和Looper类型对象。

Handler类的成员变量:

public class Handler {

    // other code

    final MessageQueue mQueue;
    final Looper mLooper;

    // other code
}

Handler类的构造方法:

public Handler(Callback callback, boolean async) {

    // other code

    mLooper = Looper.myLooper();
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;

    // other code

}

在构造方法中,Looper.myLooper()从当前线程中获取绑定的Looper对象,赋值给成员变量mLooper,再从mLooper对象中获取到mQueue对象。

Looper类的mQueue对象是何时创建的呢?
看一下Looper类的构造方法。

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}
当Looper对象创建时,就会创建一个MessageQueue类型对象mQueue。

接下来,我们将要关心的就是,Looper对象是在哪里创建的。

这时需要进入到ActivityThread类。
(注:ActivityThread.java文件的路径:\platform_frameworks_base\core\java\android\app\ActivityThread.java)

ActivityThread管理应用程序的主线程的执行。 主线程之所以可以使用Handler进行通信,是因为主线程中已经创建了Looper。如果子线程需要使用Handler通信,需要自己去创建Looper。

来看一下ActivityThread类的main()方法。
public static void main(String[] args) {

    // other code

    Looper.prepareMainLooper();

    Looper.loop();

    // other code
}

在main()方法中,调用了Looper.prepareMainLooper()和Looper.loop()两个方法。


(1).Looper.prepareMainLooper()方法

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

private static void prepare(boolean quitAllowed) {
   if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
   }
   sThreadLocal.set(new Looper(quitAllowed));
}

在prepareMainLooper()方法中调用prepare()新建了一个looper对象,并通过threadLocal.set()方法与当前线程进行了绑定。

这里再回到Handler类的构造方法,在构造方法中是通过mLooper = Looper.myLooper()获取Looper对象。
进入Looper.myLooper()方法:
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
该方法返回的就是上面sThreadLocal通过set()添加的Looper对象。


(2).Looper.loop()方法

public static void loop() {
    final Looper me = myLooper();
    final MessageQueue queue = me.mQueue;

    // other code

    for (;;) {
        Message msg = queue.next();

        msg.target.dispatchMessage(msg);

	// other code
    }

    // other code
}

在Looper.loop()方法中,循环从MessageQueue中获取Message对象,调用msg.target.dispatchMessage(msg)进行处理,即将msg交给发送它的Handler对象的dispatchMessage()方法。

兜了一大圈,我们又再次回到Handler类。

在Handler类的dispatchMessage()方法中,我们终于看到了熟悉的handleMessage(Message msg)方法。

/**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

/**
 * Subclasses must implement this to receive messages.
 */
public void handleMessage(Message msg) {
}

到这里,我们已经明白了,通过Handler类的的sendMessage(msg)发送的消息,最终是如何执行到handleMessage(Message msg)方法中。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Winform 中,UI 控件只能在主线程中更新。如果在非主线程中更新 UI 控件,会抛出 "跨线程访问无效: 从不是创建控件的线程访问它" 的异常。因此,我们需要使用以下方式在分线程中更新 UI 控件: 1. 使用 Control.Invoke 方法 使用 Control.Invoke 方法可以将代码添加到 UI 线程的消息队列中执行。这样可以保证在 UI 线程中更新控件,避免跨线程访问异常。 示例代码: ``` private void button1_Click(object sender, EventArgs e) { Thread t1 = new Thread(new ThreadStart(UpdateTextBox)); t1.Start(); } private void UpdateTextBox() { if (textBox1.InvokeRequired) { textBox1.Invoke(new MethodInvoker(delegate { textBox1.Text = "Hello World"; })); } else { textBox1.Text = "Hello World"; } } ``` 2. 使用 BackgroundWorker 组件 BackgroundWorker 组件是一个用于执行异步操作的组件,在操作完成后可以安全地更新 UI 控件。它具有一个 ProgressChanged 事件和一个 RunWorkerCompleted 事件,可以在这两个事件中更新 UI 控件。 示例代码: ``` private void button1_Click(object sender, EventArgs e) { backgroundWorker1.RunWorkerAsync(); } private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) { // 在这里执行耗时的操作 } private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) { // 在这里更新 UI 控件 } private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { // 在这里更新 UI 控件 } ``` 以上两种方式都可以在分线程中安全地更新 UI 控件,具体使用哪种方式取决于实际情况。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值