• Handler的交互场景


    我们在开发中经常用到的在子线程中执行耗时任务,然后通过Handler到主线程中更新UI。我们对它都很熟悉,今天再来复习一下这个Handler。下面几种方式是今天要复习的

     (1)单向的子线程与子线程交互:即由一个子线程向另一个子线程发送消息;

    需要先创建一个新的子线程,然后在子线程内创建Handler,来接收另一个子线程发来的消息。在子线程中创建Handler必须要先获取Looper,否则程序则会崩溃,并且会报错误:Can't create handler inside thread that has not called Looper.prepare()。Handler消息机制源码详解参考这篇文章:https://www.jianshu.com/p/750e3706c467

    1. /*
    2. (1)子线程与子线程交互(单向)。
    3. 创建一个新的子线程
    4. */
    5. class MyThread1 extends Thread{
    6. public Handler mHandler1;
    7. @Override
    8. public void run() {
    9. super.run();
    10. //1.实例化当前线程的Looper,每个线程只能有一个Looper实例。
    11. Looper.prepare();
    12. //2.创建Handler
    13. mHandler1 = new Handler(){
    14. @Override
    15. public void handleMessage(@NonNull Message msg) {
    16. super.handleMessage(msg);
    17. switch (msg.what){
    18. case 0:
    19. runOnUiThread(new Runnable() {
    20. @Override
    21. public void run() {
    22. tv_receivedMsg.setText("(1)子线程中使用Handler向另一个子线程发消息(单向)");
    23. }
    24. });
    25. break;
    26. }
    27. }
    28. };
    29. //3.开启Looper循环接收消息
    30. Looper.loop();
    31. }
    32. }

    之后由一个线程向这个新线程发送消息

    1. private MyThread1 myThread1;
    2. myThread1 = new MyThread1();
    3. myThread1.start();
    4. new Thread(){
    5. @Override
    6. public void run() {
    7. super.run();
    8. myThread1.mHandler1.sendEmptyMessage(0);
    9. }
    10. }.start();

    (2)两个子线程互发消息(双向):需要创建两个线程并相互发送消息

    1. /*
    2. (2)两个子线程互发消息(双向)
    3. 即创建两个线程,并在一个线程发送消息到另一个线程处理
    4. */
    5. class MyThread21 extends Thread{
    6. public Handler mHandler21;
    7. @Override
    8. public void run() {
    9. super.run();
    10. //1.实例化当前线程的Looper,每个线程只能有一个Looper实例。
    11. Looper.prepare();
    12. //2.创建Handler
    13. mHandler21 = new Handler(){
    14. @Override
    15. public void handleMessage(@NonNull Message msg) {
    16. super.handleMessage(msg);
    17. switch (msg.what){
    18. case 20:
    19. runOnUiThread(new Runnable() {
    20. @Override
    21. public void run() {
    22. tv_receivedMsg.setText("收到");
    23. //收到消息,使用MyThread22的mHandler22向MyThread22发送消息
    24. myThread22.mHandler22.sendEmptyMessage(22);
    25. }
    26. });
    27. break;
    28. case 21:
    29. runOnUiThread(new Runnable() {
    30. @Override
    31. public void run() {
    32. try {
    33. Thread.sleep(2000);
    34. //收到MyThread22发来的消息
    35. tv_receivedMsg.setText("(2)两个子线程互发消息(双向)\n 线程1收到MyThread22发来的消息");
    36. } catch (InterruptedException e) {
    37. e.printStackTrace();
    38. }
    39. }
    40. });
    41. break;
    42. }
    43. }
    44. };
    45. //3.开启Looper循环接收消息
    46. Looper.loop();
    47. }
    48. }
    49. class MyThread22 extends Thread{
    50. public Handler mHandler22;
    51. @Override
    52. public void run() {
    53. super.run();
    54. //1.实例化当前线程的Looper,每个线程只能有一个Looper实例。
    55. Looper.prepare();
    56. //2.创建Handler
    57. mHandler22 = new Handler(){
    58. @Override
    59. public void handleMessage(@NonNull Message msg) {
    60. super.handleMessage(msg);
    61. switch (msg.what){
    62. case 22:
    63. runOnUiThread(new Runnable() {
    64. @Override
    65. public void run() {
    66. tv_receivedMsg.setText("(2)两个子线程互发消息(双向)\n 线程2收到myThread21发来的消息");
    67. try {
    68. Thread.sleep(2000);
    69. //收到myThread21发来的消息,使用myThread21的mHandler21向myThread21发消息
    70. myThread21.mHandler21.sendEmptyMessage(21);
    71. } catch (InterruptedException e) {
    72. e.printStackTrace();
    73. }
    74. }
    75. });
    76. break;
    77. }
    78. }
    79. };
    80. //3.开启Looper循环接收消息
    81. Looper.loop();
    82. }
    83. }

    执行这两线程互相发送消息

    1. private MyThread21 myThread21;
    2. private MyThread22 myThread22;
    3. myThread21 = new MyThread21();
    4. myThread22 = new MyThread22();
    5. myThread21.start();
    6. myThread22.start();
    7. myThread21.mHandler21.sendEmptyMessage(20);

    (3)主线程发送消息到子线程:先创建一个子线程

    1. //(3)主线程发消息到子线程
    2. class MyThread3 extends Thread{
    3. public Handler mHandler3;
    4. @Override
    5. public void run() {
    6. super.run();
    7. //1.实例化当前线程的Looper,每个线程只能有一个Looper实例。
    8. Looper.prepare();
    9. //2.创建Handler
    10. mHandler3 = new Handler(){
    11. @Override
    12. public void handleMessage(@NonNull Message msg) {
    13. super.handleMessage(msg);
    14. switch (msg.what){
    15. case 0:
    16. runOnUiThread(new Runnable() {
    17. @Override
    18. public void run() {
    19. tv_receivedMsg.setText("(3)子线程收到主线程发来的消息");
    20. }
    21. });
    22. break;
    23. }
    24. }
    25. };
    26. //3.开启Looper循环接收消息
    27. Looper.loop();
    28. }
    29. }

    然后由主线程向新建的子线程发送消息

    1. private MyThread3 myThread3;
    2. myThread3 = new MyThread3();
    3. myThread3.start();
    4. myThread3.mHandler3.sendEmptyMessage(0);

    (4)子线程发送消息主线程:这个也是我们平时最常用的

    1. //子线程发送消息主线程
    2. new Thread(new Runnable() {
    3. @Override
    4. public void run() {
    5. mHandler.sendEmptyMessage(4);
    6. }
    7. }).start();

    在主线程中创建Handler来接收消息

    1. Handler mHandler = new Handler(){
    2. @Override
    3. public void handleMessage(@NonNull Message msg) {
    4. super.handleMessage(msg);
    5. switch (msg.what){
    6. case 4:
    7. tv_receivedMsg.setText("主线程收到子线程发来的消息");
    8. break;
    9. }
    10. }
    11. };

    (5)附加一个handler.post的使用,可直接在run里面更新UI。Handler的sendMessage和post的区别可参考这篇文章:https://www.jianshu.com/p/43d6cd7b06f1

    1. mHandler.post(new Runnable() {
    2. @Override
    3. public void run() {
    4. tv_receivedMsg.setText("handler.post通过实现Runnable接口,直接更新UI");
    5. }
    6. });

    整个复习测试的完整源程序

    1. public class HandlerTestActivity extends AppCompatActivity {
    2. View viewStatus;
    3. private Button btn_handler1,btn_handler2,btn_handler3,btn_handler4;
    4. private Button btn_handlerPost;
    5. private TextView tv_receivedMsg;
    6. private MyThread1 myThread1;
    7. private MyThread21 myThread21;
    8. private MyThread22 myThread22;
    9. private MyThread3 myThread3;
    10. Handler mHandler = new Handler(){
    11. @Override
    12. public void handleMessage(@NonNull Message msg) {
    13. super.handleMessage(msg);
    14. switch (msg.what){
    15. case 4:
    16. tv_receivedMsg.setText("主线程收到子线程发来的消息");
    17. break;
    18. }
    19. }
    20. };
    21. @Override
    22. protected void onCreate(@Nullable Bundle savedInstanceState) {
    23. super.onCreate(savedInstanceState);
    24. setContentView(R.layout.activity_handler_test);
    25. initView();
    26. initData();
    27. initListener();
    28. }
    29. private void initView(){
    30. if (Build.VERSION.SDK_INT >= 21) {
    31. View decorView =getWindow().getDecorView();
    32. int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
    33. | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
    34. decorView.setSystemUiVisibility(option);
    35. //这上面是颜色
    36. getWindow().setStatusBarColor(Color.TRANSPARENT); //也可以设置成灰色透明的,比较符合Material Design的风格
    37. }
    38. viewStatus = findViewById(R.id.view_status);
    39. ViewGroup.LayoutParams layoutParams = viewStatus.getLayoutParams();
    40. layoutParams.height = getStatusBarHeight();
    41. btn_handler1 = findViewById(R.id.btn_handler1);
    42. btn_handler2 = findViewById(R.id.btn_handler2);
    43. btn_handler3 = findViewById(R.id.btn_handler3);
    44. btn_handler4 = findViewById(R.id.btn_handler4);
    45. btn_handlerPost = findViewById(R.id.btn_handlerPost);
    46. tv_receivedMsg = findViewById(R.id.tv_receivedMsg);
    47. }
    48. private void initData(){
    49. /**
    50. * (1)子线程与子线程交互(单向)。
    51. */
    52. myThread1 = new MyThread1();
    53. myThread1.start();
    54. /**
    55. * (2)两个子线程互发消息(双向)
    56. */
    57. myThread21 = new MyThread21();
    58. myThread22 = new MyThread22();
    59. myThread21.start();
    60. myThread22.start();
    61. /**
    62. * (3)主线程发送消息到子线程。
    63. */
    64. myThread3 = new MyThread3();
    65. myThread3.start();
    66. }
    67. private void initListener(){
    68. //子线程中使用Handler向另一个子线程发消息(单向)
    69. btn_handler1.setOnClickListener(new View.OnClickListener() {
    70. @Override
    71. public void onClick(View v) {
    72. new Thread(){
    73. @Override
    74. public void run() {
    75. super.run();
    76. myThread1.mHandler1.sendEmptyMessage(0);
    77. }
    78. }.start();
    79. }
    80. });
    81. btn_handler2.setOnClickListener(new View.OnClickListener() {
    82. @Override
    83. public void onClick(View v) {
    84. myThread21.mHandler21.sendEmptyMessage(20);
    85. }
    86. });
    87. btn_handler3.setOnClickListener(new View.OnClickListener() {
    88. @Override
    89. public void onClick(View v) {
    90. myThread3.mHandler3.sendEmptyMessage(0);
    91. }
    92. });
    93. btn_handler4.setOnClickListener(new View.OnClickListener() {
    94. @Override
    95. public void onClick(View v) {
    96. //子线程发送消息主线程
    97. new Thread(new Runnable() {
    98. @Override
    99. public void run() {
    100. mHandler.sendEmptyMessage(4);
    101. }
    102. }).start();
    103. }
    104. });
    105. //Handler.post的使用
    106. btn_handlerPost.setOnClickListener(new View.OnClickListener() {
    107. @Override
    108. public void onClick(View v) {
    109. mHandler.post(new Runnable() {
    110. @Override
    111. public void run() {
    112. tv_receivedMsg.setText("handler.post通过实现Runnable接口,直接更新UI");
    113. }
    114. });
    115. }
    116. });
    117. }
    118. /*
    119. (1)子线程与子线程交互(单向)。
    120. 创建一个新的子线程
    121. */
    122. class MyThread1 extends Thread{
    123. public Handler mHandler1;
    124. @Override
    125. public void run() {
    126. super.run();
    127. //1.实例化当前线程的Looper,每个线程只能有一个Looper实例。
    128. Looper.prepare();
    129. //2.创建Handler
    130. mHandler1 = new Handler(){
    131. @Override
    132. public void handleMessage(@NonNull Message msg) {
    133. super.handleMessage(msg);
    134. switch (msg.what){
    135. case 0:
    136. runOnUiThread(new Runnable() {
    137. @Override
    138. public void run() {
    139. tv_receivedMsg.setText("(1)子线程中使用Handler向另一个子线程发消息(单向)");
    140. }
    141. });
    142. break;
    143. }
    144. }
    145. };
    146. //3.开启Looper循环接收消息
    147. Looper.loop();
    148. }
    149. }
    150. /*
    151. (2)两个子线程互发消息(双向)
    152. 即创建两个线程,并在一个线程发送消息到另一个线程处理
    153. */
    154. class MyThread21 extends Thread{
    155. public Handler mHandler21;
    156. @Override
    157. public void run() {
    158. super.run();
    159. //1.实例化当前线程的Looper,每个线程只能有一个Looper实例。
    160. Looper.prepare();
    161. //2.创建Handler
    162. mHandler21 = new Handler(){
    163. @Override
    164. public void handleMessage(@NonNull Message msg) {
    165. super.handleMessage(msg);
    166. switch (msg.what){
    167. case 20:
    168. runOnUiThread(new Runnable() {
    169. @Override
    170. public void run() {
    171. tv_receivedMsg.setText("收到");
    172. //收到消息,使用MyThread22的mHandler22向MyThread22发送消息
    173. myThread22.mHandler22.sendEmptyMessage(22);
    174. }
    175. });
    176. break;
    177. case 21:
    178. runOnUiThread(new Runnable() {
    179. @Override
    180. public void run() {
    181. try {
    182. Thread.sleep(2000);
    183. //收到MyThread22发来的消息
    184. tv_receivedMsg.setText("(2)两个子线程互发消息(双向)\n 线程1收到MyThread22发来的消息");
    185. } catch (InterruptedException e) {
    186. e.printStackTrace();
    187. }
    188. }
    189. });
    190. break;
    191. }
    192. }
    193. };
    194. //3.开启Looper循环接收消息
    195. Looper.loop();
    196. }
    197. }
    198. class MyThread22 extends Thread{
    199. public Handler mHandler22;
    200. @Override
    201. public void run() {
    202. super.run();
    203. //1.实例化当前线程的Looper,每个线程只能有一个Looper实例。
    204. Looper.prepare();
    205. //2.创建Handler
    206. mHandler22 = new Handler(){
    207. @Override
    208. public void handleMessage(@NonNull Message msg) {
    209. super.handleMessage(msg);
    210. switch (msg.what){
    211. case 22:
    212. runOnUiThread(new Runnable() {
    213. @Override
    214. public void run() {
    215. tv_receivedMsg.setText("(2)两个子线程互发消息(双向)\n 线程2收到myThread21发来的消息");
    216. try {
    217. Thread.sleep(2000);
    218. //收到myThread21发来的消息,使用myThread21的mHandler21向myThread21发消息
    219. myThread21.mHandler21.sendEmptyMessage(21);
    220. } catch (InterruptedException e) {
    221. e.printStackTrace();
    222. }
    223. }
    224. });
    225. break;
    226. }
    227. }
    228. };
    229. //3.开启Looper循环接收消息
    230. Looper.loop();
    231. }
    232. }
    233. //(3)主线程发消息到子线程
    234. class MyThread3 extends Thread{
    235. public Handler mHandler3;
    236. @Override
    237. public void run() {
    238. super.run();
    239. //1.实例化当前线程的Looper,每个线程只能有一个Looper实例。
    240. Looper.prepare();
    241. //2.创建Handler
    242. mHandler3 = new Handler(){
    243. @Override
    244. public void handleMessage(@NonNull Message msg) {
    245. super.handleMessage(msg);
    246. switch (msg.what){
    247. case 0:
    248. runOnUiThread(new Runnable() {
    249. @Override
    250. public void run() {
    251. tv_receivedMsg.setText("(3)子线程收到主线程发来的消息");
    252. }
    253. });
    254. break;
    255. }
    256. }
    257. };
    258. //3.开启Looper循环接收消息
    259. Looper.loop();
    260. }
    261. }
    262. @Override
    263. protected void onDestroy() {
    264. if (null != myThread1.mHandler1){
    265. myThread1.mHandler1.removeCallbacksAndMessages(null);
    266. }
    267. if (null != myThread21.mHandler21){
    268. myThread21.mHandler21.removeCallbacksAndMessages(null);
    269. }
    270. if (null != myThread22.mHandler22){
    271. myThread22.mHandler22.removeCallbacksAndMessages(null);
    272. }
    273. if (null != myThread3.mHandler3){
    274. myThread3.mHandler3.removeCallbacksAndMessages(null);
    275. }
    276. super.onDestroy();
    277. }
    278. }

    布局文件

    1. "1.0" encoding="utf-8"?>
    2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    3. android:layout_width="match_parent"
    4. android:layout_height="match_parent"
    5. android:background="@color/background"
    6. android:orientation="vertical">
    7. <View
    8. android:id="@+id/view_status"
    9. android:layout_width="match_parent"
    10. android:layout_height="0dp"
    11. android:background="#01012A" />
    12. <TextView
    13. android:layout_width="match_parent"
    14. android:layout_height="40dp"
    15. android:text="Handler的交互场景"
    16. android:background="@color/colorPrimary"
    17. android:gravity="center"
    18. android:textSize="18dp"
    19. android:textColor="@color/white"/>
    20. <Button
    21. android:id="@+id/btn_handler1"
    22. android:layout_width="wrap_content"
    23. android:layout_height="wrap_content"
    24. android:text="(1)子线程与子线程交互(单向)"/>
    25. <Button
    26. android:id="@+id/btn_handler2"
    27. android:layout_width="wrap_content"
    28. android:layout_height="wrap_content"
    29. android:layout_marginTop="10dp"
    30. android:text="(2)两个子线程互发消息(双向)"/>
    31. <Button
    32. android:id="@+id/btn_handler3"
    33. android:layout_width="wrap_content"
    34. android:layout_height="wrap_content"
    35. android:layout_marginTop="10dp"
    36. android:text="(3)主线程发送消息到子线程"/>
    37. <Button
    38. android:id="@+id/btn_handler4"
    39. android:layout_width="wrap_content"
    40. android:layout_height="wrap_content"
    41. android:layout_marginTop="10dp"
    42. android:text="(4)子线程发送消息主线程"/>
    43. <Button
    44. android:id="@+id/btn_handlerPost"
    45. android:layout_width="wrap_content"
    46. android:layout_height="wrap_content"
    47. android:layout_marginTop="10dp"
    48. android:text="(5)handlerPost方法"
    49. android:textAllCaps="false"/>
    50. <TextView
    51. android:layout_width="wrap_content"
    52. android:layout_height="wrap_content"
    53. android:text="接收消息:"
    54. android:textSize="18dp"
    55. android:textColor="@color/white"/>
    56. <TextView
    57. android:id="@+id/tv_receivedMsg"
    58. android:layout_width="wrap_content"
    59. android:layout_height="wrap_content"
    60. android:textSize="16dp"
    61. android:textColor="@color/blueP"/>
    62. LinearLayout>

  • 相关阅读:
    RIP动态路由协议详解
    关于单机流程编排技术——docker compose安装使用的问题
    【CFD小工坊】浅水模型的边界条件
    突然想散步
    关于 async 和 await 两个关键字(C#)【并发编程系列_5】
    面试经典150题——矩阵置零
    pikach靶场暴力破解
    JSR303数据校验方法
    TMUX终端复用工具小解
    自动切换背景的登录页面
  • 原文地址:https://blog.csdn.net/u013184970/article/details/127632801