Android 实现多任务——Handler(源码解析)

一、多任务实现原理
对于多线程的Android应用程序来说,有两类线程:一类是主线程(UI线程),另一类是工作线程。Android的线程间消息处理机制主要是用来处理主线程和工作线程间通信的,主线程(UI线程)中有一个消息循环(Looper),负责处理消息队列(Message Queue)中的消息。Handler的作用就是把消息加入消息队列中,并分发和处理该消息队列中的消息。

二、Android 有两种方式实现多线程操作UI:

  1. 创建线程Thread,用Handler负责线程间的通信和消息;
  2. AsyncTask 异步多任务。

本文主要记录用Handler实现多任务,AsyncTask实现多任务在下篇文章中。

android.os.Handler是Android SDK中处理定时操作的核心类,通过Handler类,可以提交和处理一个Runnable对象。这个对象的run方法可以立刻执行,也可以在指定时间之后执行。

三、Handler的使用方式

  1. Handler.sendMessage()
  2. Handler.post()

四、 Handler.post()方法

  1. 创建一个Handler对象;
  2. 将要执行的操作写在线程对象的run方法中;
  3. 使用post方法运行线程对象。

4.1、创建一个Handler对象

private Handler mHandler = new Handler();

4.2、将要执行的操作写在线程对象的run方法中

private Runnable mRunnable;
 mRunnable = new Runnable() {
            @Override
            public void run() {
                //将要执行的操作
            }
        };

4.3、使用post方法运行线程对象

 mHandler.post(mRunnable);

接着看post()方法是怎么执行的,见源码

/**
* Handler.java
*/
public final boolean post(@NonNull Runnable r) {
       return  sendMessageDelayed(getPostMessage(r), 0);
    }

(a)首先用getPostMessage()方法对Runnable进行封装,返回一个Message对象。

/**
* 获取一个message;
* 将Runnable封装进message的callback
*/
private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }

Question 1:Handler中是如何获取message对象的?
Answer:是通过Message.obtain()在Message池中获取Message对象

/**
* Message.java
* Message sPool 池里第一个对象
* int sPoolSize 对象池的长度
* Message next  连接下一个Message的成员变量
* 先判断sPool是否为空,若为空则new Message(),否则在对象池中获取一个Message对象返回;
*/
 public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

(b)接着调用sendMessageDelayed()方法

/**
* 对delayMillis做有效性检测
*/
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }

public boolean sendMessageAtTime(@NonNull 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(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

©Handler对象把Message放入MessageQueue队列中并且循环复用

/**
* MessageQueue.java
* msg.target      target是否为空
* msg.isInUse()   是否已经使用
*/
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;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
public class DownloadActivity extends Activity { private ProgressBar downloadbar; private EditText pathText; private TextView resultView; private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what) { case 1: int size = msg.getData().getInt("size"); downloadbar.setProgress(size); float result = (float)downloadbar.getProgress()/ (float)downloadbar.getMax(); int p = (int)(result*100); resultView.setText(p+"%"); if(downloadbar.getProgress()==downloadbar.getMax()) Toast.makeText(DownloadActivity.this, R.string.success, 1).show();//下载完成提示 break; case -1: Toast.makeText(DownloadActivity.this, R.string.error, 1).show(); //下载失败提示 break; } } }; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); Button button = (Button)this.findViewById(R.id.button); downloadbar = (ProgressBar)this.findViewById(R.id.downloadbar); pathText = (EditText)this.findViewById(R.id.path); resultView = (TextView)this.findViewById(R.id.result); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { String path = pathText.getText().toString(); if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){ File dir = Environment.getExternalStorageDirectory();//文件保存目录 System.out.println("文件保存目录=="+dir); download(path, dir); }else{ Toast.makeText(DownloadActivity.this, R.string.sdcarderror, 1).show();//存贮路径不存在,则淡出提示 } } }); } //对于UI控件的更新只能由主线程(UI线程)负责,如果在非UI线程更新UI控件,更新的结果不会反映在屏幕上,某些控件还会出错 private void download(final String path, final File dir){ new Thread(new Runnable() { @Override public void run() { try { FileDownloader loader = new FileDownloader(DownloadActivity.this, path, dir, 3); int length = loader.getFileSize();//获取文件的长度 downloadbar.setMax(length); loader.download(new DownloadProgressListener(){ @Override public void onDownloadSize(int size) {//可以实时得到文件下载的长度 Message msg = new Message(); msg.what = 1; msg.getData().putInt("size", size); handler.sendMessage(msg); }}); } catch (Exception e) { Message msg = new Message(); msg.what = -1; msg.getData().putString("error", "文件下载失败"); handler.sendMessage(msg); } } }).start(); }

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值