Android--Handler机制(二)(Android Studio)

下面接着上篇继续谈谈我对Handler机制的认识。

五、Handler, Looper, MessageQueue的关系详解

  1. 关系图见上一篇http://blog.csdn.net/u014294166/article/details/50677910

  2. ActivityThread会创建所有的Activity并回调所有的Activity的方法,同时默认创建main线程,在创建main线程中会创建Looper,在创建Looper过程中又会默认创建Message。
    源码跟踪分析如下:(先用word做的处理)
    Android--Handler机制(二)(Android Studio)_第1张图片

  3. Handler和Looper的关联源码
    Android--Handler机制(二)(Android Studio)_第2张图片
  4. Looper轮询

小结:Handler(sendMessage())负责发送消息,Looper(Looper.looper())负责接收Handler发送的消息,并直接把消息传给Handler自己(handMessage())。MessageQueue就是一个容器。

六、Handler与子线程

  1. 自定义与线程相关的Handler
/** * 自定义与线程相关的Handler */
public class MainActivity extends AppCompatActivity {

    private Handler mainHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            System.out.println("用来更新主线程中UI的handler---->" + Thread.currentThread());
        }
    };

    class MyThread implements Runnable{
        private Handler handler;// handler的自定义线程
        // 以下注释处的过程已经通过以上源码分析说明
        @Override
        public void run() {
            Looper.prepare();// 创建一个loop对象
            handler = new Handler(){// 获取当前线程的Looper对象
                @Override
                public void handleMessage(Message msg) {
                    //Toast.makeText(MainActivity.this, "current thread:" + Thread.currentThread(), Toast.LENGTH_LONG).show();
                    System.out.println("自定义的用来更新UI的handler------>" + Thread.currentThread());
                }
            };
            Looper.loop();// 死循环处理消息
        }
    }

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

        MyThread myThreadt = new MyThread();
        new Thread(myThreadt).start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        myThreadt.handler.sendEmptyMessage(1);// handler自定义的线程
        mainHandler.sendEmptyMessage(1);// 主线程的handler

        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
    }
}

耗时操作一般不直接在UI主线程上进行,否则容易出现卡死现象。

2 . HandlerThread
HandlerThread 继承于Thread,其本质就是Thread。与普通的Thread的差别在于,HandlerThread有Looper 成员变量,即主要的区别就在Looper的创建上,具体区别如下:
(1). 在普通Thread中实例化Handler时的一般步骤是:在线程run()方法当中先调用Looper.prepare()初始化Looper,然后再run()方法最后调用Looper.loop(),这样我们就在该线程当中创建好Looper。(注意:Looper.loop()方法默认是死循环)
(2). 在HandlerThread中创建一个HandlerThread即创建了一个Looper的线程,步骤如下:
*创建一个HandlerThread,即创建了一个包含Looper的线程。
HandlerThread handlerThread = new HandlerThread(“HandlerThread”);
handlerThread.start(); //创建HandlerThread后一定要记得start()
获取HandlerThread的Looper
Looper looper = handlerThread.getLooper();
创建Handler,通过Looper初始化
Handler handler = new Handler(looper);*

可以避免多线程并发时出现的空指针异常问题。比如说线程切换时Handler所需要Looper还没有创建。
protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        handlerThread = new HandlerThread("HandlerThread");
        handlerThread.start();// !!
        // Handler获取指定的Looper,无参时为默认个给定的Looper
        handler = new Handler(handlerThread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                System.out.println("current thread----->" + Thread.currentThread());
            }
        };
        handler.sendEmptyMessage(1);// 以上三步创建HandlerThread,然后通过handler发送消息
        //handlerThread.quit();// 如果想让HandlerThread退出,则需要调用handlerThread.quit();
    }

3 . 主线程向子线程发送消息

   以上所谈的都是子线程向主线程发送消息,通知主线程更新UI,但是也有主线程向子线程发送消息的情况。下面谈谈这种情况。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private Button b_send;// 发送消息
    private Button b_stop;// 停止发送消息

    // 主线程的Handler
    private Handler mainHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            Message message = new Message();
            System.out.println("=====》Main Handler");
            // 在主线程中给子线程每秒发送一个message
            subHandler.sendMessageDelayed(message, 1000);
        }
    };
    private Handler subHandler;// 子线程的Handler

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

        b_send = (Button) findViewById(R.id.btn_send);
        b_stop = (Button) findViewById(R.id.btn_stop);
        b_send.setOnClickListener(this);
        b_stop.setOnClickListener(this);
        // 子线程的looper的轮询是通过创建HandlerThread指定的
        HandlerThread hthread = new HandlerThread("Handler Thread");
        hthread.start();
        subHandler = new Handler(hthread.getLooper()){
            @Override
            public void handleMessage(Message msg) {
                Message message = new Message();
                System.out.println("=====》Sub Handler");
                // 在子线程中给主线程每秒发送一个message
                mainHandler.sendMessageDelayed(message, 1000);
            }
        };
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_send:
                mainHandler.sendEmptyMessage(1);
                break;
            case R.id.btn_stop:
                mainHandler.removeMessages(1);
                break;
            default:
                break;
        }
    }
}
   子线程中利用主线程的Handler向主线程发消息,主线程中利用子线程的Handler向子线程发消息。

七、Android中更新UI的几种方式

  • handler的post
  • runOnUiThread
  • handler的sendMessage
  • View控件自身调用post方法
public class MainActivity extends AppCompatActivity {

    private TextView tv;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        tv = (TextView) findViewById(R.id.tv);
        //updateUI1();
        //updateUI2();
        //updateUI3();
        updateUI4();
    }

    public void updateUI1(){
        new MyThread().start();
    }
    class MyThread extends Thread{
        @Override
        public void run() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(2000);
                        tv.setText("使用runOnUiThread的方法更新UI");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
        }
    }

    public void updateUI2(){
        Handler handler = new Handler();
        handler.post(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    tv.setText("Handler的post方法更新UI");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    private Handler handler3 = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 3){
                tv.setText("利用Handler的sendMessage方法更新UI");
            }
        }
    };
    public void updateUI3(){
        new Thread(new MyThread3()).start();
    }
    class MyThread3 implements Runnable{
        @Override
        public void run() {
            try {
                Thread.sleep(2000);
                handler3.sendEmptyMessage(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public void updateUI4(){
        tv.post(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(1000);
                    tv.setText("View控件自身调用post方法更新UI");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }
}

八、再谈“非UI线程更新UI”的问题

(1). 如下代码能正常更新UI
Android--Handler机制(二)(Android Studio)_第3张图片
(2). 如下代码报如下异常:
FATAL EXCEPTION: Thread-186 android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
Android--Handler机制(二)(Android Studio)_第4张图片

    不难看出,Android系统框架在对非UI线程直接操作UI信息进行判断的时候,准确度不够。在时间差内(代码片段中进行耗时操作,在本例中使用Thread.sleep模拟耗时操作)是可以在非UI线程直接更新UI信息的。

原因分析:
一步步查看setText()源码做分析:View中是通过如下代码检测当前线程是否是UI线程的,其中mThread即主线程。
这里写图片描述
View视图是通过ViewRootImpl类来绘制的,ViewRootImpl是在调用onResume()方法时创建的,而onResume()方法是在onCreate()之后调用的,在不休眠的情况下,ViewRootImpl还没有被创建出来,进而没来得及调用TextView里setText()方法后续的checkThread()方法。

你可能感兴趣的:(android,android,handler,Studio)