由于:耗时耗时操作不建议(不能)放在UI线程中进行处理,那么子线程
处理的数据,如何递交到UI线程进行UI控件的操作和数据的适配。
1、Handler简介:
a、handler可以在任意线程中进行消息的发送,这些消息被添加到被关联的MessageQueue。
a、1、在UI线程中创建handler
2、把UI线程中创建好的handler的内存地址引用传递给子线程
3、在子线程中的run方法中,进行handler消息池中消息的获取。
4、封装消息内容
5、通过handler发送该消息。
6、在handler中接收该消息,进行消息处理。
子线程代码:
布局界面就是一个TextView在这里就不做赘述了。
(该handler即为处理UI控件的handler)
4、直接复写该handler的handleMessage方法,完成UI更新。
子线程代码:
子线程代码:
相关的一系列测试,我放在现在下载资源中,有兴趣的可以下载看看。
处理的数据,如何递交到UI线程进行UI控件的操作和数据的适配。
1、Handler简介:
a、handler可以在任意线程中进行消息的发送,这些消息被添加到被关联的MessageQueue。
b、handler可以处理来自被关联的Looper遍历出的消息。
a、1、在UI线程中创建handler
2、把UI线程中创建好的handler的内存地址引用传递给子线程
3、在子线程中的run方法中,进行handler消息池中消息的获取。
4、封装消息内容
5、通过handler发送该消息。
6、在handler中接收该消息,进行消息处理。
(PS:以上所有的handler,指的都是UI线程中创建的handler)
例,写一个在子线程中通过传递handler对象来修改UI线程中textView中的内容:
UI线程代码:
- public class MainActivity2 extends AppCompatActivity implements View.OnClickListener{
- TextView textview;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- textview = (TextView) findViewById(R.id.textview);
- textview.setOnClickListener(this);
- }
- @Override
- public void onClick(View view) {
- //在UI线程中创建的handler属于UI线程。
- MyHandler myHandler = new MyHandler();
- //同时handler与looper是唯一映射的关系,一个handler只能拥有一个looper,
- // 一个looper只能和一个线程相照应,同时一个looper也对应着唯一一个MessageQueue。
- // (PS:一个looper却可以拥有多个handler)
- //myHandler.getLooper();
- // 所以在此处创建的handler对应的looper是UI线程的looper,对应的MessageQueue是UI线程的MessageQueue.
- BThread bThread = new BThread(myHandler);
- bThread.start();
- }
- class MyHandler extends Handler{
- @Override
- //UI线程中使用handlerMessage()方法对消息进行处理。
- public void handleMessage(Message msg) {
- textview.setText(msg.obj.toString());
- }
- }
- }
- public class BThread extends Thread{
- //这个Handler对象,在那创建就是谁的handler。(PS)此处的handler只是声明还没没有实例化。
- Handler handler;
- public BThread(Handler handler) {
- this.handler = handler;
- }
- @Override
- public void run() {
- String temp = "哈哈哈";
- temp += "AAA";
- //这个Message对象,handler消息池中的对象,为了避免实例化消耗资源,而不用new创建。
- Message message = handler.obtainMessage();
- message.obj = temp;
- //这个消息发送给了主线程的MessageQueue中,通过Looper.loop()方法将消息取出。
- //将消息发送到UI线程的MessageQueue(消息列队中);
- handler.sendMessage(message);
- }
- }
b、1、在UI线程中获取Looper
2、把UI线程中获取的Looper传递给子线程
3、在子线程的run方法中通过looper的传递实例化handler(该handler即为处理UI控件的handler)
4、直接复写该handler的handleMessage方法,完成UI更新。
UI线程代码:
- public class MainActivity4 extends AppCompatActivity implements View.OnClickListener{
- TextView textview;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- textview = (TextView) findViewById(R.id.textview);
- textview.setOnClickListener(this);
- }
- @Override
- public void onClick(View view) {
- //使用Looper.myLooper();方法获取UI线程的looper对象,并将其传递到子线程中去;
- DThread dThread = new DThread(textview, Looper.myLooper());
- dThread.start();
- }
- }
- /**
- * Created by Administrator on 2016/6/25.
- * 此例子, 通过传递UI线程的Looper,来实例化handler,依然可以成功修改UI
- */
- public class DThread extends Thread{
- TextView textview;
- Looper looper;
- public DThread(TextView textview, Looper looper) {
- this.textview = textview;
- this.looper = looper;
- }
- @Override
- public void run() {
- String temp = "哈哈哈";
- temp += "AAA";
- //使用new MyHandler(looper);构造方法创建handler对象属于该looper对应的handler,
- // 此处的looper为UI线程传递过来的looper,所以此处的handler为UI线程的handler,
- // 所以对应的MessageQueue(消息列队)也为UI线程的MessageQueue
- MyHandler handler = new MyHandler(looper);
- Message msg = handler.obtainMessage();
- msg.obj = temp;
- msg.setTarget(handler);
- handler.sendMessage(msg);
- msg.what = 0;
- // handler.sendMessageDelayed(msg, 2000);
- handler.post(new Runnable() {
- @Override
- public void run() {
- }
- });
- }
- class MyHandler extends Handler{
- public MyHandler(Looper looper){
- super(looper);
- }
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what){
- case 0:
- textview.setText(msg.obj.toString());
- break;
- }
- }
- }
- }
(PS:无论是传递主线程创建的Handler还是传递主线程的Looper,最终目的,都是传递主线程的MessageQueue)
4、拓展:子线程可以直接修改UI。
UI线程代码:
- public class MainActivity extends AppCompatActivity implements View.OnClickListener{
- TextView textview;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- textview = (TextView) findViewById(R.id.textview);
- textview.setOnClickListener(this);
- /* 所谓的在UI线程中非UI线程不能对UI进行操作是因为在布局加载完全后,底层对每次请求处理UI线程消息的线程进行了判断,
- 如果不是UI线程就不不能进行UI线程的消息处理,但是你在频幕显示绘制完成之前还是能对其进行修改的,此时线程判断的方法还未的到
- 执行,所以子线程(非UI线程能进行操控)。
- 先来看以下View.requestLayout源码:
- * Call this when something has changed which has invalidated the
- * layout of this view. This will schedule a layout pass of the view
- * tree.
- public void requestLayout() {
- mPrivateFlags |= PFLAG_FORCE_LAYOUT;
- mPrivateFlags |= PFLAG_INVALIDATED;
- if (mParent != null && !mParent.isLayoutRequested()) {
- // 先上传递,最终到达顶层View即RootViewImpl
- mParent.requestLayout();
- }
- }
- 查看下ViewRootImpl.requestLayout源码:
- public void requestLayout() {
- // 检测是否在UI线程
- checkThread();
- mLayoutRequested = true;
- // 重点
- scheduleTraversals();
- } */
- //这里在视图没有加载完全的情况下,没有对执行线程的判断,所以非UI线程在此处也得到了执行。
- //下面两线程在onCreate方法中会被执行。
- // AThread aThread = new AThread(textview);
- // aThread.start();
- }
- @Override
- public void onClick(View view) {
- AThread aThread = new AThread(textview);
- aThread.start();
- }
- }
- public class AThread extends Thread{
- TextView textview;
- public AThread(TextView textview) {
- this.textview = textview;
- }
- @Override
- public void run() {
- String temp = "哈哈哈";
- temp += "AAA";
- /*在运行时会报 android.view.ViewRootImpl$CalledFromWrongThreadException:
- Only the original thread that created a view hierarchy can touch its views
- 这个错误,意思是说子线程(非UI线程)不能操作UI线程
- 这里子线程(非UI线程)操作了UI线程中的textveiw控件为其设置内容,所以报错*/
- textview.setText(temp);
- }
- }