主线程Thread和子线程Thread的区别

1. 前言

众所周知,在android中,非ui线程中是不能更新ui,如果在子线程中做ui相关操作,可能会出现程序崩溃。一般的做法是,创建一个Message对象,然后Handler发送该message,然后在Handler的handleMessage()方法中做ui相关操作,这样就成功实现了子线程切换到主线程。 
初始化Handler有两个地方,一个在主线程中,一个在子线程中,具体有什么区别呢,接下来从源码角度来讲解。

2.使用方法

1)主线程中初始化handler

handler1= new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if (msg.arg1==1) {
                    Toast.makeText(MainActivity.this,"hanlder1",Toast.LENGTH_SHORT).show();
                }
                super.handleMessage(msg);
            }
        };
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = handler1.obtainMessage();
                message.arg1 = 1;
                handler1.sendMessage(message);
            }
        }).start();

这个是大家用得很多的场景,这边不再多说了。

2)子线程中初始化handler

new Thread(new Runnable() {
            @Override
            public void run() {
                handler2 = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.arg1==1) {
                            Toast.makeText(MainActivity.this,"hanlder2",Toast.LENGTH_SHORT).show();
                        }
                        super.handleMessage(msg);
                    }
                };
                Message message = handler2.obtainMessage();
                message.arg1 = 1;
                handler2.sendMessage(message);
            }
        }).start();

如果是上面的代码,运行程序后,程序会crash,崩溃信息如下所示:

E/AndroidRuntime: FATAL EXCEPTION: Thread-50003
        Process: com.example.cyf.myapplication, PID: 2223
        java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:200)
        at android.os.Handler.<init>(Handler.java:114)
        at com.example.cyf.myapplication.MainActivity$3$1.<init>(MainActivity.java:0)
        at com.example.cyf.myapplication.MainActivity$3.run(MainActivity.java:56)
        at java.lang.Thread.run(Thread.java:818)

从错误的解释可以看出:没有调用Looper.prepare(),不能创建handler。所以很简单,我们在创建handler前面加上Looper.prepare(),再运行程序,果然没有错误了。 
代码如下所示:

new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                handler2 = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.arg1==1) {
                            Toast.makeText(MainActivity.this,"hanlder2",Toast.LENGTH_SHORT).show();
                        }
                        super.handleMessage(msg);
                    }
                };
                Message message = handler2.obtainMessage();
                message.arg1 = 1;
                handler2.sendMessage(message);
                Looper.loop();
            }
        }).start();

这种调用方式相信很多人也明白,面试的时候,也经常提到,但是为什么这样写就可以呢?

3.源码讲解

1)我们先看下handler的源码中的构造函数。

public Handler() {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }
    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 = null;
}

可以看到第12行出现了刚刚上述的错误信息,很明显mLooper为空的时候,就会抛出如下异常。

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

2)Looper对象什么时候为空,我们看看Looper.myLooper()中的代码了,如下所示:

public static final Looper myLooper() {
    return (Looper)sThreadLocal.get();
}

代码非常少,很容易理解,就是从sThreadLocal对象中取出Looper。sThreadLocal源码其实就是个数组,源码不贴了,把他想成数组就好了。sThreadLocal什么时候存在Looper对象呢,及什么时候会set一个Looper到该数组中呢,不用想,肯定是Looper.prepare()方法,我们来看下它的源码:

3)Looper.prepare()源码

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

从上面的代码可以看出,sThreadLocal如果没有Looper,则新建Looper进去,如果存在,则抛出异常,一个线程最多只能创建一个Looper对象。

4)所以一开始调用Looper.prepare()方法,其实相当于为线程新建了一个Looper放到sThreadLocal中,这样mLooper = Looper.myLooper();则可以从sThreadLocal中获取刚刚创建的Looper,不会导致程序崩溃。

4.其他:

可能会有人说,为什么我在主线程中初始化handler的时候,没有new Looper,为什么没有报异常,相信很多人会听到别人说,主线程默认给我们创建了Looper对象,没有错。 
我们看下ActivityThread的源码中的main()方法

public static void main(String[] args) {
    SamplingProfilerIntegration.start();
    CloseGuard.setEnabled(false);
    Environment.initForCurrentUser();
    EventLogger.setReporter(new EventLoggingReporter());
    Process.setArgV0("<pre-initialized>");
    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.prepareMainLooper();源码

public static final void prepareMainLooper() {
    prepare();
    setMainLooper(myLooper());
    if (Process.supportsProcesses()) {
        myLooper().mQueue.mQuitAllowed = false;
    }
}

从上面代码可以看到,会有prepare()方法,及主线程中会默认为我们初始化一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。

5.结论:

1)主线程中可以直接创建Handler对象。 
2)子线程中需要先调用Looper.prepare(),然后创建Handler对象。

6.相关代码
package com.example.cyf.myapplication;
import android.app.Activity;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;
public class MainActivity extends Activity {
    private Handler handler1;
    private Handler handler2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initHandler1();
        initHandler2();
    }
    /**
     * 初始化handler(主线程)
     */
    private void initHandler1() {
        handler1= new Handler(){
            @Override
            public void handleMessage(Message msg) {
                if (msg.arg1==1) {
                    Toast.makeText(MainActivity.this,"hanlder1",Toast.LENGTH_SHORT).show();
                }
                super.handleMessage(msg);
            }
        };
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = handler1.obtainMessage();
                message.arg1 = 1;
                handler1.sendMessage(message);
            }
        }).start();
    }
    /**
     * 初始化handler(子线程)
     */
    private void initHandler2() {
        new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                handler2 = new Handler(){
                    @Override
                    public void handleMessage(Message msg) {
                        if (msg.arg1==1) {
                            Toast.makeText(MainActivity.this,"hanlder2",Toast.LENGTH_SHORT).show();
                        }
                        super.handleMessage(msg);
                    }
                };
                Message message = handler2.obtainMessage();
                message.arg1 = 1;
                handler2.sendMessage(message);
                Looper.loop();
            }
        }).start();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值