小言_互联网的博客

了解Android核心:Looper,Handler和HandlerThread

372人阅读  评论(0)

 

Android中的主线程由 looper 和 Handlers 组成。所以了解创建无阻碍的响应式 UI 很重要。

 

  1. MessageQueue 是一个队列,其中包含消息任务。
  2. Handler 在 MessageQueue 中以任务形式排队,Looper 在任务出现时执行它们 MessageQueue.
  3. Looper 使线程保持活动状态,循环 MessageQueue 并向相应 Handler 的进程发送消息。
  4. Thread 通过调用 Looper 的 quit() 方法终止。

 

Handler 及其组件

  • Handler:框架的重要对象,它具有双重责任。它绑定到给定线程的特定 Looper,并提供了将消息发送到相关 MessageQueue 的方法。该处理器还负责在实际执行消息的内容。
  • Message:封装有关在特定线程上执行的操作信息。
  • Runnable:抽象一个线程可以执行的任何操作的接口。
  • MessageQueue:表示线程消耗的消息队列。
  • Looper:负责循环的对象,循环检查 MessageQueue 以查看是否有消息要运行并将其发送到特定的 Handler。每个线程只能有一个 Looper。
  •  

现在,开始编写代码了。

 

例子1:

 

使用 Thread:

创建主线程处理程序

Handler handler = new Handler(Looper.getMainLooper());

  
  1. thread = new Thread( new Runnable() {
  2. @Override
  3. public void run() {
  4. handler2 = new Handler(Looper.getMainLooper());
  5. handler2.post( new Runnable() {
  6. @Override
  7. public void run() {
  8. Toast.makeText(MainActivity. this, "hello world",Toast.LENGTH_SHORT).show();
  9. }
  10. });
  11. }
  12. });

使用 HandlerThread:


  
  1. handlerThread = new HandlerThread( "name");
  2. handlerThread.start();
  3. handler = new Handler(handlerThread.getLooper());
  4. handler.post( new Runnable() {
  5. @Override
  6. public void run() {
  7. Toast.makeText(MainActivity. this, "世界你好",Toast.LENGTH_SHORT).show();
  8. }
  9. });

注意:使用完 HandlerThread 后,要调用 quit() 方法退出。

 

例子2:

 

进度条

 

MainActivity.java

  
  1. public class MainActivity extends AppCompatActivity {
  2. Handler handler;
  3. Thread thread;
  4. Button botton;
  5. int MAX = 100;
  6. TextView textView;
  7. HandlerThread handlerThread;
  8. ProgressBar progressBar;
  9. @Override
  10. protected void onCreate(Bundle savedInstanceState) {
  11. super.onCreate(savedInstanceState);
  12. setContentView(R.layout.activity_main);
  13. initView();
  14. initThread();
  15. }
  16. private void initView(){
  17. botton = (Button)findViewById(R.id.start_progress);
  18. progressBar = (ProgressBar)findViewById(R.id.progressBar);
  19. textView = (TextView)findViewById(R.id.textView);
  20. progressBar.setMax(MAX);
  21. }
  22. private void initThread(){
  23. handler = new Handler(){
  24. @Override
  25. public void handleMessage(Message message){
  26. super.handleMessage(message);
  27. textView.setText(message.what+ "");
  28. }
  29. };
  30. thread = new Thread( new Runnable() {
  31. @Override
  32. public void run() {
  33. for ( int i= 0;i< 100;i++){
  34. progressBar.setProgress(i);
  35. try {
  36. Thread.sleep( 100);
  37. } catch (InterruptedException e) {
  38. e.printStackTrace();
  39. }
  40. Message msg = new Message();
  41. msg.what = i;
  42. msg.obj = "obj";
  43. handler.sendMessage(msg);
  44. }
  45. }
  46. });
  47. }
  48. public void onClick(View view){
  49. switch (view.getId()){
  50. case R.id.start_progress:
  51. thread.start();
  52. break;
  53. default:
  54. break;
  55. }
  56. }
  57. public void onDestroy(){
  58. super.onDestroy();
  59. handlerThread.quit();
  60. }
  61. }
activity_main.xml

  
  1. <?xml version="1.0" encoding="utf-8"?>
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  3. xmlns:app= "http://schemas.android.com/apk/res-auto"
  4. xmlns:tools= "http://schemas.android.com/tools"
  5. android:layout_width= "match_parent"
  6. android:layout_height= "match_parent"
  7. tools:context= ".MainActivity">
  8. <ProgressBar
  9. android:id= "@+id/progressBar"
  10. style= "?android:attr/progressBarStyleHorizontal"
  11. android:layout_width= "match_parent"
  12. android:layout_height= "wrap_content"
  13. android:layout_alignParentLeft= "true"
  14. android:layout_alignParentStart= "true"
  15. android:layout_alignParentTop= "true" />
  16. <Button
  17. android:id= "@+id/start_progress"
  18. android:layout_width= "wrap_content"
  19. android:layout_height= "wrap_content"
  20. android:layout_alignParentLeft= "true"
  21. android:layout_alignParentStart= "true"
  22. android:layout_below= "@+id/progressBar"
  23. android:layout_marginLeft= "24dp"
  24. android:layout_marginStart= "24dp"
  25. android:layout_marginTop= "62dp"
  26. android:onClick= "onClick"
  27. android:text= "Start" />
  28. <TextView
  29. android:id= "@+id/textView"
  30. android:layout_width= "wrap_content"
  31. android:layout_height= "wrap_content"
  32. android:layout_alignBottom= "@+id/start_progress"
  33. android:layout_alignParentEnd= "true"
  34. android:layout_alignParentRight= "true"
  35. android:layout_marginEnd= "85dp"
  36. android:layout_marginRight= "85dp"
  37. android:textSize= "16sp"
  38. android:text= "Count 1"
  39. android:layout_alignTop= "@+id/start_progress" />
  40. </RelativeLayout>

例子3:

 

看到这一点,您可能不太了解,也许会感觉到我在哪里。
接下来,我们使用一个简单的示例来验证和解决我们内心的疑虑。

 

 

在这里,我们放置一个按钮,单击后更改TextView的值。

 

还设置延迟以模拟要处理的大量数据。

 


  
  1. public class MainActivity extends AppCompatActivity {
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. }
  7. public void buttonClick(View view)
  8. {
  9. try {
  10. Thread.sleep( 6000);
  11. } catch (InterruptedException e) {
  12. e.printStackTrace();
  13. }
  14. TextView myTextView = (TextView)findViewById(R.id.myTextView);
  15. myTextView.setText( "Button Pressed");
  16. }
  17. }

 


  
  1. <RelativeLayout xmlns:android= "http://schemas.android.com/apk/res/android"
  2. xmlns:tools= "http://schemas.android.com/tools"
  3. android:layout_width= "match_parent"
  4. android:layout_height= "match_parent"
  5. android:paddingLeft= "@dimen/activity_horizontal_margin"
  6. android:paddingRight= "@dimen/activity_horizontal_margin"
  7. android:paddingTop= "@dimen/activity_vertical_margin"
  8. android:paddingBottom= "@dimen/activity_vertical_margin"
  9. tools:context= ".MainActivity">
  10. <TextView
  11. android:text= "hello world"
  12. android:layout_width= "wrap_content"
  13. android:layout_height= "wrap_content"
  14. android:layout_centerVertical= "true"
  15. android:layout_centerHorizontal= "true"
  16. android:id= "@+id/myTextView" />
  17. <Button
  18. android:layout_width= "wrap_content"
  19. android:layout_height= "wrap_content"
  20. android:text= "click me"
  21. android:id= "@+id/button"
  22. android:layout_below= "@+id/myTextView"
  23. android:layout_centerHorizontal= "true"
  24. android:layout_marginTop= "45dp"
  25. android:onClick= "buttonClick" />
  26. < /RelativeLayout>

 

运行后发现,延迟了6秒后才更新视图,这在实际开发中,会严重影响用户体验,造成一种严重卡顿的现象。

 

要解决这个问题,我们可以使用多线程的方法,修改后如下。


  
  1. public void buttonClick(View view)
  2. {
  3. Thread thread = new Thread( new Runnable() {
  4. @Override
  5. public void run() {
  6. try {
  7. Thread.sleep( 6000);
  8. Log.d( "thread:", "hello world");
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }
  13. });
  14. thread.start();
  15. }

在主线程以外的线程中更新用户界面元素违反了Android开发的关键规则。因此,为了更新用户界面,有必要实现线程的处理程序(Handler)。


  
  1. Handler handler = new Handler(){
  2. @Override
  3. public void handleMessage(Message msg){
  4. super.handleMessage(msg);
  5. TextView myTextView = (TextView)findViewById(R.id.myTextView);
  6. myTextView.setText( "Button click");
  7. };
  8. };

再次修改 buttonClick() 中的内容


  
  1. public void buttonClick(View view)
  2. {
  3. Thread thread = new Thread( new Runnable() {
  4. @Override
  5. public void run() {
  6. try {
  7. Thread.sleep( 6000);
  8. Log.d( "thread:", "hello world");
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. handler.sendEmptyMessage( 0);
  13. }
  14. });
  15. thread.start();
  16. }

拓展内容:handler 几种方法

方法 描述
sendMessage 将消息推送到消息队列的末尾。
sendMessageDelayed 将消息推送到消息队列的末尾。
sendMessageAtTime 将消息推送到消息队列的末尾。
sendEmptyMessage 发送仅包含单个int代码的消息。
sendEmptyMessageDelayed 在指定的时间过后发送要传递的消息。
sendEmptyMessageAtTime 发送消息以在指定的绝对时间传递。

 

 

 

 

 

 

 

 

使用 messaga 对象保存数据

 


  
  1. public void buttonClick(View view)
  2. {
  3. Thread thread = new Thread( new Runnable() {
  4. @Override
  5. public void run() {
  6. try {
  7. Thread.sleep( 6000);
  8. Log.d( "thread:", "hello world");
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. Message msg = handler.obtainMessage();
  13. Bundle bundle = new Bundle();
  14. bundle.putString( "name", "anny");
  15. msg.setData(bundle);
  16. handler.sendMessage(msg);
  17. }
  18. });
  19. thread.start();
  20. }

obtainmessage() 是从消息池中拿来一个msg 不需要另开辟空间new

取出数据并赋值给 TextView 


  
  1. Handler handler = new Handler(){
  2. @Override
  3. public void handleMessage(Message msg){
  4. super.handleMessage(msg);
  5. Bundle bundle = msg.getData();
  6. String name = bundle.getString( "name");
  7. TextView myTextView = (TextView)findViewById(R.id.myTextView);
  8. myTextView.setText(name);
  9. };
  10. };

 

例子4:

 

为线程创建Looper和MessageQueue:

 


  
  1. public class Test extends Thread{
  2. public Handler handler;
  3. public void run(){
  4. Looper.prepare();
  5. handler = new Handler(){
  6. @Override
  7. public void handleMessage(Message message){
  8. super.handleMessage(message);
  9. Log.d( "obj:", "hello world");
  10. }
  11. };
  12. Looper.loop();
  13. }
  14. }

 

 

仔细看你会发现,我们在这里使用了 Looper.prepare()和 Looper.loop()

 

为什么前面的示例不起作用?为什么在这里使用它?

 

1:在默认线程下,主线程系统将自动创建Looper对象并启动消息循环。

2:在主线程中mainHandler = new Handler()等同于new Handler(Looper.myLooper())

 

将消息发送到 MessageQueue

 

第一种方式:Message


  
  1. Message msg = new Message();
  2. msg.obj = "Ali send message";
  3. handler.sendMessage(msg);

第二种方式:Runnable


  
  1. new Handler(Looper.getMainLooper()).post( new Runnable() {
  2. @Override
  3. public void run() {
  4. // this will run in the main thread
  5. }
  6. });

 

拓展:

方法 描述
post 立即让Runnable排队执行。
postAtTime 使Runnable排队以在以毫秒为单位的绝对时间执行。
postDelayed 使Runnable排队以指定的毫秒延迟后执行。
postAtFrontOfQueue 立即将Runnable排队到要执行的前端。

 

 

 

 

 

 

 

 Android 提供了 HandlerThread (线程的子类) 来简化过程。在内部,它以健壮的方式执行与我们相同的操作,因此,请始终使用 HandlerThread。

 


  
  1. public class Test extends HandlerThread {
  2. public Handler handler;
  3. public Test(String name) {
  4. super(name);
  5. }
  6. @Override
  7. protected void onLooperPrepared(){
  8. handler = new Handler(getLooper()){
  9. @Override
  10. public void handleMessage(Message message){
  11. super.handleMessage(message);
  12. //接收消息
  13. }
  14. };
  15. }
  16. }
 

我们在调用 onLooperPrepared 时实例化了 Handler。因此,Handler 可以将其与 Looper 关联。

 

1:仅在 start() 调用 HandlerThread 之后,即线程运行之后,才准备 Looper。

2:只有在准备好之后,Handler 才能与 HandlerThread Looper 关联。

 

另一种方式创建 HandlerThread 的方式(之前提过):


  
  1. HandlerThread handlerThread = new HandlerThread("myHandlerThread");
  2. handlerThread.start();
  3. Looper looper = handlerThread.getLooper();
  4. Handler handler = new Handler(looper);

重要说明:请记住,在完成后台线程或活动onDestroy()方法之后调用handlerThread.quit()。


转载:https://blog.csdn.net/qq_41974199/article/details/105699841
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场