zoukankan      html  css  js  c++  java
  • android为什么不允许新开启一个线程来更新UI,而是用handler来更新界面

    下面是快速创建一个新线程的方法:

    第一种:直接创建子线程并启动
          new Thread() {
    @Override
    public void run() {
         //这里写入子线程需要做的工作
            }
       }.start();
       
    第二种:先创建子线程,然后启动
            private Thread newThread; //声明一个子线程
    newThread = new Thread(new Runnable() {
        @Override
                public void run() {
                //这里写入子线程需要做的工作
                }
            });

        newThread.start(); //启动线程

    操作是很有可能并发的,而界面只有一个
    这个和买票排队是一回事
    买票的人太多了,卖票的只有一个,只能一个一个来
    如果你开多线程,让100个人同时去买票,而且不排队,那么后果会怎么样- -
    同理,你开多线程,让100个线程去设置同一个TextView的显示内容,每个显示内容都不一样,它该听谁的?

    那为什么不直接new一个新线程而要使用一个所谓的handler?

    就是因为new了一个子线程才要用handler的,
    不然在主线程里更新UI要handler干什么?多此一举

    就好比只有1个人来买票,卖票的难道会跟他说:同志,请你排队!?
    handle是主线程 ,Thread是从线程。控件数据更改只能在主线程 里,所以要用handle

    更新UI只能在主线程里进行,否则会报错。但有时我们在子线程里进行操作需要更新UI,handler就登场了,它可以把子线程的数据传给主线程,让主线程同步操作来更新UI。

    先来看这样一个例子

     package com.hua;

    import android.app.Activity;
    import android.os.Bundle;

    public class UpdateUInum1Activity extends Activity {
     
     @Override
     public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      new Thread(new Runnable() {
       @Override
       public void run() {
        setTitle("fengyi.hua");
       }
      });
     }
    }

        上面就是为了实现用一个Thread来更新Title,可以实现这个功能,刷新UI界面。但是这样是不对的,因为它违背了单线程模型:Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行。

        有些人觉得这个方法确实也多余,为什么呢,因为既然是刷新一次,我完全可以在主线程中执行刷新Title的操作的,为什么还要开启线程。这是因为可能涉及到延时或者其它。比如说等待1min后再进行刷新操作,这个时间段要保证主UI线程是可操作的,所以要用到Thread来更新。但是这确实对于android的单线程模型有冲突,不建议使用。使用错误的例子如下:

    package com.hua;

    import java.util.Date;
    import java.util.Timer;
    import java.util.TimerTask;

    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;

    public class UpdateTitleActivity extends Activity {
     

      private void updateTitle() {
      Date date = new Date();
      int hour, minute, second;
      String shour, sminute, ssecond;
      hour = (date.getHours() + 8) % 24;
      minute = date.getMinutes();
      second = date.getSeconds();

      if (hour < 10) {
       shour = "0" + hour;
      } else {
       shour = "" + hour;
      }

      if (minute < 10) {
       sminute = "0" + minute;
      } else {
       sminute = "" + minute;
      }

      if (second < 10) {
       ssecond = "0" + second;
      } else {
       ssecond = "" + second;
      }

      setTitle("当前时间:" + shour + ":" + sminute + ":" + ssecond);
     }

     @Override
     public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      Timer timer = new Timer();
      timer.scheduleAtFixedRate(new myTask(), 1, 1000);//这里是利用Timer,跟使用Thread是一个效果。表现

      //的效果就是多次更新Title,看会不会出问题。
     }

     private class myTask extends TimerTask {
      @Override
      public void run() {
         updateTitle();
      }

     }

    }

       上面的代码用来每1s刷新一次Title,用来显示当前时间。但是由于android是单线程模型,存在线程安全问题,所以当第二次刷新的时候,出现错误。

    正确的做法

        上面所述两种方法,分别是Thread方法,和TimerTask方法。在Java中是常用的,因为线程安全。但是在单线程模型的android中,是不能用的。正确的方法有2个。

    1.Thread+handler

    2.TimerTask+handler

    3.Runnable+Handler.postDelayed(runnable,time)

    例子:Timertask+handler

    package com.hua;

    import java.util.Date;
    import java.util.Timer;
    import java.util.TimerTask;

    import android.app.Activity;
    import android.os.Bundle;
    import android.os.Handler;
    import android.os.Message;

    public class UpdateTitleActivity extends Activity {
     

      private Handler mHandler = new Handler(){

      @Override
      public void handleMessage(Message msg) {
      super.handleMessage(msg);
      switch (msg.what) {
      case 1:
      updateTitle();
      break;
     
      default:
      break;
      }
      }
      };

     private void updateTitle() {
      Date date = new Date();
      int hour, minute, second;
      String shour, sminute, ssecond;
      hour = (date.getHours() + 8) % 24;
      minute = date.getMinutes();
      second = date.getSeconds();

      if (hour < 10) {
       shour = "0" + hour;
      } else {
       shour = "" + hour;
      }

      if (minute < 10) {
       sminute = "0" + minute;
      } else {
       sminute = "" + minute;
      }

      if (second < 10) {
       ssecond = "0" + second;
      } else {
       ssecond = "" + second;
      }

      setTitle("当前时间:" + shour + ":" + sminute + ":" + ssecond);
     }

     @Override
     public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);
      Timer timer = new Timer();
      timer.scheduleAtFixedRate(new myTask(), 1, 10000);
     }

     private class myTask extends TimerTask {
      @Override
      public void run() {
        Message msg = new Message();
        msg.what = 1;
        mHandler.sendMessage(msg);
      }

     }

    }

        记住,处理都是在handleMessage里面,当然也可以不在,可以在handler的内类Callback的方法handleMessage里面。Handler跟其Callback也是学问,可以以后讲。

    为了解决在Android非UI线程更新UI这个问题,Android提供了一些方法,从其他线程访问UI线程。

    1. Activity.runOnUiThread(Runnable)
    2. View.post(Runnable)
    3. View.postDelayed(Runnable, long)
    4. Looper的方式。
    5. 使用Handler的方式。
    // 1. 使用runOnUiThread的方式
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
        }
    });
    
    // 2. 使用post的方式
    btn.post(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
        }
    });
    
    // 3. 使用postDelayed的方式
    btn.postDelayed(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
        }
    }, 1000);
    
    // 4. 使用Looper的方式
    Looper.prepare();
    Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
    Looper.loop();
    
    // 5. 使用Handler的方式
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
                    break;
            }
    
        }
    };
    // 发送消息
    handler.sendEmptyMessage(1);
    或者
    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(MainActivity.this, "sdf", Toast.LENGTH_SHORT).show();
        }
    });
  • 相关阅读:
    解决死锁四大方式
    Windows内存管理简介:
    排序算法优劣
    排序
    HTTPs
    http和https的异同
    HTTP协议
    FTP与TFTP
    tomcat热部署
    开发心得体会
  • 原文地址:https://www.cnblogs.com/Alex80/p/11563797.html
Copyright © 2011-2022 走看看