Android多线程基础

线程的基本用法

Android多线程编程并不比Java多线程编程特殊,基本都是使用相同的语法。
那么如何创建一个线程呢?下面有几种方法可以使用:

  • 定义一个类继承自Thread,重写run()方法,在其中编写耗时逻辑
class Thread1 extends Thread{    
  @Override    
  public void run() {        
    //处理具体逻辑    
   }
}

最后使用其start()方法就可以开启这个线程

new Thread1().start();
  • 使用Runnable接口的方式来定义一个线程
class Thread2 implements Runnable{
    @Override
    public void run() {
        //处理具体逻辑
    }
}

Thread的构造方法中传入了实现了Runnable接口的Thread2对象,然后调用Thread的start()方法

Thread2 thread2 = new Thread2();
new Thread(thread2).start();
  • 当然更多的时候我们使用匿名类的方式
new Thread(new Runnable() {
    @Override
    public void run() {
        //处理具体逻辑
    }
}).start();

异步消息处理机制

因为Android是线程不安全的,也就是说更新UI只能在主线程中进行。Android提供了一套异步消息处理机制,很好的解决了这个问题,先看一下是如何写的:

  • 先新建一个项目,修改activity_main.xml中的代码



    
  • 定义了两个控件,TextView用于显示字符,Button用于改变TextView中显示的内容。点击Button后可以将TextView中的字符串更改成“这是更新过的UI界面”。
    修改MainActivity中的代码
public class MainActivity extends Activity implements View.OnClickListener {

    private TextView textView;
    private Button button;
    public static final int UPDATE_UI = 1;

    private Handler myHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case UPDATE_UI :
                    textView.setText("这是更新过的UI界面");
                    break;
                default:
                    break;
            }
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        textView = (TextView) findViewById(R.id.text_view);
        button = (Button) findViewById(R.id.update_ui);
        textView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.update_ui:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_UI;
                        myHandler.sendMessage(message);
                    }
                }).start();
                break;
            default:
                break;
        }
    }
}

点击了button之后,在子线程中创建了一个Message对象,并将一个整型常值UPDATE_UI 赋值给Message.what,通过Handler的sendMessage()方法把Message对象发送出去。在主线程中的Handler收到这个Message对象,并在handleMessage()中处理这条Message。对Message.what值进行判断,如果相符,就更新UI。

这样,异步处理的基本用法就掌握了,接下来分析一下Android异步消息处理机制是如何工作的。

分析异步消息处理机制

Android异步消息处理主要是由四个部分组成,Message,Handler,MessageQueue和Looper。那么,MessageQueue和Looper是什么呢?接下来分析一下源码,之后就会对这四个部分的功能有了基本的了解。

  • 创建Message对象,并将整型常值UPDATE_UI传入Message.what,之后调用Handler的sendMessage()方法将消息发送出去。查看sendMessage()的源码,最终会调用到以下方法:
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);
    }
  • sendMessageAtTime()方法传入两个值,第一个就是我们传入的Message对象,第二个是延迟时间。然后创造了MessageQueue对象,并调用enqueueMessage()方法。
boolean enqueueMessage(Message msg, long when) {
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                msg.next = p; // invariant: p == prev.next
                prev.next = msg;
            }

            // We can assume mPtr != 0 because mQuitting is false.
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }
  • 该方法将传入的Message对象按照时间排序。那么现在很明显了,MessageQueue是消息队列,主要用于存放Handler发送的消息,这部分消息会一直存在消息队列中,每个线程中只有一个MessageQueue。
    那么,有存消息,当然也有处理消息的,那就是Looper,他从MessageQueue中取出Message对象,并对其处理。代码如下:
    public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        final MessageQueue queue = me.mQueue;

        // Make sure the identity of this thread is that of the local process,
        // and keep track of what that identity token actually is.
        Binder.clearCallingIdentity();
        final long ident = Binder.clearCallingIdentity();

        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }

            // This must be in a local variable, in case a UI event sets the logger
            Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            msg.target.dispatchMessage(msg);

            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            // Make sure that during the course of dispatching the
            // identity of the thread wasn't corrupted.
            final long newIdent = Binder.clearCallingIdentity();
            if (ident != newIdent) {
                Log.wtf(TAG, "Thread identity changed from 0x"
                        + Long.toHexString(ident) + " to 0x"
                        + Long.toHexString(newIdent) + " while dispatching to "
                        + msg.target.getClass().getName() + " "
                        + msg.callback + " what=" + msg.what);
            }

            msg.recycleUnchecked();
        }
    }
  • 调用queue.next()方法,拿出了最新的Message对象,之后调用msg.target.dispatchMessage()方法,那么msg.target是什么,查看代码发现msg.target是Handler类,之后调用Handler的dispatchMessage()方法
        public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

到这里,发现了熟悉的方法,handleMessage()方法,这个方法也就是之前处理Message,更新UI的方法。

那么现在Android异步消息处理的流程就是如下:

  • 创建一个Message对象,通过Handler将消息发送出去
  • 这条消息存放在MessageQueue中等待被处理
  • Looper从MessageQueue中取出待处理的消息,分发回Handler的handleMessage()方法中。

参考资料:http://blog.csdn.net/guolin_blog/article/details/9991569 郭神的博客
《第一行代码》 郭神的书

PS1:Looper源码是很棒的单例设计模式

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

PS2:写博客好难啊,即使我基本上是实现郭神博客和书上的内容。这篇很大程度上借鉴了郭神的内容,无法称原创。很感谢郭神,stormzhang等大神写的原创博客和书,让我收获很多。

你可能感兴趣的:(Android多线程基础)