那么什么叫异步消息处理线程呢,开始在主线程

时间:2019-09-30 15:37来源:美高梅手机游戏网站
不信我们验证一下: NetworkOnMainThreadException 一个简单的案例:我们想通过网络请求获取一段文字,显示在页面中 public class HandlerActivity extends AppCompatActivity implements View.OnClickListener { pri

不信我们验证一下:

NetworkOnMainThreadException

一个简单的案例:我们想通过网络请求获取一段文字,显示在页面中

public class HandlerActivity extends AppCompatActivity implements View.OnClickListener {

    private Button mButton;
    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        mButton = (Button) findViewById(R.id.button1);
        mButton.setOnClickListener(this);
        mTextView = (TextView) findViewById(R.id.textView1);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                HttpURLConnection connection;
                BufferedReader bufferedReader;
                try {
                    URL url = new URL("https://www.baidu.com");
                    connection = (HttpURLConnection) url.openConnection();
                    connection.setRequestMethod("GET");
                    InputStream inputStream = connection.getInputStream();
                    bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                    final StringBuilder response = new StringBuilder();
                    String line;
                    while ((line = bufferedReader.readLine()) != null) {
                        response.append(line);
                    }

                    mTextView.setText(response.toString());

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

思路很简单,点击事件中,通过一段普通的网络操作,向百度首页请求信息,并从返回的字节流中读取出文字,最后把结果显示在 mTextView上,但运行即可发现如下报错:

E/AndroidRuntime: FATAL EXCEPTION: main
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1145)

NetworkOnMainThreadException,“在主线程里进行网络操作异常”,可以回想起来,我们常常说诸如大型文件读写,网络操作等耗时的操作,要放在子线程里,以免阻塞主线程的进行。在 Android 应用程序里,主线程控制下的应用界面很忙,它在一直进行显示工作,由于 APP 的交互性,还必须去响应随时到来点击事件,button 键、back 键和 home 键,一旦某个按键不立即响应,就会产生卡死的效果,这是一个交互性应用要绝对避免的。

回到我们的例子,主线程中,点下按钮,开始在主线程进行访问网络操作,如果网络过程没结束,就意味着主线程内其它的操作不能进行,即使这时用户不耐烦地点下 back 键要退出,界面也不会有一点反应,这太糟糕了!

所以耗时操作放在子线程中,点击按钮,OK,让子线程去忙吧,主线程还是可以应付交互操作的。

3 多线程处理handler的时候,容易造成内存泄漏。。。

这时,mUiHandler 最觉无颜以对,原本以为它生于主线程,作用于主线程,所以 UI 操作才不在话下信手拈来,没想到,却被生于子线程的 mZiHandler 砸了场子。

初识 Handler/Message

话不多说,看看一段正式的 Handler 代码是如何解决这个问题的:

public class HandlerActivity extends AppCompatActivity implements View.OnClickListener {

    private Button mButton;
    private TextView mTextView;

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 200:
                    mTextView.setText((String) msg.obj);
                    break;
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        mButton = (Button) findViewById(R.id.button1);
        mButton.setOnClickListener(this);
        mTextView = (TextView) findViewById(R.id.textView1);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        HttpURLConnection connection;
                        BufferedReader bufferedReader;
                        try {
                            URL url = new URL("https://www.baidu.com");
                            connection = (HttpURLConnection) url.openConnection();
                            connection.setRequestMethod("GET");
                            InputStream inputStream = connection.getInputStream();
                            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                            final StringBuilder response = new StringBuilder();
                            String line;
                            while ((line = bufferedReader.readLine())!=null){
                                response.append(line);
                            }

                            Message message = Message.obtain();
                            message.what = 200;
                            message.obj = response.toString();
                            mHandler.sendMessage(message);

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

代码修改成这样,我们再运行,终于成功地显示了网络请求的数据。来看看现在的操作和原来有什么不同:

1.在主线程新建了一个匿名类 mHandler,并实现了它的一个方法叫 handleMessage(Message msg)
2.子线程里访问 UI 控件的那句代码被挪到了上述 handleMessage(Message msg) 方法里,空出的地方则替换成了几句 Message 相关的代码

mHandler,是主线程设置的专门负责接收子线程的消息处理类,当下子线程中 Message 相关的几句可以这么理解:

1.子线程找来一个消息盒子:Message message = Message.obtain(); // 为什么不直接 new Message(); 读者可以自己搜索一下
2.盒子上的代号设置为200:message.what = 200;
3.盒子内容是子线程的结果:message.obj = response.toString();
4.向目标 mHandler 发送:mHandler.sendMessage(message);

就这样,一个消息就由子线程,发向了身处主线程里的 mHandler,经处理后交由主线程决定呈现内容~~~

 

我:展开讲讲?

mQueue 在 Handler 里的作用

整理一下思路,还记得子线程发送 message 给 mHandler 的操作吗?当时包装好消息盒子,最后一步是干什么来着?

mHandler.sendMessage(message);

没错,就这么一句,子线程自己好像没干什么,包装好的消息盒子就发到 mHandler 那里了,然后又到 mHandler 的 handleMessage() 方法里了。这么说,是 mHandler 找各子线程一个个搜集消息盒子然后直接处理?子线程不确定,它想搞清楚自己的消息盒子是怎么流转的,于是决定去 Handler 的 sendMessage(message) 源码里看个究竟。

mHandler.sendMessage(message) 方法几经辗转,最后到了 Handler 的这个方法里

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);
}

可以看到,子线程的消息,连同发送时间,都被记录走了,等等,我们看到了什么?mQueue!接着看 enqueueMessage(queue, msg, uptimeMillis),看 mQueue 在这里干什么

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

代码最后,程序由 Handler 的方法,走进了 MessageQueue 的 enqueueMessage(msg, uptimeMillis) 方法,看来消息盒子都进 mQueue 里去了!

boolean enqueueMessage(Message msg, long when) {
    ...
    synchronized (this) {
        ...
        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;
}

可以看出,在 mQueue 内部,Message 作为 Node 以链表的形式,按时间顺序一个一个地链接了起来,同时可以看见其内部操作都有同步锁加持,保证了多线程下传递消息的安全。

子线程明白了,消息盒子从自己内部发给 mHandler 后,并不是直接就被处理了,而是悄悄排在了它内部的 mQueue 里,mHandler 应该就是以这种方式才有条不紊地将各个消息处理的吧。

嗯,至此,子线程明白了自己的消息盒子去了哪里,又以怎样的形式排列着,全靠的是 mHandler 的 mQueue!

至于 mHandler 内部的处理方式,子线程不在意,毕竟消息的处理自己可说不上话。

 

我:嚯,不得了

走向二:

如果 mCallback 不为空,就走 mCallback.handleMessage(msg)

怎么又是一个 callback!先别急,此 mCallback 非彼 callback,之前的 callback 属于 Message的成员,还是 Runnable类型。这里的 mCallback呢,我们回看一下上面的 Handler 源码处,还记得我们当时留的伏笔吗

public interface Callback {
    public boolean handleMessage(Message msg);
}

原来,这个 mCallback 是 Handler 的成员,而且类型也不是 Runnable,而是一个要求实现 handleMessage(Message msg) 方法的接口,该方法也正是该走向的真正处理方式。等于说,我们新建 mHandler 的时候,不是以匿名类的方式重写 handleMessage(Message msg),而是给一个普通 Handler 对象设置一个 Callback 成员,这个成员是个接口,自己要 handleMessage(Message msg)方法。

和走向一一样,怎么给一个普通的 mHandler 设置 Callback,也翻一翻 Handler 源码的构造函数去试试吧。

不过,这里虽然名字也是 handleMessage(Message msg),但也不是我们重写所产生的效果……

 

我:……

Looper 在创建 handler 时的作用

主线程的 looper 哪儿来的了知道了,那么回到第一个疑问:为什么没有 looper,handler 就建立不了呢?

我们看看 Handler 的源码吧,其构造方法有重载,不过最终都辗转到其中一个

public class Handler {

    final Looper mLooper;
    final MessageQueue mQueue;
    final Callback mCallback;
    ...
    public Handler(Callback callback, boolean async) {
        ...
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
}

真相大白了!Handler 的成员里就有 mLooper!而且在 Handler 的构造方法里,其对 mLooper 进行了初始化和检查。

初始化时,拿的是当前所在线程的 looper:Looper.myLooper()。检查时,如果发现当前线程 Looper 为空,就会报出我们之前见的"Can't create handler inside thread that has not called Looper.prepare()"异常!

同时我们看到了 Handler 的成员里还有个 Callback 成员,还有个 MessageQueue 成员,前者先留个伏笔,后者是 MessageQueue,熟悉吗?翻看上面的 Looper 源码,MessageQueue 本身是 Looper 的成员,这里的 handler 在构造方法中,经由 mLooper,也得到了 mQueue 的引用。

事实上,mLooper 作为 Handler 的必需成员,其自身成员 mQueue 也担负着重要功能!我们马上讲到。

目前,为什么没 Looper 就不能创建 Handler 子线程也算是明白了。

单线程模型的UI主线程也是不安全的,会造成不可确定的结果。

子线程:之前的例子都太简单了,一个子线程,发一个简单的消息,通知主线程简单地显示,根本没优化空间……你想一下这种的情况,假如,咱写的是省高考成绩处理程序……

CalledFromWrongThreadException

于是,我们新建一个子线程,将网络操作部分挪了进去,然后 start 起来

public class HandlerActivity extends AppCompatActivity implements View.OnClickListener {

    private Button mButton;
    private TextView mTextView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        mButton = (Button) findViewById(R.id.button1);
        mButton.setOnClickListener(this);
        mTextView = (TextView) findViewById(R.id.textView1);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        HttpURLConnection connection;
                        BufferedReader bufferedReader;
                        try {
                            URL url = new URL("https://www.baidu.com");
                            connection = (HttpURLConnection) url.openConnection();
                            connection.setRequestMethod("GET");
                            InputStream inputStream = connection.getInputStream();
                            bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
                            final StringBuilder response = new StringBuilder();
                            String line;
                            while ((line = bufferedReader.readLine())!=null){
                                response.append(line);
                            }

                            mTextView.setText(response.toString());

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

又报错了,不过这次的异常名称不太一样

E/AndroidRuntime: FATAL EXCEPTION: Thread-290
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6087)
at android.view.ViewRootImpl.requestLayout(ViewRootImpl.java:868)

读一下异常信息:首先这个异常是从 android.view.ViewRootImpl的 checkThread 方法抛出的,内容翻译过来是“只有创建视图层次的原始线程才能触及其视图”翻译成人话,其实就是我们常常念叨的“只有主线程才能更改界面”。嗯,回到我们的例子,我们确实是在一个随意新建的子线程中,试图去访问并更改 mTextView 内容,这才引发的异常。

关于 ViewRootImpl 相关方法,涉及到布局绘制,AMS等纷繁的领域,笔者暂不能展开详解…如果一定要知道为什么只有主线程可以访问 UI,子线程碰都碰不得这个问题,读者可以暂且这么理解:学习并发的时候我们就知道,最大的隐患就是多线程的同步问题,如果程序里有多个子线程,在进行完数据读取后,要更改的是同一个目标 —— mTextView,就难免引发数据错乱,影响界面内容。

所以把结果都交给主线程吧,通知它一个人有条不紊地进行下去。

这样,我们的目的就很明确了,要在主线程里运行

mTextView.setText(response.toString());

但子线程的工作结果 response,要如何交给主线程?

在 Android中,这就是异步消息处理问题,Android 提供了 Handler、Message 等工具,为子线程和主线程之间的数据传递,打开了通道!

Handler的构造函数中,会把当前ui线程的Looper与Handler关联。

“Only the original thread that created a view hierarchy can touch its views”

关于新建 handler 的意外

这时,子线程觉得 mHandler 这个角色很不错,决定也搞一套类似“主线程 —— Handler —— 子线程”这种模式,自己效仿主线程,也设立一个消息处理人handler(虽然好像根本没有其他线程找他通讯托办事……)但是他不管,就是要 Handler!那就来吧:

public class HandlerActivity extends AppCompatActivity implements View.OnClickListener {
    ……

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
               ……
            }
        }
    };

    private Handler mZiHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        ……
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                ...
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                       ……
                       mZiHandler = new Handler();
                    }
                }).start();

                break;
        }
    }
}

于是,声明了 mZiHandler 后,子线程在自己的 run() 方法里 new 了一个 Handler 做初始化,运行一下吧

RuntimeException: "Can't create handler inside thread that has not called Looper.prepare()"

报错了,可见,Handler 这个东西,不是你子线程想建就能建啊!

注意:::

子线程:没有。

走向一:

被处理的 Message 如果自带 callback 的话,就走 handleCallback(Message message),嚯,消息盒子还能带着回调去让 Handle 处理呐。再看看其内部实现:

private static void handleCallback(Message message) {
    message.callback.run();
}

直接运行所携带的 callback 的 run() 方法了,等于说,子线程带着自己定义的处理方式,直接找 Handler 让它照着办,脸可真够大的……

并且我们应该可以得知,Message 自带的 callback 是 Runnable 类型的。

至于当时发送 Message 时怎么加上个 Runnable 的,读者请先翻翻源码自己看一下,文章最后会讲,到时再来验证。

走向一说完,感觉这个走向应该不是我们重写所实现的……

 

……场面一度十分尴尬,子线程的数据,居然在 UI 上显示了!

走向三:

如果 message 既没有自带 callback,mHandler 也没有设置 Callback 成员,或者有 Callback 成员但处理结果返回 false,那就走向了最终的 handleMessage(Message msg) 方法。

这个也叫 handleMessage(Message msg)!!!找源码看一下

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

子类要接收 Message 必须实现的方法!没错了,这就是我们原程序里的重写方式,我们当时写的,就是走向三。

最后,我们将走向一和走向二的写法也展示一下吧。

走向一:你可能没有找到给 message 设置 Runnable 类型的 callback 的操作,不过看下面的程序

public class HandlerActivity extends AppCompatActivity implements View.OnClickListener {
    ...
    private Handler mHandler = new Handler();
    ...
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button1:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        ...
                        try {
                            ...
                            //方式一
                            runOnUiThread(new Runnable() {
                                @Override
                                public void run() {
                                    mTextView.setText(response.toString());
                                }
                            });

                            //方式二
                            mHandler.post(new Runnable() {
                                @Override
                                public void run() {
                                    mTextView.setText(response.toString());
                                }
                            });

                            //方式三,利用方式二
                            mTextView.post(new Runnable() {
                                @Override
                                public void run() {
                                    mTextView.setText(response.toString());
                                }
                            });

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

以上三种常见方式,翻阅源码,可以知道其内部都是通过将 Runnable 实现类包装给一个新建的 message 作 callback,然后发送给主线程的 handler,也就是走向一的方式(读者可以亲手去跟进证实一下)。

注意,此时的 handler 只需简单的一个声明创建,毕竟处理方式被受理的 message 指定了要走自带的 callback 的 run() 方法,那自己执行就是,不需要再重写或添加什么处理方式。

走向二:你应该写得出来吧

public class HandlerActivity extends AppCompatActivity implements View.OnClickListener {

    private Handler mHandler = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            switch (msg.what) {
                case 200:
                    mTextView.setText((String) msg.obj);
                    break;
            }
            return true; 
        }
    });

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        ...
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        ...
                        try {
                            ...
                            Message message = Message.obtain();
                            message.what = 200;
                            message.obj = response.toString();
                            mHandler.sendMessage(message);
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
                break;
        }
    }
}

和原程序走向三的写法相比,只有新建 mHandler 一处不同,就是并非实现 handleMessage(Message msg),而是将一个实现了 handleMessage(Message msg) 方法的 Callback 实现类作为参数传入到 Handler 的带参构造方法里。

到此,我们的异步消息处理 Handler 篇算是讲完咯~~~~

基于Handler机制,在子线程先获得Handler对象,该对象将数据发送到主线程消息队列,Main线程通过MainLooper从消息队列中取出Handler发过来的这个消息时,会回调 Handler的handlerMessage()方法。

目前可以看出,一个线程的 Handler,处理方法能否作用于 UI,并不取决于 handler 归属在哪,而是要看构建它消息处理机制的 looper 是属于哪个线程的。

主线程的 Looper 哪儿来的?

原来,应用程序启动的代码如下

public final class ActivityThread {
    public static final void main(String[] args) {
        ......
        Looper.prepareMainLooper();
        ......
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null) {    
            sMainThreadHandler = thread.getHandler();
        }
        ......
        AsyncTask.init();  

        if (false) {  
            Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread"));  
        }  
        Looper.loop();  
        throw new RuntimeException("Main thread loop unexpectedly exited"); 
    }
}

可以看见里面有这么一句

Looper.prepareMainLooper(); 

嗯,原来主线程在应用启动的时候就着手准备主 Looper 了,那去看看 Looper 类里这个方法具体干了什么吧。

public final class Looper {
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    private static Looper sMainLooper;  // guarded by Looper.class
    final MessageQueue mQueue;
    ...
    public static void prepareMainLooper() {
            prepare(false);
            synchronized (Looper.class) {
                if (sMainLooper != null) {
                    throw new IllegalStateException("The main Looper has already been prepared.");
                }
                sMainLooper = myLooper();
            }
    } 
    ...
    public static void prepare() {
        prepare(true);
    }
    ...
    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));
    }
    ...
    /**
     * Return the Looper object associated with the current thread.  Returns null if the calling thread is not associated with a Looper.
     */
    public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
}

一行一行看过程,走到 prepare(false),先检查 sThreadLocal.get(),目前的情况是程序刚启动,如果这刚开始还没设置呢,就 get 到了一个不为空的 looper,显然是不合理的。那就报错吧,“一条线程只能有一个 looper”。如果顺顺利利地 get 到了 null,才现场 new 一个不可取消的 looper,并为当前Looper类的静态成员 sThreadLocal set 进去这个 looper,这个 Looper,就是主线程独有的 MainLooper。至此,我们应用程序的主线程里就始终存在着这个主 Looper对象了。

(读者到这里可能有疑问,如果子线程都准备了自己的 looper,想获得这个 looper 时,应该走的是上面代码中 myLooper() 方法,然后走 sThreadLocal.get(),但是!这个 sThreadLocal 是静态类,也就是所有 looper 对象共享的啊,get 的时候岂不所有线程得到的都是同一份 looper ??? 其实这就是 ThreadLocal 的妙用了,先回答你,在不同线程里 sThreadLocal.get() 的并不是同一个结果,而是线程各自的 looper 哦!之后会新开文章详细介绍的。)

Handler 、 Looper 、Message 这三者都与Android异步消息处理线程相关的概念。

子线程:“协同处理线程”。

Looper 在 Handler 处理 Message 时的作用

再总结一下吧,一条条子线程,有消息就包一个盒子,发给主线程的 handler,在 handler 内部,盒子按时间顺序链接在 mQueue 里,现在的疑问是,这些盒子是如何一个个送到我们重写的 handleMessage() 方法里的呢。

按道理,盒子要被一个个取出,我们就得看见 mQueue 链表遍历的操作,mQueue 是 Handler 成员,看看 Handler 源码有没有遍历操作看了一遍,似乎没有,别忘了,mQueue 也是 Looper 的成员,再去 Looper 源码看看,这时,一个叫 loop() 的方法引起了我们的注意。

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;
    ...
    for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }
        ...
        try {
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
        ...
        msg.recycleUnchecked();
    }
}

看见没!mQueue 就是在这里被遍历,将 Message 各个取出的!很好,取出后走到

msg.target.dispatchMessage(msg);

嗯,这应该就是 Message 的处理过程了。

至此,我们知道,mQueue 中排列的消息,是通过 Looper 的 loop() 方法遍历取出并交由 mHandler 处理的。

再回看 ActivityThread 的 main() 方法,果然当时其创建完主线程的 MainLooper,紧接着就 loop 了起来,动作真快啊!回想起某个傻傻的子线程,上来就设立 handler 失败不说,等准备了 looper 再设立 handler,最后还忘了 loop(),就算有线程给你发消息你也分发处理不起来呀,图样啊~不过,这个子线程愿意学习先进技术,并有获取其他线程消息,自己处理的想法,敢想敢干,以后的文章肯定可以见到他发光发热的一天!

等等,先别回忆了,这里又有疑问啦,两个:

1.处理 Message 的确实应该是 mHandler 我们知道,但你这里的处理者是 msg.target 啊,这俩是一个东西吗?
2.处理方法名字对不上啊,你这里是 dispatchMessage(msg) ,我们新建的 mHandler 重写的方法叫 handleMessage(Message msg) 啊

先看第一个问题:

通过查看 Message 的源码,我们了解到其含有一个 Handler 类型的成员,不出意外,这个 msg.target 应该就是我们的 mHandler 了。那什么时候设置的这个 target 的呢?往回看 Handler 的 enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) 方法,第一句 :

msg.target = this; 

this 是什么,当然就是我们的 mHandler 啦,所以没毛病,msg.target 就是我们的 mHandler!

再看第二个问题:

这里消息处理的方法叫 dispatchMessage(msg) ,而我们新建的 mHandler 重写的方法叫 handleMessage(Message msg) ,不多说,看看前者的源码:

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

可以看出,Message 的处理方法有三个走向

(2)  前者最终还是会转化成sendMessage,只不过最终的处理方式不一样,前者会执行runnable的run方法;后者可以被安排到线程中执行。

子线程:还可以,你源码怎么给的。

新角色 Looper

“不能在一个没有 prepare looper 的线程内创建 handler”,对于这个解释,子线程不是很满意,有至少两个疑问

1.Looper 是干什么的,为什么没它连 Handler 都 new 不了? 
2.主线程也没见准备Looper,为什么它就 new 了?

先不管了,让咱准备 looper 那就先 prepare 一下 looper 吧,然后再新建 handler

public class HandlerActivity extends AppCompatActivity implements View.OnClickListener {
    ……

    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
               ……
            }
    };

    private Handler mZiHandler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        ……
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                       ……
                       Looper.prepare();
                       mZiHandler = new Handler();
                    }
                }).start();
                break;
        }
    }
}

果然,加上这句就正常运行了。接下来,就来回答一下子线程的两个疑问吧,先从第二个疑问开始。

3 耗时的都放到子线程处理,因为UI线程优先级和其他普通线程一样。被阻塞超过5秒 ,会提示ANR。

我:不更新 UI,那别的子线程找你的 handler 干嘛?有什么诉求是你能干的而人家主线程干不了的吗?

Android只允许主线程更新UI界面(非线程安全的),子线程处理后的结果,即无法直接访问主线程,这就要用到Handler机制来解决此问题.

“Only the original thread that created a view hierarchy can touch its views”

6 子线程thread想管理消息队列,需要在线程当中调用Looper.prepare()方法使消息循环初始化,并且调用Looper.loop()使消息循环一直处于运行状态,直到停止循环。所以Looper主要就是完成MessageQueue与Handler进行交互的功能。

子线程:并不,你看主线程,它就没有这么多规矩,Looper 一早就备好了,还自己 loop() 了起来,handler 直接 new 就行。我是来帮它的,我也要这种待遇!

前提,主线程ui,只能刷新页面和处理事件;耗时的数据处理都在子线程中。。。

被这套机制吸引的子线程异想天开,想效仿主线程,给自己也设置个 Handler,结果吃了苦头,发现没准备 Looper 的话,Handler 压根设置不了,接着又发现,Looper、Handler 设置好了还不行,这 Looper 还得开启 loop(),消息处理才能真正流转起来。然而人家主线程,应用启动时就在 ActivityThread 的 main() 方法里为它全权安排好了 Looper 事宜,自己就放心 new Handler() 即可,真是线程比线程,气死个线程。

那么什么叫异步消息处理线程呢?

之前子线程 handler 的失败,在于新建时,new Handler() 操作会默认取得当前线程的 looper,进而构建消息处理机制,looper属于子线程,MessageQueue 也属于子线程,handler 接收的 message 就在子线程中排列、遍历,handleMessage 所属的 dispatchMessage 方法,也还嵌套在 loop() 方法里,整个处理过程自然也就跳不出子线程的范围,当然不能去碰更新 UI 这雷区。

1.  不要阻塞UI线程;

new Thread(new Runnable() { @Override public void run() { // 1 Looper looper = Looper.getMainLooper(); // 2 mZiHandler = new Handler { @Override public void handleMessage(Message msg) { switch  { case 100: mTextView.setText msg.obj); break; } } }; Message message = Message.obtain(); message.what = 100; message.obj = "请子线程处理者帮我在界面显示这句话"; mZiHandler.sendMessage; }}).start();

详细总结:::

public class MainActivity extends AppCompatActivity{ private Handler mUiHandler; private Handler mZiHandler; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_handler_thread); mUiHandler = new Handler() { @Override public void handleMessage(Message msg) { switch  { case 200: mTextView.setText msg.obj); break; } } }; new Thread(new Runnable() { @Override public void run() { // 1 Looper.prepare(); // 2 mZiHandler = new Handler() { @Override public void handleMessage(Message msg) { switch  { case 100: mTextView.setText msg.obj); break; } } }; // 3 Looper.loop(); Message message = Message.obtain(); message.what = 100; message.obj = "请子线程处理者帮我在界面显示这句话"; mZiHandler.sendMessage; } }).start(); }

IntentService里面就包括了HandlerThread,所以才能实现消息队列 循环处理消息,而不阻赛。

子线程:前面说了啊,子线程们查询检索就够忙了,没功夫给你解析计算综合分,只能把所有未处理的原始数据,包装成一个个消息,统统找主线程的 handler 抛给 MainLooper 处理。

5 Activity中的UI主线程中,无需使用显式的方式进行Looper的初始化以及开始循环,是因为Activity内部包含一个Looper对象,它会自动管理Looper,处理子线程中发送过来的消息。

意料之中的胜利~ 毕竟,常识怎么可能出错呢?

 

编辑:美高梅手机游戏网站 本文来源:那么什么叫异步消息处理线程呢,开始在主线程

关键词: