1. 程式人生 > >檔案快取與記憶體快取

檔案快取與記憶體快取

<strong><span style="font-size:24px;">1.java的引用型別:</span></strong>
<p style="border-width: 0px; padding-top: 0px; padding-bottom: 0px; margin-top: 0px; margin-bottom: 8px; list-style: none; text-indent: 2em; color: rgb(51, 51, 51); font-family: 宋體; font-size: 14px; line-height: 28px; background-color: rgb(249, 249, 249);">1)強引用(StrongReference)
    強引用是使用最普遍的引用。如果一個物件具有強引用,那垃圾回收器絕不會回收它。當記憶體空間不足,Java<a target=_blank href="http://www.2cto.com/os/xuniji/" target="_blank" class="keylink" style="color: rgb(51, 51, 51); text-decoration: none;">虛擬機器</a>寧願丟擲OutOfMemoryError錯誤,使程式異常終止,也不會靠隨意回收具有強引用的物件來解決記憶體不足的問題。
    2)軟引用(SoftReference)
    如果一個物件只具有軟引用,則記憶體空間足夠,垃圾回收器就不會回收它;如果記憶體空間不足了,就會回收這些物件的記憶體。只要垃圾回收器沒有回收它,該物件就可以被程式使用。軟引用可用來實現記憶體敏感的快取記憶體(下文給出示例)。
    軟引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果軟引用所引用的物件被垃圾回收器回收,Java虛擬機器就會把這個軟引用加入到與之關聯的引用佇列中。</p><p style="border-width: 0px; padding-top: 0px; padding-bottom: 0px; margin-top: 0px; margin-bottom: 8px; list-style: none; text-indent: 2em; color: rgb(51, 51, 51); font-family: 宋體; font-size: 14px; line-height: 28px; background-color: rgb(249, 249, 249);">3) 弱引用(WeakReference)
    弱引用與軟引用的區別在於:弱引用的物件擁有更短暫的生命週期。在垃圾回收器執行緒掃描它所管轄的記憶體區域的過程中,一旦發現了只具有弱引用的物件,不管當前記憶體空間足夠與否,都會回收它的記憶體。不過,由於垃圾回收器是一個優先順序很低的執行緒,因此不一定會很快發現那些只具有弱引用的物件。
    弱引用可以和一個引用佇列(ReferenceQueue)聯合使用,如果弱引用所引用的物件被垃圾回收,Java虛擬機器就會把這個弱引用加入到與之關聯的引用佇列中。</p><p style="border-width: 0px; padding-top: 0px; padding-bottom: 0px; margin-top: 0px; margin-bottom: 8px; list-style: none; text-indent: 2em; color: rgb(51, 51, 51); font-family: 宋體; font-size: 14px; line-height: 28px; background-color: rgb(249, 249, 249);"> 4)虛引用(PhantomReference)
    “虛引用”顧名思義,就是形同虛設,與其他幾種引用都不同,虛引用並不會決定物件的生命週期。如果一個物件僅持有虛引用,那麼它就和沒有任何引用一樣,在任何時候都可能被垃圾回收器回收。
虛引用主要用來跟蹤物件被垃圾回收器回收的活動。虛引用與軟引用和弱引用的一個區別在於:虛引用必須和引用佇列(ReferenceQueue)聯合使用。當垃圾回收器準備回收一個物件時,如果發現它還有虛引用,就會在回收物件的記憶體之前,把這個虛引用加入到與之 關聯的引用佇列中。</p>----
軟引用:
結合載入歌手圖片來說明:
1.先建立一個MAP集合,用來儲存圖片的快取檔案
<pre name="code" class="html">	//記憶體快取 使用軟引用
	private Map<String, SoftReference<Bitmap>> softcache=new HashMap<String, SoftReference<Bitmap>>();
2.使用時 先在獲取到圖片的Bitmap資料位置進行資料的放入快取操作。
<span style="white-space:pre">	</span>//將獲取的bitmap存入記憶體快取
        softcache.put(task.imagePath, new SoftReference<Bitmap>(bitmap));
3.快取原理就是:如果檔案在快取中有,就直接從快取中獲取資料,由於是軟引用,當系統記憶體不足時,記憶體回收機制GC就可能會銷燬掉部分快取資料,沒有時,才需要重新載入圖片資料。所以在adapter的getview中 先判斷後、然後就可以直接使用控制元件載入bitmap資料
	//先去記憶體快取檢視是否有此圖的快取圖片
		SoftReference<Bitmap> ref= softcache.get(path); //獲取快取中的快取物件
		if(ref!=null){
			//從快取物件中取出快取圖片
			Bitmap b= ref.get();
			if(b!=null){//有圖
				holder.ivImage.setImageBitmap(b); //直接載入記憶體的快取的圖片
				Log.d("tedu", "從記憶體快取讀取。。");
				
				return convertView ;//有則直接返回
				
			}
		}
<strong><span style="font-size:18px;">2.檔案引用</span></strong>
檔案引用就是將開始讀取的資料放入app對應的快取資料夾內,當下次讀取時,就可以直接載入顯示 節省流量。
1.先建立兩個方法,一個是用於儲存資料到快取,一個是用於通過快取檔案路徑去取出資料用於顯示
<pre name="code" class="html">	//用於儲存檔案	
	public static void saveMap(Bitmap bitmap,File file) throws FileNotFoundException{
		if(!file.getParentFile().exists()){
			//獲取父目錄資料夾是否存在,不存在就建立
			file.getParentFile().mkdirs();
		}
		//建立檔案輸出流 儲存檔案
		FileOutputStream fos=new FileOutputStream(file);
		
		//將bitmap檔案壓縮並輸出
		bitmap.compress(CompressFormat.JPEG, 100, fos);
		
	}
<span style="white-space:pre">	</span>//用於獲取資料的方法
<pre name="code" class="html">	public static Bitmap getBitmapByFile(File file){
		
		if(!file.exists()){
			return null;
		}
		
		//根據檔案去找到檔案快取中的圖片、
		Bitmap bitmap=BitmapFactory.decodeFile(file.getAbsolutePath());				
		return bitmap;
	}
	

2.然後與軟引用一樣。在獲取到資料的位置,呼叫儲存方法,將資料放入快取檔案件夾內:
<pre name="code" class="html"><span style="white-space:pre">					</span>//將獲取的圖再存入檔案快取中
					String path=task.imagePath;//獲取當前任務中的檔案路徑
					String filename=path.substring(path.lastIndexOf("/")+1);//不包含/
					File file=new File(context.getCacheDir(), "images/"+filename);
					//呼叫方法 儲存檔案
					BitMapUtil.saveMap(bitmap, file);

3.同樣的方法,在getview中判斷後 就可以直接使用控制元件進行資料的顯示:
<span style="white-space:pre">		</span>//從檔案中讀取圖片
		String filename=path.substring(path.lastIndexOf("/")+1);
		File file=new File(context.getCacheDir(),"images/"+filename);
		Bitmap map=BitMapUtil.getBitmapByFile(file);
		if(map!=null){
			holder.ivImage.setImageBitmap(map);
			Log.d("tedu", "從檔案快取讀取。。");
			//從檔案讀時同時儲存到記憶體快取。加快讀取速度
			softcache.put(path, new SoftReference<Bitmap>(map));
			
			return convertView;
		}
上面這段程式碼中,加了一句當從檔案獲取資料快取資料時,重新將資料放入記憶體的快取集合中 ,這樣可以加快圖片的顯示,從記憶體中獲取是最快的!


原碼:
package com.wuxs.three_musicplayer_v4.Adapter;

import java.io.File;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.wuxs.three_musicplayer_v4.R;
import com.wuxs.three_musicplayer_v4.entity.Music;
import com.wuxs.three_musicplayer_v4.util.BitMapUtil;
import com.wuxs.three_musicplayer_v4.util.MusicHttpUtil;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
public class MusicsAdapter extends BaseAdapter {

	private Context context;
	private List<Music> musics;
	private LayoutInflater inflater;
	private ListView lvmusic;// 為了獲取標記的imageview
	//建立任務集合。
	private List<LoadImageTask> tasks=new ArrayList<LoadImageTask>();
	private Thread thread;
	private boolean isLoop=true;
	//記憶體快取 使用軟引用
	private Map<String, SoftReference<Bitmap>> softcache=new HashMap<String, SoftReference<Bitmap>>();
	
	

	private Handler handler=new Handler(new Handler.Callback() {
		
		@Override
		public boolean handleMessage(Message msg) {
			switch (msg.what) {
			case LOAD_IMAGE_SUCCESS: //載入ok
				  LoadImageTask task= (LoadImageTask) msg.obj;
				   Bitmap bitmap= task.bitmap; //資料ok
				//取出
				   ImageView ivImage= (ImageView) lvmusic.findViewWithTag(task.imagePath);
				   if(ivImage!=null){
					   if(bitmap!=null){
						   ivImage.setImageBitmap(bitmap);						   
					   }
				   }
				
				break;
			}
	
			return false;
		}
	});

	private static final int LOAD_IMAGE_SUCCESS=1;
	
	
	
	public MusicsAdapter(final Context context, List<Music> musics,ListView lvmusic) {
		super();
		this.context = context;
		this.musics = musics;
		this.inflater = LayoutInflater.from(context);
		this.lvmusic=lvmusic;
		
		thread=new Thread(){	
			
			@Override
			public void run() {
				//在工作執行緒處理載入任務
				while(isLoop){//讓他不斷迴圈判斷 類似handler的looper
				if(!tasks.isEmpty()){//如果任務集合不為空
					//獲取bitmap資料
					LoadImageTask task=tasks.remove(0); //每次移除第一個任務出來
				//	Bitmap bitmap=getBitMap(task.imagePath);//改為採用壓縮格式圖片獲取
					try {
					Bitmap bitmap = BitMapUtil.getYasuoPhoto(task.imagePath,40,40);
						//將bitmap資料封裝進去
						task.bitmap=bitmap;
					//在這個位置獲取了圖片的bitmap資訊 在此加上記憶體快取
					//將獲取的bitmap存入記憶體快取
					softcache.put(task.imagePath, new SoftReference<Bitmap>(bitmap));
					//將獲取的圖再存入檔案快取中
					String path=task.imagePath;//獲取當前任務中的檔案路徑
					String filename=path.substring(path.lastIndexOf("/")+1);//不包含/
					File file=new File(context.getCacheDir(), "images/"+filename);
					//呼叫方法 儲存檔案
					BitMapUtil.saveMap(bitmap, file);
					
					} catch (Exception e) {
						e.printStackTrace();
					}
					//發出訊息
					Message mes=Message.obtain();
					mes.what=LOAD_IMAGE_SUCCESS;
					mes.obj=task;
					handler.sendMessage(mes);
					
				}else{
					//任務為空時 ,讓執行緒休眠
					synchronized (thread) {
						try {
							thread.wait();
						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
				}
				
				}
				
				
			}
			
		};
		thread.start();
		
		
		
		
	}
	//獲取bitmap
	protected Bitmap getBitMap(String imagePath) {
		//呼叫方法
		try {
		String  url=imagePath;
		InputStream is= MusicHttpUtil.getXmlInputStream(url);
		Bitmap bitmap=BitmapFactory.decodeStream(is);//通過輸入流獲取圖片bitmap資料
		
		return bitmap;
		
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return musics.size();
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return musics.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		Music music=musics.get(position);
		viewHolder holder=null;
		if(convertView==null){
			convertView=inflater.inflate(R.layout.list_m, null);
			holder=new viewHolder();
			holder.ivImage=(ImageView) convertView.findViewById(R.id.ivphoto);
			holder.tvAuthor=(TextView) convertView.findViewById(R.id.tv_author);
			holder.tvTitle=(TextView) convertView.findViewById(R.id.tv_title);
			convertView.setTag(holder);
		}else {
			holder=(viewHolder) convertView.getTag();
		}
		
		holder.tvAuthor.setText(music.getAuthor());
		holder.tvTitle.setText(music.getTitle());
		
		
		
		//標記每個imageview
		String path=music.getPic_small();
		holder.ivImage.setTag(path); //用small路徑去標記imageview
		//先去記憶體快取檢視是否有此圖的快取圖片
		SoftReference<Bitmap> ref= softcache.get(path); //獲取快取中的快取物件
		if(ref!=null){
			//從快取物件中取出快取圖片
			Bitmap b= ref.get();
			if(b!=null){//有圖
				holder.ivImage.setImageBitmap(b); //直接載入記憶體的快取的圖片
				Log.d("tedu", "從記憶體快取讀取。。");
				
				return convertView ;//有則直接返回
				
			}
		}
		
		//從檔案中讀取圖片
		String filename=path.substring(path.lastIndexOf("/")+1);
		File file=new File(context.getCacheDir(),"images/"+filename);
		Bitmap map=BitMapUtil.getBitmapByFile(file);
		if(map!=null){
			holder.ivImage.setImageBitmap(map);
			Log.d("tedu", "從檔案快取讀取。。");
			//從檔案讀時同時儲存到記憶體快取。加快讀取速度
			softcache.put(path, new SoftReference<Bitmap>(map));
			
			return convertView;
		}
		//沒有則繼續發任務 子執行緒中獲取圖片	
		//在這裡不能直接使用子執行緒來顯示圖片。應為那樣會建立很多個執行緒、改為在構造方法時建立子執行緒。就會建立一個
		//在這裡要做的事就是發出一個圖片顯示任務,然後給到子執行緒,去處理。
		LoadImageTask task=new LoadImageTask();
		task.imagePath=music.getPic_small(); //將路徑設定進去
		//然後往集合裡新增該task。 也就是發出了一個圖片載入任務
		tasks.add(task);
		//喚醒休眠後的執行緒
		synchronized (thread) {
			thread.notify(); //喚醒執行緒
		}
		
		return convertView;
	}
	
	class viewHolder{
		ImageView ivImage;
		TextView tvTitle;
		TextView tvAuthor;
	}
	
	//設定任務屬性
	private class LoadImageTask{
		private String  imagePath;
		private Bitmap bitmap;
	}
	
	//停止執行緒方法
	public void StopThread(){
		isLoop=false; //停止子執行緒
		//如果執行緒在休眠 就喚醒
		synchronized (thread) {
			thread.notify();
		}
		
	}
	
	
}