icode icode
首页
  • Android学习

    • 📁基础内容
    • 📺AndroidCore
    • 🎨Android-UI
    • 🏖️Components
    • 📊Fragment
    • 🔗网络操作
    • 🔏异步机制
    • 📦数据存储
    • 🗃️Gradle
  • 学习笔记

    • 『框架』笔记
    • 『Kotlin』笔记
    • 《Vue》笔记
    • 《Git》学习笔记
    • 『Bug踩坑记录』
  • ListView
  • RecyclerView
  • ViewPager
  • Java笔记

    • 🟠JavaSE
    • 🟢JavaWeb
    • 🔴JavaEE
    • ⚪JavaTopic
    • 🍳设计模式
  • 计算机基础

    • 📌计算机网络
    • 🔍数据结构
    • 📦数据库
    • 💻OS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 关于

    • 📫关于我
  • 收藏

    • 网站
    • 资源
    • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

iqqcode

保持对技术的探索实践与热爱
首页
  • Android学习

    • 📁基础内容
    • 📺AndroidCore
    • 🎨Android-UI
    • 🏖️Components
    • 📊Fragment
    • 🔗网络操作
    • 🔏异步机制
    • 📦数据存储
    • 🗃️Gradle
  • 学习笔记

    • 『框架』笔记
    • 『Kotlin』笔记
    • 《Vue》笔记
    • 《Git》学习笔记
    • 『Bug踩坑记录』
  • ListView
  • RecyclerView
  • ViewPager
  • Java笔记

    • 🟠JavaSE
    • 🟢JavaWeb
    • 🔴JavaEE
    • ⚪JavaTopic
    • 🍳设计模式
  • 计算机基础

    • 📌计算机网络
    • 🔍数据结构
    • 📦数据库
    • 💻OS
  • 技术文档
  • GitHub技巧
  • Nodejs
  • 博客搭建
  • 学习
  • 面试
  • 心情杂货
  • 实用技巧
  • 友情链接
  • 关于

    • 📫关于我
  • 收藏

    • 网站
    • 资源
    • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础内容

  • AndroidCore

  • Android UI

  • Components

  • Fragment

  • 网络操作

  • 异步机制

    • Handler通信

      • Android进程划分
      • Android-UI线程启动
      • Handler简介
        • 1. Handler介绍
        • 2. Handler API
        • 3. 更新UI
        • 4. 实例Demo
          • 4.1 请求网路数据
          • 4.2 数字加减变化Demo
      • Handler内部实现原理
      • Handler内存泄漏问题分析
      • AsyncTask
  • 数据存储

  • 学习笔记

  • 自定义View

  • View事件体系

  • Android
  • 异步机制
  • Handler通信
iqqcode
2021-06-17
目录

Handler简介

【知识点】

  • Handler是什么
  • 为什么要使用Handler
  • Handler / Looper / MessageQueue / Message分别是做什么的
  • Handler如何去实现
  • 工作原理
  • 如何更好的使用
  • 扩展及总结

# 1. Handler介绍

在Android开发中,我们常常会使用单独的线程来完成某些操作,比如用一个线程来完成从网络上下载图片,然后显示在一个 ImageView上,在多线程操作时,Android中必须保证以下两点:

  1. 不要阻塞UI线程
  2. 不要在UI线程之外更新UI

类比生活中的案例,工作线程(子线程)和 主线程(UI线程)是两个不同的角色,更新UI操作由主线程来负责;工作线程只能是建议主线程发生更改,主线程收到建议后做更新修改,但是它没有权利越俎代庖自己去修改更新主线程内容。

有了以上两点限制,我们在线程之间的消息如何进行传递呢?

Handler登场,Handler作为消息的处理者,主要负责:

  • 定时任务:消息调度和在将来某一时间带你执行一个Runnable
  • 线程之间消息的处理:多个任务加入到一个队列中执行

# 2. Handler API

Handler是Message的处理器,负责消息的发送和移除

即时消息和延迟消息都会立即被发送出去,只是延迟消息是延迟一定时间做处理(并不是延迟发送,是延迟处理)

  • 发送即时消息:sendMessage(Message msg)
  • 发送延迟消息:sendMessgaeDelayed(Message msg, long time)
  • 处理消息:handleMessage(Message msg) 回调方法
  • 移除还未处理的消息:removeMessage(int what)
thread(start = true) {
    Log.d(TAG,"running from sub-thread(): ${Thread.currentThread()}")
    // 子线程中发送消息,通知UI更新
    handler.sendEmptyMessage(1001) // 发送空消息
    val msg = handler.obtainMessage() // 获取消息对象
    msg.what = 2000
    msg.obj = "要存储的消息" // 任意类型
    handler.sendMessage(msg) // 发送消息
    handler.sendEmptyMessageAtTime(3000, System.currentTimeMillis() + 3000) // 在指定时间后延迟处理
    handler.sendEmptyMessageDelayed(4000,2000) // 延迟多少时间后处理
}
1
2
3
4
5
6
7
8
9
10
11

# 3. 更新UI

不能在主线程中直接更新UI

image-20210613120250497

【报错信息】:只有创建视图层次结构的原始线程才能触及它的视图

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 
1

修改

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        var rootView: View = binding.root
        setContentView(rootView)

        // 创建Handler
        val handler: Handler = object : Handler() {
            override fun handleMessage(msg: Message) {
                super.handleMessage(msg)
                Log.d(TAG,"running from UI thread(): ${Thread.currentThread()}")
                Log.d(TAG, "handleMessage: ${msg.what}")
                // 主线程接到子线程的消息,处理消息,更新UI
                if(msg.what == 1001) {
                    binding.mTextView.text = "📺 iqqcode..."
                }
            }
        }

        binding.mButton.setOnClickListener {
            thread(start = true) {
                Log.d(TAG,"running from sub-thread(): ${Thread.currentThread()}")
                // 子线程中发送消息,通知UI更新
                handler.sendEmptyMessage(1001)
                // TODO 处理耗时操作
            }
        }
    }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

不同的线程在操作

image-20210613121003127


# 4. 实例Demo

# 4.1 请求网路数据

在点击Button时,延迟请求网络数据,此时显示ProgressBar,加载完成后将数据显示到EditText中。

image-20210614110313072

View布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".HandlerTestActivity">

    <ProgressBar
        android:id="@+id/mProgressBar"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="20dp"
        android:max="100"
        android:visibility="invisible" />

    <Button
        android:id="@+id/mButton"
        android:layout_width="300dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="30dp"
        android:text="GET Submit" />


    <EditText
        android:id="@+id/mEditText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="10dp"
        android:hint="显示结果" />

</LinearLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

HandlerTestActivity:使用Handler实现异步任务

  1. 创建Handler成员变量对象, 并重写其handleMessage()
  2. 在分/主线程创建Message对象
  3. 使用handler对象发送Message
  4. 在handleMessage()中处理消息
class HandlerTestActivity : AppCompatActivity() {

    private lateinit var binding: ActivityHandlerTestBinding
    private val urlString = "http://japi.juhe.cn/qqevaluate/qq?qq=2726109782&key=e6ce0de3a1db3739d4343b9d19a9d61f" // 聚合数据API

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityHandlerTestBinding.inflate(layoutInflater)
        val rootView: View = binding.root
        setContentView(rootView)
        binding.mButton.setOnClickListener {
            asyncRequestByGet(urlString)
        }
    }

    // 1. 创建Handler成员变量对象, 并重写其handleMessage()
    private val handler = object : Handler(Looper.myLooper()!!) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            if (msg.what === 2000) {
                // 4. 在handleMessage()中处理消息
                val result = msg.obj as String
                binding.mEditText.setText(result)
                binding.mProgressBar.visibility = View.INVISIBLE
            }
        }
    }

    /**
     * 请求服务器端,得到返回的结果字符串
     * @return
     * @throws Exception
     */
    @Throws(java.lang.Exception::class)
    fun asyncRequestByGet(path: String?) {

        // 主线程, 显示提示视图(ProgressDialog/ProgressBar)
        binding.mProgressBar.visibility = View.VISIBLE;

        // OKHttp
        val client: OkHttpClient = OkHttpClient.Builder()
            .connectTimeout(8000, TimeUnit.MILLISECONDS)
            .build()

        val request: Request = Request.Builder()
            .url(path.toString())
            .get()
            .build()

        client.newCall(request).enqueue(object : Callback {
            override fun onFailure(call: Call, e: IOException) {
                Log.i("TAG", e.message!!)
            }

            override fun onResponse(call: Call, response: Response) {
                val resultData = response.body!!.string()
                // 分线程, 联网请求, 并得到响应数据
                thread(start = true) {
                    Thread.sleep(3000)
                    try {
                        Log.i("TAG", "asyncRequestByGet: ==> $resultData")
                        // 2. 在分/主线程创建Message对象
                        val message = Message.obtain()
                        message.what = 2000 //标识
                        message.obj = resultData
                        // 3. 使用handler对象发送Message
                        handler.sendMessage(message)
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                }
            }
        })
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

# 4.2 数字加减变化Demo

  1. 初始时:显示10,可以通过点击按钮改变其值
  2. 点击“自动增加”,每隔1S上面的文本数值增加1,但最大显示20并作出提示
  3. 点击“自动减少”,每隔1S上面的文本数值减少1,但最小显示1并作出提示
  4. 点击“暂停”,上面的数值文本不再变化

View布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".HandlerDemoActivity">

    <TextView
        android:id="@+id/mNumber"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="70dp"
        android:gravity="center"
        android:text="10"
        android:textSize="200sp"
        android:textStyle="bold" />

    <Button
        android:id="@+id/mIncrease"
        android:layout_width="260dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="70dp"
        android:text="自动增加" />

    <Button
        android:id="@+id/mDecrease"
        android:layout_width="260dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="10dp"
        android:text="自动减少" />

    <Button
        android:id="@+id/mPause"
        android:layout_width="260dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginTop="10dp"
        android:enabled="false"
        android:text="暂停" />

</LinearLayout>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

HandlerDemoActivity

class HandlerDemoActivity : AppCompatActivity(), View.OnClickListener {

    private lateinit var binding: ActivityHandlerDemoBinding

    private val WHAT_INCREASE = 100
    private val WHAT_DECREASE = 200
    private val RAND_COLOR = 300

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityHandlerDemoBinding.inflate(layoutInflater)
        val rootView: View = binding.root
        setContentView(rootView)

        // 使用定时器
        val timer = Timer();
        timer.schedule(object: TimerTask() {
            override fun run() {
                handler.sendEmptyMessage(RAND_COLOR)
            }
        }, 1000,1000)

        binding.mDecrease.setOnClickListener(this)
        binding.mIncrease.setOnClickListener(this)
        binding.mPause.setOnClickListener(this)
    }

    private val handler = object : Handler(Looper.myLooper()!!) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
            // 得到当前现实的数值
            var number = Integer.parseInt(binding.mNumber.text.toString())
            when (msg.what) {
                // 限制number <= 20
                WHAT_INCREASE -> {
                    if (number == 20) {
                        // 设置暂停不能操作
                        binding.mPause.isEnabled = false;
                        Toast.makeText(this@HandlerDemoActivity, "已经达到最大值", Toast.LENGTH_SHORT).show();
                        return
                    }
                    number++
                    binding.mNumber.text = number.toString()
                    // 发送增加的延迟消息
                    sendEmptyMessageDelayed(WHAT_INCREASE, 1000) // 延迟多少时间后处理
                }

                WHAT_DECREASE -> {
                    // 限制number >= 1
                    if (number == 1) {
                        //设置暂停不能操作
                        binding.mPause.isEnabled = false;
                        Toast.makeText(this@HandlerDemoActivity, "已经达到最小值", Toast.LENGTH_SHORT)
                            .show();
                        return
                    }
                    number--;
                    binding.mNumber.text = number.toString();
                    // 发送减少的延迟消息
                    sendEmptyMessageDelayed(WHAT_DECREASE, 1000);
                }
                RAND_COLOR -> {
                    // 使用rgb混合生成一种新的颜色,Color.rgb生成的是一个int数
                    binding.mNumber.setTextColor(randomTextColor())
                }
            }
        }
    }

    /**
     * 获取随机rgb颜色值
     * @return Int
     */
    private fun randomTextColor(): Int {
        val random = Random()
        // 0-190 ,如果颜色值过大,就越接近白色,就看不清了,所以需要限定范围
        val red = random.nextInt(150)
        val green = random.nextInt(150)
        val blue = random.nextInt(150)
        // 使用rgb混合生成一种新的颜色,Color.rgb生成的是一个int数
        return Color.rgb(red, green, blue)
    }

    override fun onClick(v: View?) {
        when(v?.id) {
            R.id.mIncrease -> {
                // 限制Button可操作性
                binding.mIncrease.isEnabled = false
                binding.mDecrease.isEnabled = true
                binding.mPause.isEnabled = true

                // 停止减少(移除未处理的减少的消息)
                handler.removeMessages(WHAT_DECREASE);
                // 发消息(增加)
                handler.sendEmptyMessage(WHAT_INCREASE);
            }
            R.id.mDecrease -> {
                // 限制Button可操作性
                binding.mIncrease.isEnabled = false
                binding.mDecrease.isEnabled = true
                binding.mPause.isEnabled = true

                // 停止增加(移除未处理的增加的消息)
                handler.removeMessages(WHAT_INCREASE);

                // 发消息(减少)
                handler.sendEmptyMessage(WHAT_DECREASE);
            }
            R.id.mPause -> {
                // 限制Button可操作性
                binding.mIncrease.isEnabled = false
                binding.mDecrease.isEnabled = true
                binding.mPause.isEnabled = true

                // 停止增加/减少(移除未处理的减少/增加的消息)
                handler.removeMessages(WHAT_INCREASE);
                handler.removeMessages(WHAT_DECREASE);
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
编辑 (opens new window)
#handler
上次更新: 2021/06/17, 17:05:09
Android-UI线程启动
Handler内部实现原理

← Android-UI线程启动 Handler内部实现原理→

最近更新
01
匿名内部类
10-08
02
函数式接口
10-08
03
ARouter-Kotlin踩坑
10-05
更多文章>
Theme by Vdoing | Copyright © 2021-2023 iqqcode | MIT License | 备案号-京ICP备2021028793号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×