Android:Handler二三事(一)簡介

主要內容

  • 爲什麼要使用Handler
  • 什麼是Handler
  • 如何使用Handler
  • 在子線程中創建Handler

爲什麼要使用Handler

子線程不允許訪問UI,UI操作必須在UI線程,也就是主線程中執行。

Android UI是線程不安全的,要想在子線程中更新UI,必須通過線程間通信,可以使用Handler,AsyncTask,runonUiThread等來實現。

什麼是Handler

Handler允許發送和處理與MessageQueue相關的Message和Runnable對象,每個Handler實例都與一個線程和這個線程的MessageQueue相關聯。當你new一個Handler時,它將綁定到創建他的Thread/MessageQueue上,然後它將向MessageQueue上發送消息或Runnable對象,並在他們從MessageQueue中釋放時執行他們。

它實際上是一種消息循環處理機制。其中Handler,Looper都是在當前線程,也就是創建Handler的線程。

如何使用Handler

常用用法

新寫一個類繼承Handler

 private static class MyHandler extends Handler {
        private Context context;

        private MyHandler(Context context) {
            this.context = context;
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case 1:
                    Toast.makeText(context, "hha", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    break;
            }
        }
    }

創建Handler實例

private MyHandler handler = new MyHandler(this);

這裏需要注意的是:靜態內部類只能訪問外部類的靜態屬性和方法,非靜態內部類會持有外部類的引用,可能會造成內存泄露。如果Handler在主線程中創建,那麼它自動綁定主線程。Looper在哪個線程,Handler就在哪個線程處理消息。

模擬子線程發送消息

        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.what = 1;    // 標誌位,可以區分不同類型的消息,類比type
                message.obj = 2;     // 傳遞的對象
                handler.sendMessage(message);
            }
        }).run();

與Handler一樣,這裏Thread同樣存在可能造成內存泄漏的問題,但是偷個懶,這裏暫時不做處理。

Toast

因爲在主線程中纔可以展示Toast,爲了防止代碼中出現子線程展示Toast的情況,我們可以在工具類中對Toast做處理

    private static Handler handler;

    public static void showToast(final Context context, final String msg) {
        if (context == null || TextUtils.isEmpty(msg)) {
            return;
        }

        if (handler == null) {
            handler = new Handler(Looper.getMainLooper());
        }

        handler.post(new Runnable() {
            @Override
            public void run() {
                Toast.makeText(context, msg, LENGTH_SHORT).show();
            }
        });
    }

在子線程中創建Handler?

上面是關於主線程的Handler,也就是子線程通知主線程,那麼主線程如何通知子線程呢?

仿照子線程通知主線程,我們將子線程的Looper設置到Handler中去

private static class MyThread extends Thread {

        /**
         * 取出子線程的Looper
         */
        private Looper looper;

        public Looper getLooper() {
            return looper;
        }

        @Override
        public void run() {
            super.run();

            // 創建子線程的Looper
            Looper.prepare();
            // 取出該子線程的Looper
            looper = Looper.myLooper();
            // 只有調用下面的方法纔可以循環取用消息
            Looper.loop();
        }
    }

初始化對象

    private MyThread thread;
    private Handler handler;

調用

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

        thread = new MyThread();
        thread.start();

        handler = new Handler(thread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        handler.sendEmptyMessage(0);

    }

但是這樣會報空指針異常,爲什麼呢?因爲在OnCreate()方法中,子線程執行之後,下面的代碼也會依此執行,可能出現的情況就是Looper還沒來得及初始化,就直接在Handler中使用了,那麼就會出現空指針異常。也可能不會出現異常,但這都是隨機的。

我們可以使用這種方式在子線程中創建Handler

private static class LooperThread extends Thread {
        private Handler handler;

        public Handler getHandler() {
            return handler;
        }

        @Override
        public void run() {
            super.run();

            Looper.prepare();

            handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);

                    // 處理消息
                    Log.i("TAG","hha");
                }
            };

            Looper.loop();
        }
    }

但是需要注意的是,Handler可能爲空

HandlerThread

上面的這種方式過於麻煩,我們可以使用HandlerThread。

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

        HandlerThread handlerThread = new HandlerThread("HandlerThread");
        handlerThread.start();

        Handler handler = new Handler(handlerThread.getLooper()) {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);

                // 這裏是子線程,可以處理耗時操作
                // 處理消息
            }
        };
        handler.sendEmptyMessage(0);

    }
HandlerThread還可用於多任務下載

引用

https://www.jianshu.com/p/0a274564a4b1

https://blog.csdn.net/iispring/article/details/47115879

https://developer.android.com/reference/android/os/Handler

https://blog.csdn.net/chenxiaofeng_/article/details/51492764

https://www.cnblogs.com/lang-yu/p/6228832.html

https://blog.csdn.net/u011240877/article/details/72905631#handlerthread-%E7%AE%80%E4%BB%8B

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章