一、線程的實現(異步機制、耗時操作)
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.預加載, 運行在主線程
protected void onPreExecute() {
super.onPreExecute();
// System.out.println("onPreExecute");
}
// 2.正在加載, 運行在子線程(核心方法), 可以直接異步請求,
//通過exccute()方法傳入參數
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.更新進度的方法, 運行在主線程
protected void onProgressUpdate(Integer... values) {
// 更新進度條
super.onProgressUpdate(values);
}
// 4.加載結束, 運行在主線程(核心方法), 可以直接更新UI
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() {
public void run() {
model.executeWork(flowId, runNodeId, executeListener);
}
}, 300);
2、首先runOnUiThread是Activity內部的方法,在運用的時候最好指明當前環境變(Context).
(1) new Thread(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getId());
runOnUiThread(new Runnable() {
public void run() {
Toast.makeText(RunOnUIThreadActivity.this, "UI操作...", 1000).show();
}
});
}
}).start();
(2) new Thread(new Runnable() {
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);
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() {
public void run() {
for (;;) {
if (bChanged == !bChanged) {
System.out.println("!=");
System.exit(0);
}
}
}
}.start();
Thread.sleep(1);
new Thread() {
public void run() {
for (;;) {
bChanged = !bChanged;
}
}
}.start();
}
}