Android中怎么使用Looper和Handler进行子线程数据操作和提交到UI线程

来源:互联网 发布:现在淘宝店铺免费吗? 编辑:程序博客网 时间:2024/05/18 02:10
由于:耗时耗时操作不建议(不能)放在UI线程中进行处理,那么子线程
处理的数据,如何递交到UI线程进行UI控件的操作和数据的适配。
1、Handler简介:
a、handler可以在任意线程中进行消息的发送,这些消息被添加到被关联的MessageQueue。

b、handler可以处理来自被关联的Looper遍历出的消息。



2、Handler的具体用法。
a、1、在UI线程中创建handler
  2、把UI线程中创建好的handler的内存地址引用传递给子线程
  3、在子线程中的run方法中,进行handler消息池中消息的获取。
  4、封装消息内容
  5、通过handler发送该消息。
  6、在handler中接收该消息,进行消息处理。

  (PS:以上所有的handler,指的都是UI线程中创建的handler)

例,写一个在子线程中通过传递handler对象来修改UI线程中textView中的内容:

UI线程代码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class MainActivity2 extends AppCompatActivity implements View.OnClickListener{  
  2.     TextView textview;  
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         textview = (TextView) findViewById(R.id.textview);  
  8.         textview.setOnClickListener(this);  
  9.     }  
  10.   
  11.     @Override  
  12.     public void onClick(View view) {  
  13.         //在UI线程中创建的handler属于UI线程。  
  14.         MyHandler myHandler = new MyHandler();  
  15.         //同时handler与looper是唯一映射的关系,一个handler只能拥有一个looper,  
  16.         // 一个looper只能和一个线程相照应,同时一个looper也对应着唯一一个MessageQueue。  
  17.         // (PS:一个looper却可以拥有多个handler)  
  18.         //myHandler.getLooper();  
  19.         // 所以在此处创建的handler对应的looper是UI线程的looper,对应的MessageQueue是UI线程的MessageQueue.  
  20.   
  21.         BThread bThread = new BThread(myHandler);  
  22.         bThread.start();  
  23.     }  
  24.   
  25.     class MyHandler extends Handler{  
  26.         @Override  
  27.         //UI线程中使用handlerMessage()方法对消息进行处理。  
  28.         public void handleMessage(Message msg) {  
  29.             textview.setText(msg.obj.toString());  
  30.         }  
  31.     }  
  32. }  
子线程代码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class BThread extends Thread{  
  2.     //这个Handler对象,在那创建就是谁的handler。(PS)此处的handler只是声明还没没有实例化。  
  3.     Handler handler;  
  4.   
  5.     public BThread(Handler handler) {  
  6.         this.handler = handler;  
  7.     }  
  8.   
  9.     @Override  
  10.     public void run() {  
  11.         String temp = "哈哈哈";  
  12.         temp += "AAA";  
  13.         //这个Message对象,handler消息池中的对象,为了避免实例化消耗资源,而不用new创建。  
  14.         Message message = handler.obtainMessage();  
  15.         message.obj = temp;  
  16.         //这个消息发送给了主线程的MessageQueue中,通过Looper.loop()方法将消息取出。  
  17.         //将消息发送到UI线程的MessageQueue(消息列队中);  
  18.         handler.sendMessage(message);  
  19.     }  
  20. }  
布局界面就是一个TextView在这里就不做赘述了。

b、1、在UI线程中获取Looper

  2、把UI线程中获取的Looper传递给子线程

  3、在子线程的run方法中通过looper的传递实例化handler
 (该handler即为处理UI控件的handler)
  4、直接复写该handler的handleMessage方法,完成UI更新。

UI线程代码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class MainActivity4 extends AppCompatActivity implements View.OnClickListener{  
  2.     TextView textview;  
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         textview = (TextView) findViewById(R.id.textview);  
  8.         textview.setOnClickListener(this);  
  9.     }  
  10.   
  11.     @Override  
  12.     public void onClick(View view) {  
  13.         //使用Looper.myLooper();方法获取UI线程的looper对象,并将其传递到子线程中去;  
  14.         DThread dThread = new DThread(textview, Looper.myLooper());  
  15.         dThread.start();  
  16.     }  
  17.   
  18. }  
子线程代码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /** 
  2.  * Created by Administrator on 2016/6/25. 
  3.  * 此例子, 通过传递UI线程的Looper,来实例化handler,依然可以成功修改UI 
  4.  */  
  5. public class DThread extends Thread{  
  6.     TextView textview;  
  7.     Looper looper;  
  8.   
  9.     public DThread(TextView textview, Looper looper) {  
  10.         this.textview = textview;  
  11.         this.looper = looper;  
  12.     }  
  13.   
  14.     @Override  
  15.     public void run() {  
  16.         String temp = "哈哈哈";  
  17.         temp += "AAA";  
  18. //使用new MyHandler(looper);构造方法创建handler对象属于该looper对应的handler,  
  19. // 此处的looper为UI线程传递过来的looper,所以此处的handler为UI线程的handler,  
  20. // 所以对应的MessageQueue(消息列队)也为UI线程的MessageQueue  
  21.         MyHandler handler = new MyHandler(looper);  
  22.         Message msg = handler.obtainMessage();  
  23.         msg.obj = temp;  
  24.         msg.setTarget(handler);  
  25.         handler.sendMessage(msg);  
  26.         msg.what = 0;  
  27.   
  28. //        handler.sendMessageDelayed(msg, 2000);  
  29.           handler.post(new Runnable() {  
  30.               @Override  
  31.               public void run() {  
  32.   
  33.               }  
  34.           });  
  35.     }  
  36.   
  37.     class MyHandler extends Handler{  
  38.         public MyHandler(Looper looper){  
  39.             super(looper);  
  40.         }  
  41.   
  42.         @Override  
  43.         public void handleMessage(Message msg) {  
  44.             switch (msg.what){  
  45.                 case 0:  
  46.                     textview.setText(msg.obj.toString());  
  47.                     break;  
  48.             }  
  49.   
  50.         }  
  51.   
  52.   
  53.     }  
  54. }  

(PS:无论是传递主线程创建的Handler还是传递主线程的Looper,最终目的,都是传递主线程的MessageQueue)




4、拓展:子线程可以直接修改UI。

UI线程代码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends AppCompatActivity implements View.OnClickListener{  
  2.     TextView textview;  
  3.     @Override  
  4.     protected void onCreate(Bundle savedInstanceState) {  
  5.         super.onCreate(savedInstanceState);  
  6.         setContentView(R.layout.activity_main);  
  7.         textview = (TextView) findViewById(R.id.textview);  
  8.         textview.setOnClickListener(this);  
  9. /* 所谓的在UI线程中非UI线程不能对UI进行操作是因为在布局加载完全后,底层对每次请求处理UI线程消息的线程进行了判断, 
  10. 如果不是UI线程就不不能进行UI线程的消息处理,但是你在频幕显示绘制完成之前还是能对其进行修改的,此时线程判断的方法还未的到 
  11. 执行,所以子线程(非UI线程能进行操控)。 
  12. 先来看以下View.requestLayout源码: 
  13.  * Call this when something has changed which has invalidated the 
  14.  * layout of this view. This will schedule a layout pass of the view 
  15.  * tree. 
  16.         public void requestLayout() { 
  17.             mPrivateFlags |= PFLAG_FORCE_LAYOUT; 
  18.             mPrivateFlags |= PFLAG_INVALIDATED; 
  19.  
  20.             if (mParent != null && !mParent.isLayoutRequested()) { 
  21.                 // 先上传递,最终到达顶层View即RootViewImpl 
  22.                 mParent.requestLayout(); 
  23.             } 
  24.         } 
  25.  
  26. 查看下ViewRootImpl.requestLayout源码: 
  27. public void requestLayout() { 
  28.     // 检测是否在UI线程 
  29.     checkThread(); 
  30.     mLayoutRequested = true; 
  31.     // 重点 
  32.     scheduleTraversals(); 
  33. } */  
  34. //这里在视图没有加载完全的情况下,没有对执行线程的判断,所以非UI线程在此处也得到了执行。  
  35. //下面两线程在onCreate方法中会被执行。  
  36. //        AThread aThread = new AThread(textview);  
  37. //        aThread.start();  
  38.   
  39.     }  
  40.   
  41.     @Override  
  42.     public void onClick(View view) {  
  43.         AThread aThread = new AThread(textview);  
  44.         aThread.start();  
  45.     }  
  46. }  
子线程代码:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class AThread extends Thread{  
  2.     TextView textview;  
  3.   
  4.     public AThread(TextView textview) {  
  5.         this.textview = textview;  
  6.     }  
  7.   
  8.     @Override  
  9.     public void run() {  
  10.         String temp = "哈哈哈";  
  11.         temp += "AAA";  
  12.         /*在运行时会报 android.view.ViewRootImpl$CalledFromWrongThreadException: 
  13.          Only the original thread that created a view hierarchy can touch its views 
  14.          这个错误,意思是说子线程(非UI线程)不能操作UI线程 
  15.          这里子线程(非UI线程)操作了UI线程中的textveiw控件为其设置内容,所以报错*/  
  16.         textview.setText(temp);  
  17.     }  
  18. }  
相关的一系列测试,我放在现在下载资源中,有兴趣的可以下载看看。
2 0
原创粉丝点击