線程異步、接口回調


一、線程的實現(異步機制、耗時操作)

Thread:
Runnable:
Handler:
在Handler 異步實現時,涉及到 Handler, Looper, Message,Thread四個對象,
實現異步的流程是主線程啟動Thread(子線程)àthread(子線程)運行並生成
Message-àLooper獲取Message並傳遞給HandleràHandler逐個獲取Looper中的Message,並進行UI變更。
AsyncTask:
(1)首先明確Android之所以有Handler和AsyncTask,都是為了不阻塞主線程(UI線程),
且UI的更新只能在主線程中完成,因此異步處理是不可避免的。
輕量級的異步類,實現異步操作,可以實現異步請求和主界面更新(線程池+handler)

        package com.itheima.zhbj74.utils;
  import java.io.IOException;
  import java.io.InputStream;
  import java.net.HttpURLConnection;
  import java.net.MalformedURLException;
  import java.net.URL;
  import android.graphics.Bitmap;
  import android.graphics.BitmapFactory;
  import android.os.AsyncTask;
  import android.widget.ImageView;
  /**
  * 網絡緩存
  * 
  * @author Kevin
  * @date 2015-10-24
  */
  public class NetCacheUtils {
   private LocalCacheUtils mLocalCacheUtils;
   private MemoryCacheUtils mMemoryCacheUtils;
   public NetCacheUtils(LocalCacheUtils localCacheUtils,
     MemoryCacheUtils memoryCacheUtils) {
    mLocalCacheUtils = localCacheUtils;
    mMemoryCacheUtils = memoryCacheUtils;
   }
   public void getBitmapFromNet(ImageView imageView, String url) {
    // AsyncTask 異步封裝的工具, 可以實現異步請求及主界面更新(對線程池+handler的封裝)
    new BitmapTask().execute(imageView, url);// 啟動AsyncTask
   }
   /**
   * 三個泛型意義: 
   第一個泛型:doInBackground里的參數類型 
   第二個泛型:onProgressUpdate里的參數類型 
   第三個泛型:onPostExecute里的參數類型及doInBackground的返回類型
   * 
   * @author Kevin
   * @date 2015-10-24
   */
   class BitmapTask extends AsyncTask<Object, Integer, Bitmap> {
    private ImageView imageView;
    private String url;
    // 1.預加載, 運行在主線程
    @Override
    protected void onPreExecute() {
     super.onPreExecute();
     // System.out.println("onPreExecute");
    }
    // 2.正在加載, 運行在子線程(核心方法), 可以直接異步請求,
    //通過exccute()方法傳入參數
    @Override
    protected Bitmap doInBackground(Object... params) {
     // System.out.println("doInBackground");
     imageView = (ImageView) params[0];
     url = (String) params[1];
     imageView.setTag(url);// 打標記, 將當前imageview和url綁定在了一起
     // 開始下載圖片
     Bitmap bitmap = download(url);
     // publishProgress(values) 調用此方法實現進度更新(會回調onProgressUpdate)
     return bitmap;
    }
    // 3.更新進度的方法, 運行在主線程
    @Override
    protected void onProgressUpdate(Integer... values) {
     // 更新進度條
     super.onProgressUpdate(values);
    }
    // 4.加載結束, 運行在主線程(核心方法), 可以直接更新UI
    @Override
    protected void onPostExecute(Bitmap result) {
     // System.out.println("onPostExecute");
     if (result != null) {
      // 給imageView設置圖片
      // 由於listview的重用機制導致imageview對象可能被多個item共用,
      // 從而可能將錯誤的圖片設置給了imageView對象
      // 所以需要在此處校驗, 判斷是否是正確的圖片
      String url = (String) imageView.getTag();
      if (url.equals(this.url)) {// 判斷圖片綁定的url是否就是當前bitmap的url,
             // 如果是,說明圖片正確
       imageView.setImageBitmap(result);
       System.out.println("從網絡加載圖片啦!!!");
       // 寫本地緩存
       mLocalCacheUtils.setLocalCache(url, result);
       // 寫內存緩存
       mMemoryCacheUtils.setMemoryCache(url, result);
      }
     }
     super.onPostExecute(result);
    }
   }
   // 下載圖片
   public Bitmap download(String url) {
    HttpURLConnection conn = null;
    try {
     conn = (HttpURLConnection) new URL(url).openConnection();
     conn.setRequestMethod("GET");
     conn.setConnectTimeout(5000);// 連接超時
     conn.setReadTimeout(5000);// 讀取超時
     conn.connect();
     int responseCode = conn.getResponseCode();
     if (responseCode == 200) {
      InputStream inputStream = conn.getInputStream();
      // 根據輸入流生成bitmap對象
      Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
      return bitmap;
     }
    } catch (MalformedURLException e) {
     e.printStackTrace();
    } catch (IOException e) {
     e.printStackTrace();
    } finally {
     if (conn != null) {
      conn.disconnect();
     }
    }
    return null;
   }
  }
 
 
 
其他實現:
1、利用Handler發送延時消息
 
new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
    model.executeWork(flowId, runNodeId, executeListener);
    }
   }, 300);
 
 
 
 
2、首先runOnUiThread是Activity內部的方法,在運用的時候最好指明當前環境變(Context).
 
(1) new Thread(new Runnable() {
   @Override
   public void run() {
   System.out.println(Thread.currentThread().getId());
   runOnUiThread(new Runnable() {
   @Override
   public void run() {
   Toast.makeText(RunOnUIThreadActivity.this, "UI操作...", 1000).show(); 
   }
   });
   }
   }).start();
  (2) new Thread(new Runnable() {
    @Override
    public void run() {
     if(isClose)
     return;
     if(i!=0)
     Toast.makeText(RunOnUIThreadActivity.this, i+"", 1000).show(); 
     i++;
     handler.postDelayed(this, 2000);
    }
   }).start(); 
 
 
 
上面兩個其實原理一樣,runOnUiThread這個會調用父類中的
 
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
    mHandler.post(action);
    } else {
    action.run();
    }
   }
 
 
首先判斷是否是UI線程,不是的話就post,如果是的話就正常運行該線程.
只要經過主線程中的Handler.post或者postDelayed處理線程runnable則都可以將其轉為UI主線程.
再說Handler的機制就是來處理線程與UI通訊的.
 
 

二、接口與回調

接口與回調就是傳值、通知的過程
1、以內部類的形式
 
class A{
  int i=0;
  //2、聲明接口變量
  private Inner inner;
  //1、定義接口(可以新建一個)
  public interface Inner{
   public void method(int i);
  }
  //3、設置回調監聽
  public void setInner(Inner inner){
   this.inner=inner;
  }
  //4、回調接口方法
  public void CallBack(){
   inner.method(i);
  }
 }
 class B{
  setInner(new Inner(){
   public void method(int i){
    //根據需求處理變量i
   }
  });
 }
 
 
 
2、各個類的形式

//傳遞數據的接口
public interface Inner(){
    void method(int i);
}
//持有數據的類
public class A{
    protected Inner inner;
    //給inner賦值
    public void setInner(Inner inner){
        this.inner=inner;
    }
    //給最終的結果賦值
    public void setData(int result){
        inner.method(result);以調用方法接口的形式
    }
}
//想要獲取數據的類
public class B implements Inner{
    A a=new A();
    a.setInner(this);
    @Override
    void method(int i){
        ......;//獲取到數據i,以重寫接口方法的形式
    }
}
 
 

三、同步和異步的區別

1、Java關鍵字volatile(不穩定):
    在當前的Java內存模型下,線程可以把變量保存在本地內存(比如機器的寄存器)中,而不是直接在主存中進行讀寫。這就可能造成一個線程在主存中修改了一個變量的值,而另外一個線程還繼續使用它在寄存器中的變量值的拷貝,造成數據的不一致。
代碼示例:

public class TestWithVolatile { 
    private static volatile boolean bChanged; 
 
    public static void main(String[] args) throws InterruptedException { 
        new Thread() { 
 
            @Override 
            public void run() { 
                for (;;) { 
                    if (bChanged == !bChanged) { 
                        System.out.println("!="); 
                        System.exit(0); 
                    }  
                }  
            }  
        }.start(); 
        Thread.sleep(1); 
        new Thread() { 
 
            @Override 
            public void run() { 
                for (;;) { 
                    bChanged = !bChanged; 
                }  
            }  
        }.start(); 
    }  
 
}  
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM