美文网首页
Android的Handler机制

Android的Handler机制

作者: 赵宇_阿特奇 | 来源:发表于2023-10-29 13:23 被阅读0次

    Android的Handler机制是一种用于处理和调度线程之间消息传递的机制,通常用于在后台线程中执行任务,并将结果返回到主线程中更新UI。Handler机制的核心是Message和MessageQueue,以及Looper。

    以下是Android Handler机制的主要组成部分和工作原理:

    1.Message(消息):Message是一个包含要传递的数据和指令的对象。它可以携带整数、字符串、Bundle等不同类型的数据。当需要在不同线程之间传递数据或执行任务时,通常会创建一个Message并将其发送给Handler。

    2.Handler(处理程序):Handler是用于处理Message的对象。它通常与一个特定的线程(通常是主线程)关联。通过Handler,您可以将Message发送到与其关联的线程的消息队列中,以便在那个线程中执行处理。

    3.Looper(消息循环器):Looper是一个用于管理线程的消息队列的对象。每个线程都可以有一个Looper,它会在线程上创建一个消息队列,允许该线程接收并处理Message。主线程通常已经具有一个默认的Looper,而后台线程需要显式创建一个Looper。

    4.MessageQueue(消息队列):MessageQueue是一个FIFO(先进先出)队列,用于存储待处理的Message。每个Looper都有一个关联的MessageQueue,Handler将Message发送到这个队列中,然后由Looper依次处理队列中的Message。

    Handler机制的工作流程:

    1.在主线程(或其他线程)上创建一个Handler对象,这个Handler会关联到当前线程的Looper。

    2.在后台线程中,创建一个Message对象,可以将一些数据和处理指令放入这个Message。

    3.使用Handler的sendMessage方法将Message发送到与Handler关联的Looper的MessageQueue中。

    4.Looper在后台线程中不断轮询MessageQueue,当有新的Message到达时,将Message取出并交给Handler处理。

    5.Handler收到Message后,可以根据Message中的指令执行相应的操作,通常是在主线程中更新UI。

    6.如果需要定时任务或循环执行,可以使用Handler的postDelayed方法。

    Handler 的三种使用方法,分别是:

    Handler.sendMessage(Message)
    Handler.post(Runnable)
    Handler.obtainMessage(what).sendToTarget();

    2. Handler.sendMessage()方法

    Handler.sendMessage(Msg) 方法是最为常见的一种方法。

    2.1 使用步骤说明

    其使用步骤分四步,如下所示:
    1、步骤一:新建 Handler 对象,覆写 handleMessage(Message) 方法。
    2、步骤二:新建 Message 对象,设置其携带的数据。
    3、步骤三:在子线程中通过 Handler.sendMessage(Message) 方法发送信息。
    4、步骤四:在 Handler 的 handleMessage(Message msg) 方法中处理消息,通知主线程作出相对应的 UI 工作。

    步骤一:新建 Handler 对象,覆写 handleMessage(Message) 方法
    //创建 Handler对象,并关联主线程消息队列
    mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
                ···略···
            }
        }
    };
    
    步骤二:新建 Message 对象,设置其携带的数据
    Bundle bundle = new Bundle();
    bundle.putInt(CURRENT_PROCESS_KEY, i);
    Message msg = new Message();
    msg.setData(bundle);
    msg.what = 2;
    
    步骤三:在子线程中通过 Handler.sendMessage(Message) 方法发送信息
    mHandler.sendMessage(msg)
    
    步骤四:在 Handler 的 handleMessage(Message msg) 方法中处理消息,通知主线程作出相对应的 UI 工作
    mHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //根据信息编码及数据做出相对应的处理
            switch (msg.what) {
                case 1:
                    //更新 TextView UI
                    mDisplayTv.setText("CustomChildThread starting!");
                    break;
                case 2:
                    //获取 ProgressBar 的进度,然后显示进度值
                    Bundle bundle = msg.getData();
                    int process = bundle.getInt(CURRENT_PROCESS_KEY);
                    mProgressBar.setProgress(process);
                    break;
                default:
                    break;
            }
        }
    };
    
    2.2.1Java版本的具体代码如下所示:
    public class HandlerAddThreadActivity extends AppCompatActivity {
        public static final String CURRENT_PROCESS_KEY = "CURRENT_PROCESS";
        private TextView mDisplayTv;
        private Handler mHandler;
        private ProgressBar mProgressBar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_handler_add_thread);
    
            TextView titleTv = findViewById(R.id.title_tv);
            titleTv.setText("Handler + Thread");
            
            mDisplayTv = findViewById(R.id.display_tv);
            mProgressBar = findViewById(R.id.test_handler_progress_bar);
    
            //mHandler用于处理主线程消息队列中的子线程消息
            mHandler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what) {
                        case 1:
                            //更新 TextView UI
                            mDisplayTv.setText("CustomChildThread starting!");
                            break;
                        case 2:
                            //获取 ProgressBar 的进度,然后显示进度值
                            Bundle bundle = msg.getData();
                            int process = bundle.getInt(CURRENT_PROCESS_KEY);
                            mProgressBar.setProgress(process);
                            break;
                        default:
                            break;
                    }
    
                }
            };
            
            Button mClickBtn = findViewById(R.id.click_btn);
            mClickBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //开启子线程,子线程处理UI工作
                    CustomChildThread customThread = new CustomChildThread();
                    customThread.start();
                }
            });
        }
    
        /**
         * 子线程,用于处理耗时工作
         */
        public class CustomChildThread extends Thread {
    
            @Override
            public void run() {
                //在子线程中创建一个消息对象
                Message childThreadMessage = new Message();
                childThreadMessage.what = 1;
                //将该消息放入主线程的消息队列中
                mHandler.sendMessage(childThreadMessage);
    
                //模拟耗时进度,将进度值传给主线程用于更新 ProgressBar 进度。
                for (int i = 1; i <= 5; i++) {
                    try {
                        //让当前执行的线程(即 CustomChildThread)睡眠 1s
                        Thread.sleep(1000);
    
                        //Message 传递参数
                        Bundle bundle = new Bundle();
                        bundle.putInt(CURRENT_PROCESS_KEY, i);
                        Message progressBarProcessMsg = new Message();
                        progressBarProcessMsg.setData(bundle);
                        progressBarProcessMsg.what = 2;
                        mHandler.sendMessage(progressBarProcessMsg);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    2.2.2Kotlin版本的代码如下所示:
    class TestThreadAddHandlerActivity : AppCompatActivity() {
        companion object {
            const val PROGRESS_VALUE_KEY = "PROGRESS_VALUE"
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_thread_add_handler)
    
            handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener {
                //工作线程开始模拟下载任务
                val workThread: WorkThread = WorkThread(this)
                workThread.start()
            })
        }
    
        class WorkThread(activity: TestThreadAddHandlerActivity) : Thread() {
            private var handler: MyHandler = MyHandler(activity)
    
            override fun run() {
                super.run()
                for (i in 0..6) {
                    sleep(1000)
                    //通过 Handler 将进度参数传递给 主线程,让其更新 progressBar 进度
                    val message = Message()
                    message.what = 1
                    val bundle = Bundle()
                    bundle.putInt(PROGRESS_VALUE_KEY, i)
                    message.data = bundle
                    handler.sendMessage(message)
                }
            }
        }
    
        /**
         * 静态内部类,防止内存泄漏
         */
        class MyHandler(activity: TestThreadAddHandlerActivity) : Handler() {
            private var weakReference = WeakReference(activity)
    
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                //处理消息
                when (msg.what) {
                    1 -> {
                        val activity = weakReference.get()
                        if (activity != null && !activity.isFinishing) {
                            //获取消息中携带的任务处理进度参数,然后设置成 ProgressBar 的进度。
                            val progressValue: Int = msg.data.get(PROGRESS_VALUE_KEY) as Int
                            activity.handlerAddThreadProgressBar.progress = progressValue
                        }
                    }
                }
            }
        }
    }
    

    3. Handler.post()方法

    除了使用 Handler.sendMessage(Message) 来发送信息,Handler 还支持 post(Runnable) 方法来传递消息,通知主线程做出相对应的 UI 工作。使用方法如下:

    /**
     * 将可运行的 Runnable 添加到消息队列。Runnable 将在该 Handler 相关的线程上运行处理。
     * The runnable will be run on the thread to which this handler is attached.
     */
    new Handler().post(new Runnable() {
        @Override
        public void run() {
            //更新处理 UI 工作
        }
    });
    
    3.1.1Java版本的具体代码如下
    public class HandlerPostFunctionActivity extends AppCompatActivity {
        private Handler mMainHandler;
        private ProgressBar mProgressBar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_handler_add_thread);
    
            TextView titleTv = findViewById(R.id.title_tv);
            titleTv.setText("Handler post() function");
    
            mProgressBar = findViewById(R.id.test_handler_progress_bar);
    
            //新建静态内部类 Handler 对象
            mMainHandler = new Handler(getMainLooper());
    
            Button mClickBtn = findViewById(R.id.click_btn);
            mClickBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //开启子线程,子线程处理UI工作
                    CustomChildThread customThread = new CustomChildThread();
                    customThread.start();
                }
            });
        }
    
        /**
         * 子线程,用于处理耗时工作
         */
        public class CustomChildThread extends Thread {
    
            @Override
            public void run() {
                //模拟耗时进度,将进度值传给主线程用于更新 ProgressBar 进度。
                for (int i = 1; i <= 5; i++) {
                    try {
                        //让当前执行的线程(即 CustomChildThread)睡眠 1s
                        Thread.sleep(1000);
    
                        //新创建一个 Runnable 用户处理 UI 工作
                        MyRunnable runnable = new MyRunnable(HandlerPostFunctionActivity.this, i);
                        //调用Handler post 方法。
                        mMainHandler.post(runnable);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    
        /**
         * 将 Runnable 写成静态内部类,防止内存泄露
         */
        public static class MyRunnable implements Runnable {
            private int progressBarValue;
            private WeakReference<HandlerPostFunctionActivity> weakReference;
    
            MyRunnable(HandlerPostFunctionActivity activity, int value) {
                this.weakReference = new WeakReference<>(activity);
                this.progressBarValue = value;
            }
    
            @Override
            public void run() {
                HandlerPostFunctionActivity activity = weakReference.get();
                if (activity != null && !activity.isFinishing()) {
                    activity.mProgressBar.setProgress(progressBarValue);
                }
            }
        }
    }
    
    3.1.2Kotlin版本的具体代码如下:
    class TestHandlerPostRunnableActivity : AppCompatActivity() {
        private var mMainHandler: Handler? = null
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_thread_add_handler)
    
            handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener {
                //工作线程开始模拟下载任务
                val workThread: WorkThread = WorkThread(this)
                workThread.start()
            })
    
            //创建 Handler,关联App的 主Looper 对象
            mMainHandler = Handler(Looper.getMainLooper())
        }
    
        class WorkThread(private var activity: TestHandlerPostRunnableActivity) : Thread() {
            private var handler: Handler? = activity.mMainHandler
    
            override fun run() {
                super.run()
                for (i in 0..6) {
                    sleep(1000)
                    //新建 Runnable 设置进度参数传,然后通过 post(Runnable) 方法,让其更新 progressBar 进度
                    val runnable: MyRunnable = MyRunnable(activity, i)
                    handler?.post(runnable)
                }
            }
        }
    
        /**
         * 处理 UI 工作。
         * 静态内部类,防止内存泄露
         */
        class MyRunnable(activity: TestHandlerPostRunnableActivity, value: Int) : Runnable {
            private var weakReference = WeakReference(activity)
            private var progressValue = value
    
            override fun run() {
                val activity = weakReference.get()
                if (activity != null && !activity.isFinishing) {
                    //获取任务执行进度参数,更新 progressBar 进度
                    activity.handlerAddThreadProgressBar.progress = progressValue
                }
            }
        }
    }
    

    4. obtainMessage()方法

    obtainMessage() 方法与 sendMessage() 方法很相似,通过 mHandler.obtainMessage().sendToTarget() 发送信息。该方法与 sendMessage() 的区别就是你不用额外去创建一个 Message 对象。

    obtainMessage() 方法有三种,分别是:

    //指定 what 用于区分,通过 Message.what 获得
    public final Message obtainMessage(int what);
    
    //传递obj参数,通过 Message.obj 获得
    public final Message obtainMessage(int what, @Nullable Object obj)
    
    //传递arg1 arg2参数,通过 Message.arg1 Message.arg2 获得
    public final Message obtainMessage(int what, int arg1, int arg2)
    
    4.1.1ava版本的具体代码如下:
    public class HandlerObtainMessageActivity extends AppCompatActivity {
        private TextView mDisplayTv;
        private Handler mHandler;
        private ProgressBar mProgressBar;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_handler_add_thread);
    
            TextView titleTv = findViewById(R.id.title_tv);
            titleTv.setText("Handler + Thread");
    
            mDisplayTv = findViewById(R.id.display_tv);
            mProgressBar = findViewById(R.id.test_handler_progress_bar);
    
            //mHandler用于处理主线程消息队列中的子线程消息
            mHandler = new Handler(Looper.getMainLooper()) {
                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    switch (msg.what) {
                        case 1:
                            //更新 TextView UI
                            mDisplayTv.setText("Handler obtainMessage() Test!!");
                            break;
                        case 2:
                            //通过 msg.obj 获取 ProgressBar 的进度,然后显示进度值
                            int process = (int) msg.obj;
                            mProgressBar.setProgress(process);
                            break;
                        default:
                            break;
                    }
    
                }
            };
    
            Button mClickBtn = findViewById(R.id.click_btn);
            mClickBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //开启子线程,子线程处理UI工作
                    CustomChildThread customThread = new CustomChildThread();
                    customThread.start();
                }
            });
        }
    
        /**
         * 子线程,用于处理耗时工作
         */
        public class CustomChildThread extends Thread {
    
            @Override
            public void run() {
                //发送第一条消息,代表开始执行异步任务
                mHandler.obtainMessage(1).sendToTarget();
    
                //模拟耗时进度,将进度值传给主线程用于更新 ProgressBar 进度。
                for (int i = 1; i <= 5; i++) {
                    try {
                        //让当前执行的线程(即 CustomChildThread)睡眠 1s
                        Thread.sleep(1000);
    
                        //将执行进度参数 i 传递给主线程 progressBar
                        mHandler.obtainMessage(2, i).sendToTarget();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
    
    
            }
        }
    
    }
    
    4.1.2Kotlin版本的具体代码如下:
    class TestHandlerObtainMessageActivity : AppCompatActivity() {
        companion object {
            const val PROGRESS_VALUE_KEY = "PROGRESS_VALUE"
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_thread_add_handler)
    
            handlerAddThreadStartBtn.setOnClickListener(View.OnClickListener {
                //工作线程开始模拟下载任务
                val workThread: WorkThread = WorkThread(this)
                workThread.start()
            })
        }
    
        class WorkThread(activity: TestHandlerObtainMessageActivity) : Thread() {
            private var handler: MyHandler = MyHandler(activity)
    
            override fun run() {
                super.run()
                for (i in 0..6) {
                    sleep(1000)
                    //通过 Handler 将进度参数传递给 主线程,让其更新 progressBar 进度
                    val bundle = Bundle()
                    bundle.putInt(PROGRESS_VALUE_KEY, i)
                    handler.obtainMessage(1, bundle).sendToTarget()
                }
            }
        }
    
        /**
         * 静态内部类,防止内存泄漏
         */
        class MyHandler(activity: TestHandlerObtainMessageActivity) : Handler() {
            private var weakReference = WeakReference(activity)
    
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                when (msg.what) {
                    1 -> {
                        val activity = weakReference.get()
                        if (activity != null && !activity.isFinishing) {
                            //获取任务执行进度参数,然后通过 ProgressBar 显示出来
                            val bundle: Bundle = msg.obj as Bundle
                            val progressValue: Int = bundle.get(PROGRESS_VALUE_KEY) as Int
                            activity.handlerAddThreadProgressBar.progress = progressValue
                        }
                    }
                }
            }
        }
    }
    

    5. 总结:

    在实际开发中,三种方法的使用都可行,具体用哪种方法,需结合你的实际情况及个人喜好。另外,在实际使用中往往将 Handler 写成静态内部类,这时需要注意防止内存泄露!(The handler class should be static or leaks might occur),具体代码见上方!

    5.1 在子线程中创建Handler

    思考: 在上面代码中, 我们都是在主线程中创建了 Handler 对象,那如果在子线程中创建一个 Handler 对象呢?会发生什么呢?
    如下所示:我们在 CustomChildThread 线程中,新建一个 Handler 对象。

    public class CustomChildThread extends Thread {
        @Override
        public void run() {
            Handler handler = new Handler(Activity.this);
            //会报错:java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        }
    }
    

    结果: 抛出异常: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()。
    原因: 因为在创建 Handler对象时要关联所处线程的 Looper对象,而我们的子线程没有 Looper,所以会抛出上述异常。
    解决方法: 通过调用,Looper.prepare() 方法为子线程创建一个 Looper 对象,并且调用 Looper.loop() 方法开始消息循环。如下所示:

    class CustomChildThread extends Thread {
        @Override
        public void run() {
            //为当前线程创建一个 Looper 对象
            Looper.prepare();
            
            //在子线程中创建一个 Handler 对象
            Handler handler = new Handler() {
                public void handleMessage(Message msg) {
                    // 在这里处理传入的消息
                }
            };
            //开始消息循环
            Looper.loop();
        }
    }
    

    相关文章

      网友评论

          本文标题:Android的Handler机制

          本文链接:https://www.haomeiwen.com/subject/takvidtx.html