android爲何不容許新開啓一個線程來更新UI,而是用handler來更新界面

下面是快速建立一個新線程的方法:java

第一種:直接建立子線程並啓動
      new Thread() {
@Override
public void run() {
     //這裏寫入子線程須要作的工做
        }
   }.start();
   
第二種:先建立子線程,而後啓動
        private Thread newThread; //聲明一個子線程
newThread = new Thread(new Runnable() {
    @Override
            public void run() {
            //這裏寫入子線程須要作的工做
            }
        });android

    newThread.start(); //啓動線程安全

 

操做是頗有可能併發的,而界面只有一個
這個和買票排隊是一回事
買票的人太多了,賣票的只有一個,只能一個一個來
若是你開多線程,讓100我的同時去買票,並且不排隊,那麼後果會怎麼樣- -
同理,你開多線程,讓100個線程去設置同一個TextView的顯示內容,每一個顯示內容都不同,它該聽誰的?多線程

那爲何不直接new一個新線程而要使用一個所謂的handler?併發

就是由於new了一個子線程纔要用handler的,
否則在主線程裏更新UI要handler幹什麼?畫蛇添足

就比如只有1我的來買票,賣票的難道會跟他說:同志,請你排隊!?
handle是主線程 ,Thread是從線程。控件數據更改只能在主線程 裏,因此要用handleapp

更新UI只能在主線程裏進行,不然會報錯。但有時咱們在子線程裏進行操做須要更新UI,handler就登場了,它能夠把子線程的數據傳給主線程,讓主線程同步操做來更新UI。ide

先來看這樣一個例子oop

 package com.hua;post

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

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(); } });
相關文章
相關標籤/搜索