1. 程式人生 > >Android DiskLruCache完全解析,硬碟快取的最佳方案

Android DiskLruCache完全解析,硬碟快取的最佳方案

概述

記得在很早之前,我有寫過一篇文章Android高效載入大圖、多圖解決方案,有效避免程式OOM,這篇文章是翻譯自Android Doc的,其中防止多圖OOM的核心解決思路就是使用LruCache技術。但LruCache只是管理了記憶體中圖片的儲存與釋放,如果圖片從記憶體中被移除的話,那麼又需要從網路上重新載入一次圖片,這顯然非常耗時。對此,Google又提供了一套硬碟快取的解決方案:DiskLruCache(非Google官方編寫,但獲得官方認證)。只可惜,Android Doc中並沒有對DiskLruCache的用法給出詳細的說明,而網上關於DiskLruCache的資料也少之又少,因此今天我準備專門寫一篇部落格來詳細講解DiskLruCache的用法,以及分析它的工作原理,這應該也是目前網上關於DiskLruCache最詳細的資料了。

那麼我們先來看一下有哪些應用程式已經使用了DiskLruCache技術。在我所接觸的應用範圍裡,Dropbox、Twitter、網易新聞等都是使用DiskLruCache來進行硬碟快取的,其中Dropbox和Twitter大多數人應該都沒用過,那麼我們就從大家最熟悉的網易新聞開始著手分析,來對DiskLruCache有一個最初的認識吧。

初探

相信所有人都知道,網易新聞中的資料都是從網路上獲取的,包括了很多的新聞內容和新聞圖片,如下圖所示:


但是不知道大家有沒有發現,這些內容和圖片在從網路上獲取到之後都會存入到本地快取中,因此即使手機在沒有網路的情況下依然能夠加載出以前瀏覽過的新聞。而使用的快取技術不用多說,自然是DiskLruCache了,那麼首先第一個問題,這些資料都被快取在了手機的什麼位置呢?

其實DiskLruCache並沒有限制資料的快取位置,可以自由地進行設定,但是通常情況下多數應用程式都會將快取的位置選擇為 /sdcard/Android/data/<application package>/cache 這個路徑。選擇在這個位置有兩點好處:第一,這是儲存在SD卡上的,因此即使快取再多的資料也不會對手機的內建儲存空間有任何影響,只要SD卡空間足夠就行。第二,這個路徑被Android系統認定為應用程式的快取路徑,當程式被解除安裝的時候,這裡的資料也會一起被清除掉,這樣就不會出現刪除程式之後手機上還有很多殘留資料的問題。

那麼這裡還是以網易新聞為例,它的客戶端的包名是com.netease.newsreader.activity,因此資料快取地址就應該是 /sdcard/Android/data/com.netease.newsreader.activity/cache ,我們進入到這個目錄中看一下,結果如下圖所示:


可以看到有很多個資料夾,因為網易新聞對多種型別的資料都進行了快取,這裡簡單起見我們只分析圖片快取就好,所以進入到bitmap資料夾當中。然後你將會看到一堆檔名很長的檔案,這些檔案命名沒有任何規則,完全看不懂是什麼意思,但如果你一直向下滾動,將會看到一個名為journal的檔案,如下圖所示:


那麼這些檔案到底都是什麼呢?看到這裡相信有些朋友已經是一頭霧水了,這裡我簡單解釋一下。上面那些檔名很長的檔案就是一張張快取的圖片,每個檔案都對應著一張圖片,而journal檔案是DiskLruCache的一個日誌檔案,程式對每張圖片的操作記錄都存放在這個檔案中,基本上看到journal這個檔案就標誌著該程式使用DiskLruCache技術了。

下載

好了,對DiskLruCache有了最初的認識之後,下面我們來學習一下DiskLruCache的用法吧。由於DiskLruCache並不是由Google官方編寫的,所以這個類並沒有被包含在Android API當中,我們需要將這個類從網上下載下來,然後手動新增到專案當中。DiskLruCache的原始碼在Google Source上,地址如下:

如果Google Source打不開的話,也可以點選這裡下載DiskLruCache的原始碼。下載好了原始碼之後,只需要在專案中新建一個libcore.io包,然後將DiskLruCache.java檔案複製到這個包中即可。

開啟快取

這樣的話我們就把準備工作做好了,下面看一下DiskLruCache到底該如何使用。首先你要知道,DiskLruCache是不能new出例項的,如果我們要建立一個DiskLruCache的例項,則需要呼叫它的open()方法,介面如下所示:

public static DiskLruCache open(File directory, int appVersion, int valueCount, long maxSize)

open()方法接收四個引數,第一個引數指定的是資料的快取地址,第二個引數指定當前應用程式的版本號,第三個引數指定同一個key可以對應多少個快取檔案,基本都是傳1,第四個引數指定最多可以快取多少位元組的資料。

其中快取地址前面已經說過了,通常都會存放在 /sdcard/Android/data/<application package>/cache 這個路徑下面,但同時我們又需要考慮如果這個手機沒有SD卡,或者SD正好被移除了的情況,因此比較優秀的程式都會專門寫一個方法來獲取快取地址,如下所示:

public File getDiskCacheDir(Context context, String uniqueName) {
	String cachePath;
	if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
			|| !Environment.isExternalStorageRemovable()) {
		cachePath = context.getExternalCacheDir().getPath();
	} else {
		cachePath = context.getCacheDir().getPath();
	}
	return new File(cachePath + File.separator + uniqueName);
}

可以看到,當SD卡存在或者SD卡不可被移除的時候,就呼叫getExternalCacheDir()方法來獲取快取路徑,否則就呼叫getCacheDir()方法來獲取快取路徑。前者獲取到的就是 /sdcard/Android/data/<application package>/cache 這個路徑,而後者獲取到的是 /data/data/<application package>/cache 這個路徑。

接著又將獲取到的路徑和一個uniqueName進行拼接,作為最終的快取路徑返回。那麼這個uniqueName又是什麼呢?其實這就是為了對不同型別的資料進行區分而設定的一個唯一值,比如說在網易新聞快取路徑下看到的bitmap、object等資料夾。

接著是應用程式版本號,我們可以使用如下程式碼簡單地獲取到當前應用程式的版本號:

	public int getAppVersion(Context context) {
		try {
			PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
			return info.versionCode;
		} catch (NameNotFoundException e) {
			e.printStackTrace();
		}
		return 1;
	}
需要注意的是,每當版本號改變,快取路徑下儲存的所有資料都會被清除掉,因為DiskLruCache認為當應用程式有版本更新的時候,所有的資料都應該從網上重新獲取。

後面兩個引數就沒什麼需要解釋的了,第三個引數傳1,第四個引數通常傳入10M的大小就夠了,這個可以根據自身的情況進行調節。

因此,一個非常標準的open()方法就可以這樣寫:

DiskLruCache mDiskLruCache = null;
try {
	File cacheDir = getDiskCacheDir(context, "bitmap");
	if (!cacheDir.exists()) {
		cacheDir.mkdirs();
	}
	mDiskLruCache = DiskLruCache.open(cacheDir, getAppVersion(context), 1, 10 * 1024 * 1024);
} catch (IOException e) {
	e.printStackTrace();
}

首先呼叫getDiskCacheDir()方法獲取到快取地址的路徑,然後判斷一下該路徑是否存在,如果不存在就建立一下。接著呼叫DiskLruCache的open()方法來建立例項,並把四個引數傳入即可。

有了DiskLruCache的例項之後,我們就可以對快取的資料進行操作了,操作型別主要包括寫入、訪問、移除等,我們一個個進行學習。

寫入快取

先來看寫入,比如說現在有一張圖片,地址是http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg,那麼為了將這張圖片下載下來,就可以這樣寫:

private boolean downloadUrlToStream(String urlString, OutputStream outputStream) {
	HttpURLConnection urlConnection = null;
	BufferedOutputStream out = null;
	BufferedInputStream in = null;
	try {
		final URL url = new URL(urlString);
		urlConnection = (HttpURLConnection) url.openConnection();
		in = new BufferedInputStream(urlConnection.getInputStream(), 8 * 1024);
		out = new BufferedOutputStream(outputStream, 8 * 1024);
		int b;
		while ((b = in.read()) != -1) {
			out.write(b);
		}
		return true;
	} catch (final IOException e) {
		e.printStackTrace();
	} finally {
		if (urlConnection != null) {
			urlConnection.disconnect();
		}
		try {
			if (out != null) {
				out.close();
			}
			if (in != null) {
				in.close();
			}
		} catch (final IOException e) {
			e.printStackTrace();
		}
	}
	return false;
}
這段程式碼相當基礎,相信大家都看得懂,就是訪問urlString中傳入的網址,並通過outputStream寫入到本地。有了這個方法之後,下面我們就可以使用DiskLruCache來進行寫入了,寫入的操作是藉助DiskLruCache.Editor這個類完成的。類似地,這個類也是不能new的,需要呼叫DiskLruCache的edit()方法來獲取例項,介面如下所示:
public Editor edit(String key) throws IOException

可以看到,edit()方法接收一個引數key,這個key將會成為快取檔案的檔名,並且必須要和圖片的URL是一一對應的。那麼怎樣才能讓key和圖片的URL能夠一一對應呢?直接使用URL來作為key?不太合適,因為圖片URL中可能包含一些特殊字元,這些字元有可能在命名檔案時是不合法的。其實最簡單的做法就是將圖片的URL進行MD5編碼,編碼後的字串肯定是唯一的,並且只會包含0-F這樣的字元,完全符合檔案的命名規則。

那麼我們就寫一個方法用來將字串進行MD5編碼,程式碼如下所示:

public String hashKeyForDisk(String key) {
	String cacheKey;
	try {
		final MessageDigest mDigest = MessageDigest.getInstance("MD5");
		mDigest.update(key.getBytes());
		cacheKey = bytesToHexString(mDigest.digest());
	} catch (NoSuchAlgorithmException e) {
		cacheKey = String.valueOf(key.hashCode());
	}
	return cacheKey;
}

private String bytesToHexString(byte[] bytes) {
	StringBuilder sb = new StringBuilder();
	for (int i = 0; i < bytes.length; i++) {
		String hex = Integer.toHexString(0xFF & bytes[i]);
		if (hex.length() == 1) {
			sb.append('0');
		}
		sb.append(hex);
	}
	return sb.toString();
}
程式碼很簡單,現在我們只需要呼叫一下hashKeyForDisk()方法,並把圖片的URL傳入到這個方法中,就可以得到對應的key了。

因此,現在就可以這樣寫來得到一個DiskLruCache.Editor的例項:

String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";
String key = hashKeyForDisk(imageUrl);
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
有了DiskLruCache.Editor的例項之後,我們可以呼叫它的newOutputStream()方法來建立一個輸出流,然後把它傳入到downloadUrlToStream()中就能實現下載並寫入快取的功能了。注意newOutputStream()方法接收一個index引數,由於前面在設定valueCount的時候指定的是1,所以這裡index傳0就可以了。在寫入操作執行完之後,我們還需要呼叫一下commit()方法進行提交才能使寫入生效,呼叫abort()方法的話則表示放棄此次寫入。

因此,一次完整寫入操作的程式碼如下所示:

new Thread(new Runnable() {
	@Override
	public void run() {
		try {
			String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";
			String key = hashKeyForDisk(imageUrl);
			DiskLruCache.Editor editor = mDiskLruCache.edit(key);
			if (editor != null) {
				OutputStream outputStream = editor.newOutputStream(0);
				if (downloadUrlToStream(imageUrl, outputStream)) {
					editor.commit();
				} else {
					editor.abort();
				}
			}
			mDiskLruCache.flush();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}).start();
由於這裡呼叫了downloadUrlToStream()方法來從網路上下載圖片,所以一定要確保這段程式碼是在子執行緒當中執行的。注意在程式碼的最後我還呼叫了一下flush()方法,這個方法並不是每次寫入都必須要呼叫的,但在這裡卻不可缺少,我會在後面說明它的作用。

現在的話快取應該是已經成功寫入了,我們進入到SD卡上的快取目錄裡看一下,如下圖所示:


可以看到,這裡有一個檔名很長的檔案,和一個journal檔案,那個檔名很長的檔案自然就是快取的圖片了,因為是使用了MD5編碼來進行命名的。

讀取快取

快取已經寫入成功之後,接下來我們就該學習一下如何讀取了。讀取的方法要比寫入簡單一些,主要是藉助DiskLruCache的get()方法實現的,介面如下所示:

public synchronized Snapshot get(String key) throws IOException
很明顯,get()方法要求傳入一個key來獲取到相應的快取資料,而這個key毫無疑問就是將圖片URL進行MD5編碼後的值了,因此讀取快取資料的程式碼就可以這樣寫:
String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";
String key = hashKeyForDisk(imageUrl);
DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
很奇怪的是,這裡獲取到的是一個DiskLruCache.Snapshot物件,這個物件我們該怎麼利用呢?很簡單,只需要呼叫它的getInputStream()方法就可以得到快取檔案的輸入流了。同樣地,getInputStream()方法也需要傳一個index引數,這裡傳入0就好。有了檔案的輸入流之後,想要把快取圖片顯示到介面上就輕而易舉了。所以,一段完整的讀取快取,並將圖片載入到介面上的程式碼如下所示:
try {
	String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";
	String key = hashKeyForDisk(imageUrl);
	DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key);
	if (snapShot != null) {
		InputStream is = snapShot.getInputStream(0);
		Bitmap bitmap = BitmapFactory.decodeStream(is);
		mImage.setImageBitmap(bitmap);
	}
} catch (IOException e) {
	e.printStackTrace();
}

我們使用了BitmapFactory的decodeStream()方法將檔案流解析成Bitmap物件,然後把它設定到ImageView當中。如果執行一下程式,將會看到如下效果:


OK,圖片已經成功顯示出來了。注意這是我們從本地快取中載入的,而不是從網路上載入的,因此即使在你手機沒有聯網的情況下,這張圖片仍然可以顯示出來。

移除快取

學習完了寫入快取和讀取快取的方法之後,最難的兩個操作你就都已經掌握了,那麼接下來要學習的移除快取對你來說也一定非常輕鬆了。移除快取主要是藉助DiskLruCache的remove()方法實現的,介面如下所示:

public synchronized boolean remove(String key) throws IOException
相信你已經相當熟悉了,remove()方法中要求傳入一個key,然後會刪除這個key對應的快取圖片,示例程式碼如下:
try {
	String imageUrl = "http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg";  
	String key = hashKeyForDisk(imageUrl);  
	mDiskLruCache.remove(key);
} catch (IOException e) {
	e.printStackTrace();
}

用法雖然簡單,但是你要知道,這個方法我們並不應該經常去呼叫它。因為你完全不需要擔心快取的資料過多從而佔用SD卡太多空間的問題,DiskLruCache會根據我們在呼叫open()方法時設定的快取最大值來自動刪除多餘的快取。只有你確定某個key對應的快取內容已經過期,需要從網路獲取最新資料的時候才應該呼叫remove()方法來移除快取。

其它API

除了寫入快取、讀取快取、移除快取之外,DiskLruCache還提供了另外一些比較常用的API,我們簡單學習一下。

1. size()

這個方法會返回當前快取路徑下所有快取資料的總位元組數,以byte為單位,如果應用程式中需要在介面上顯示當前快取資料的總大小,就可以通過呼叫這個方法計算出來。比如網易新聞中就有這樣一個功能,如下圖所示:


2.flush()

這個方法用於將記憶體中的操作記錄同步到日誌檔案(也就是journal檔案)當中。這個方法非常重要,因為DiskLruCache能夠正常工作的前提就是要依賴於journal檔案中的內容。前面在講解寫入快取操作的時候我有呼叫過一次這個方法,但其實並不是每次寫入快取都要呼叫一次flush()方法的,頻繁地呼叫並不會帶來任何好處,只會額外增加同步journal檔案的時間。比較標準的做法就是在Activity的onPause()方法中去呼叫一次flush()方法就可以了。

3.close()

這個方法用於將DiskLruCache關閉掉,是和open()方法對應的一個方法。關閉掉了之後就不能再呼叫DiskLruCache中任何操作快取資料的方法,通常只應該在Activity的onDestroy()方法中去呼叫close()方法。

4.delete()

這個方法用於將所有的快取資料全部刪除,比如說網易新聞中的那個手動清理快取功能,其實只需要呼叫一下DiskLruCache的delete()方法就可以實現了。

解讀journal

前面已經提到過,DiskLruCache能夠正常工作的前提就是要依賴於journal檔案中的內容,因此,能夠讀懂journal檔案對於我們理解DiskLruCache的工作原理有著非常重要的作用。那麼journal檔案中的內容到底是什麼樣的呢?我們來開啟瞧一瞧吧,如下圖所示:


由於現在只快取了一張圖片,所以journal中並沒有幾行日誌,我們一行行進行分析。第一行是個固定的字串“libcore.io.DiskLruCache”,標誌著我們使用的是DiskLruCache技術。第二行是DiskLruCache的版本號,這個值是恆為1的。第三行是應用程式的版本號,我們在open()方法裡傳入的版本號是什麼這裡就會顯示什麼。第四行是valueCount,這個值也是在open()方法中傳入的,通常情況下都為1。第五行是一個空行。前五行也被稱為journal檔案的頭,這部分內容還是比較好理解的,但是接下來的部分就要稍微動點腦筋了。

第六行是以一個DIRTY字首開始的,後面緊跟著快取圖片的key。通常我們看到DIRTY這個字樣都不代表著什麼好事情,意味著這是一條髒資料。沒錯,每當我們呼叫一次DiskLruCache的edit()方法時,都會向journal檔案中寫入一條DIRTY記錄,表示我們正準備寫入一條快取資料,但不知結果如何。然後呼叫commit()方法表示寫入快取成功,這時會向journal中寫入一條CLEAN記錄,意味著這條“髒”資料被“洗乾淨了”,呼叫abort()方法表示寫入快取失敗,這時會向journal中寫入一條REMOVE記錄。也就是說,每一行DIRTY的key,後面都應該有一行對應的CLEAN或者REMOVE的記錄,否則這條資料就是“髒”的,會被自動刪除掉。

如果你足夠細心的話應該還會注意到,第七行的那條記錄,除了CLEAN字首和key之外,後面還有一個152313,這是什麼意思呢?其實,DiskLruCache會在每一行CLEAN記錄的最後加上該條快取資料的大小,以位元組為單位。152313也就是我們快取的那張圖片的位元組數了,換算出來大概是148.74K,和快取圖片剛剛好一樣大,如下圖所示:


前面我們所學的size()方法可以獲取到當前快取路徑下所有快取資料的總位元組數,其實它的工作原理就是把journal檔案中所有CLEAN記錄的位元組數相加,求出的總合再把它返回而已。

除了DIRTY、CLEAN、REMOVE之外,還有一種字首是READ的記錄,這個就非常簡單了,每當我們呼叫get()方法去讀取一條快取資料時,就會向journal檔案中寫入一條READ記錄。因此,像網易新聞這種圖片和資料量都非常大的程式,journal檔案中就可能會有大量的READ記錄。

那麼你可能會擔心了,如果我不停頻繁操作的話,就會不斷地向journal檔案中寫入資料,那這樣journal檔案豈不是會越來越大?這倒不必擔心,DiskLruCache中使用了一個redundantOpCount變數來記錄使用者操作的次數,每執行一次寫入、讀取或移除快取的操作,這個變數值都會加1,當變數值達到2000的時候就會觸發重構journal的事件,這時會自動把journal中一些多餘的、不必要的記錄全部清除掉,保證journal檔案的大小始終保持在一個合理的範圍內。

好了,這樣的話我們就算是把DiskLruCache的用法以及簡要的工作原理分析完了。至於DiskLruCache的原始碼還是比較簡單的, 限於篇幅原因就不在這裡展開了,感興趣的朋友可以自己去摸索。下一篇文章中,我會帶著大家通過一個專案實戰的方式來更加深入地理解DiskLruCache的用法。感興趣的朋友請繼續閱讀 Android照片牆完整版,完美結合LruCache和DiskLruCache 。

關注我的技術公眾號,每天都有優質技術文章推送。關注我的娛樂公眾號,工作、學習累了的時候放鬆一下自己。

微信掃一掃下方二維碼即可關注:

        

相關推薦

Android DiskLruCache完全解析硬碟快取最佳方案

概述記得在很早之前,我有寫過一篇文章Android高效載入大圖、多圖解決方案,有效避免程式OOM,這篇文章是翻譯自Android Doc的,其中防止多圖OOM的核心解決思路就是使用LruCache技術。但LruCache只是管理了記憶體中圖片的儲存與釋放,如果圖片從記憶體中被

Android ActionBar完全解析使用官方推薦的最佳導航欄(下)

本篇文章主要內容來自於Android Doc,我翻譯之後又做了些加工,英文好的朋友也可以直接去讀原文。限於篇幅的原因,在上篇文章中我們只學習了ActionBar基礎部分的知識,那麼本篇文章我們將接著上一章的內容繼續學習,探究一下ActionBar更加高階的知識。如果你還沒有看

Android Service完全解析關於服務你所需知道的一切(下)

並且 無法 數據類型 界面 其它 wid logcat listen 程序崩潰 文章轉載至:http://blog.csdn.net/guolin_blog/article/details/9797169 這是郭霖寫的.......就是寫 "第一行代碼"的那個厲害人物,大

Android Service完全解析關於服務你所需知道的一切(上)(筆記)

參考原文:Android Service完全解析,關於服務你所需知道的一切(上) Service的基本用法 然後新建一個MyService繼承自Service,並重寫父類的onCreate()、onStartCommand()和onDestroy()方法, 可以看到,在Sta

Android Fragment完全解析關於碎片你所需知道的一切

我們都知道,Android上的介面展示都是通過Activity實現的,Activity實在是太常用了,我相信大家都已經非常熟悉了,這裡就不再贅述。但是Activity也有它的侷限性,同樣的介面在手機上顯示可能很好看,在平板上就未必了,因為平板的螢幕非常大,手機的介面放在平板上

Android Scroller完全解析關於Scroller你所需知道的一切

轉載請註明出處:http://blog.csdn.net/guolin_blog/article/details/48719871 2016大家新年好!這是今年的第一篇文章,那麼應CSDN工作人員的建議,為了能給大家帶來更好的閱讀體驗,我也是將部落格換成了寬屏

Android Service完全解析關於服務你所需知道的一切(上)

相信大多數朋友對Service這個名詞都不會陌生,沒錯,一個老練的Android程式設計師如果連Service都沒聽說過的話,那確實也太遜了。Service作為Android四大元件之一,在每一個應用程式中都扮演著非常重要的角色。它主要用於在後臺處理一些耗時的邏輯,或者去執行

Android Service完全解析關於服務你所需知道的一切(上和下)

在上一篇文章中,我們學習了Android Service相關的許多重要內容,包括Service的基本用法、Service和Activity進行通訊、Service的銷燬方式、Service與Thread的關係、以及如何建立前臺Service。以上所提到的這些知識點,基本上涵蓋了大部分日常開發工作當

Android AsyncTask完全解析帶你從原始碼的角度徹底理解

我們都知道,Android UI是執行緒不安全的,如果想要在子執行緒裡進行UI操作,就需要藉助Android的非同步訊息處理機制。之前我也寫過了一篇文章從原始碼層面分析了Android的非同步訊息處理機制,感興趣的朋友可以參考 Android Handler、Message完

Android Context完全解析你所不知道的Context的各種細節

前幾篇文章,我也是費勁心思寫了一個ListView系列的三部曲,雖然在內容上可以說是絕對的精華,但是很多朋友都表示看不懂。好吧,這

Android硬碟快取DiskLrucache完全解析

概述 記得在很早之前,我有寫過一篇文章 Android高效載入大圖、多圖解決方案,有效避免程式OOM ,這篇文章是翻譯自Android Doc的,其中防止多圖OOM的核心解決思路就是使用LruCache技術。但LruCache只是管理了記憶體中圖片的儲存與釋放,

Android檢視繪製流程完全解析帶你一步步深入瞭解View(二)

在上一篇文章中,我帶著大家一起剖析了一下LayoutInflater的工作原理,可以算是對View進行深入瞭解的第一步吧。那麼本篇文章中,我們將繼續對View進行深入探究,看一看它的繪製流程到底是什麼樣的。如果你還沒有看過我的上一篇文章,可以先去閱讀 Android Layo

Android事件分發機制完全解析帶你從原始碼的角度徹底理解(上)-郭霖

其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入了一

Android事件分發機制完全解析帶你從原始碼的角度徹底理解(下)-郭霖

記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。 還未閱讀過的朋友,請先參考 Android事件分發機制完全解析,帶你從原始碼的角度徹底理解(上) 。 那麼今天我們將繼

Android Volley完全解析帶你從原始碼的角度理解Volley

                經過前三篇文章的學習,Volley的用法我們已經掌握的差不多了,但是對於Volley的工作原理,恐怕有很多朋友還不是很清楚。因此,本篇文章中我們就來一起閱讀一下Volley的原始碼,將它的工作流程整體地梳理一遍。同時,這也是Volley系列的最後一篇文章了。其實,Volley的

Android任務和返回棧完全解析細數那些你所不知道的細節

本篇文章主要內容來自於Android Doc,我翻譯之後又做了些加工,英文好的朋友也可以直接去讀原文。任務和返回棧一個應用程式當中通常都會包含很多個Activity,每個Activity都應該設計成為一個具有特定的功能,並且可以讓使用者進行操作的元件。另外,Activity之

Android ListView工作原理完全解析帶你從原始碼的角度徹底理解

在Android所有常用的原生控制元件當中,用法最複雜的應該就是ListView了,它專門用於處理那種內容元素很多,手機螢幕無法展示出所有內容的情況。ListView可以使用列表的形式來展示內容,超出螢幕部分的內容只需要通過手指滑動就可以移動到螢幕內了。另外ListView還

Android事件分發機制完全解析帶你從原始碼的角度徹底理解(上)

其實我一直準備寫一篇關於Android事件分發機制的文章,從我的第一篇部落格開始,就零零散散在好多地方使用到了Android事件分發的知識。也有好多朋友問過我各種問題,比如:onTouch和onTouchEvent有什麼區別,又該如何使用?為什麼給ListView引入

Android事件分發機制完全解析帶你從原始碼的角度徹底理解(下)

記得在前面的文章中,我帶大家一起從原始碼的角度分析了Android中View的事件分發機制,相信閱讀過的朋友對View的事件分發已經有比較深刻的理解了。那麼今天我們將繼續上次未完成的話題,從原始碼的角度分析ViewGroup的事件分發。首先我們來探討一下,什麼是ViewGro

Android屬性動畫完全解析Interpolator和ViewPropertyAnimator的用法

大家好,歡迎繼續回到Android屬性動畫完全解析。在上一篇文章當中我們學習了屬性動畫的一些進階技巧,包括ValueAnimator和ObjectAnimator的高階用法,那麼除了這些之外,當然還有一些其它的高階技巧在等著我們學習,因此本篇文章就對整個屬性動畫完