編輯:關於Android編程
這次練習的是圖片的三級緩存處理。主要是避免下載圖片過大過多導致的內存洩露的問題,我們要將下載的圖片進行緩存。緩存主要分三級,是首先存儲再強引用LruCache中,存不下之後存在軟引用SoftRerences中,同時也會存在本地的SD卡中。由於LruCache和SoftRerences是存在運行時內存中,雖然容量下,但是讀取快,而且程序一旦退出,數據就會丟失。同時本地內存卡中可以存入大量數據,但是讀取慢,但是退出程序後數據任然存在本地,不會丟失。所以結合雙方不同的特性我們將其結合起來使用,所以進行圖片的三級緩存處理。
步驟:首先會從內存和本地中取出圖片,有的話可以直接顯示,如果沒有則就要通過網絡下載圖片,並存入到內存和本地中。
取出的順序,先從LruCache中讀取,如果有直接顯示,沒有的話從SoftRerences中找,有的話顯示,沒有就去本地SD卡中尋找,有的話顯示,如果也沒有那麼就只能通過網絡下載圖片。下載圖片後存入順序,先存如到LruCache和本地中,如果LruCache存滿,則將系統自動刪除的圖片存入到SoftReferences中。
LruCache:
1.再創建對象時可以指定存儲容量一般為當前運行時內存的1/8或者1/16。
2.當存滿後會自動刪除之前數據並存入新數據。
3.自身是一個相當於map集合可以存儲n組鍵值對。
SoftRerences:
1.沒有最大存儲大小,存儲大小可以根據當前設備調整。
2.如果內存也存儲滿了,也會自動刪除數據,保證能獲取新的數據。
3.本身只能存儲一個對象。兩者配合使用可以存儲更多數據。
接下來介紹以下將網絡上的數據和圖片下載後顯示到listview中的緩存操作,詳細見代碼。
主要分為:
1.MyMainActivity主函數類主要用於初始化控件,網絡下載數據初始化數據源並設置listview適配器。
2.AsyncTaskOfJson異步任務解析JSON數據類 主要通過url下載數據後json解析得到數據源。
3.AsyncTaskOfBitmap異步任務下載Bitmap圖片類主要是通過網址下載bitmap圖片並通過接口回調返回。
4.MyInterface接口定義接口方法,通過調用接口方法回傳數據。
5.DataBean對象類用於存儲對應json解析的數據存儲類。
6.HttpURLUtils類網絡請求下載工具類主要結合異步任務類進行下載操作。
7.MyBaseAdapter自定義適配器類主要是listview適配器對網絡下載的數據圖片進行緩存處理,讀取操作。
1.MyMainActivity主函數類
package com.bane.mymain;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ListView;
import com.bane.adapter.MyBaseAdapter;
import com.bane.asynctaskjson.AsyncTaskOfJson;
import com.bane.bean.DataBean;
import com.bane.myinterface.InterfaceData;
import com.example.administrator.android_picturesave.R;
import java.util.ArrayList;
import java.util.List;
//主函數類初始化控件,設置適配器和開啟網絡下載數據的任務
public class MyMainActivity extends AppCompatActivity {
//tag標簽
private static final String TAG = "===";
//創建一個list集合存儲下載的數據
private List listData = new ArrayList<>();
//網址數據為JSON
private String path = "http://lib.wap.zol.com.cn/ipj/docList/?v=6.0&class_id=0&page=4&vs=and412&retina=1";
//聲明適配器引用
private MyBaseAdapter adapter ;
//聲明listview引用
private ListView listview;
//初始化控件方法
private void assignViews() {
listview = (ListView) findViewById(R.id.listview);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my_main);
//初始化控件
assignViews();
//開啟網絡下載異步任務
startAsyncTask();
}
private void startAsyncTask() {
//創建異步任務下載數據
new AsyncTaskOfJson(new InterfaceData(){
@Override
public void getDataOfJson(List data) {
if(data!=null){
//通過接口回調的到數據之後加入都list集合中存儲
listData.addAll(data);
Log.i(TAG, "getDataOfJson: "+listData.size());
//數據回傳回來之後進行適配器的初始化
initAdapter();
}
}
}).execute(path);
}
private void initAdapter() {
//創建適配器對象
adapter = new MyBaseAdapter(this,listData);
//設置適配器
listview.setAdapter(adapter);
//刷新適配器
adapter.notifyDataSetChanged();
}
}
2.AsyncTaskOfJson異步任務解析JSON數據類
package com.bane.asynctaskjson; import android.os.AsyncTask; import android.util.Log; import com.bane.bean.DataBean; import com.bane.httputils.HttpURLUtils; import com.bane.myinterface.InterfaceData; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; import java.util.List; /** * Created by Bane on 2016/8/7 0007. * 異步任務類 * 用於下載path對應網址的數據得到相應的文字和圖片對應的網址 * (詳細解析見上篇博文代碼) */ public class AsyncTaskOfJson extends AsyncTask>{ private static final String TAG = "==="; private InterfaceData interfaceData; private List list = new ArrayList<>(); public AsyncTaskOfJson(InterfaceData interfaceData){ this.interfaceData = interfaceData; } @Override protected List doInBackground(String... params) { Log.i(TAG, "doInBackground:params "+params[0]); String json = HttpURLUtils.getDataOfString(params[0]); Log.i(TAG, "doInBackground:json "+json); try { JSONObject ob = new JSONObject(json); JSONArray array = ob.getJSONArray("list"); for (int i = 0; i < array.length(); i++) { JSONObject obj = array.getJSONObject(i); String stitle = obj.getString("stitle"); String imgsrc2 = obj.getString("imgsrc2"); Log.i(TAG, "doInBackground:stitle, imgsrc2"+stitle+","+imgsrc2); DataBean dataBean = new DataBean(stitle,imgsrc2); list.add(dataBean); } Log.i(TAG, "doInBackground: list.size()"+list.size()); return list; } catch (JSONException e) { Log.i(TAG, "doInBackground: JSONException================="); e.printStackTrace(); } return null; } @Override protected void onPostExecute(List result) { super.onPostExecute(result); if(result!=null){ interfaceData.getDataOfJson(result); } } }
package com.bane.asynctaskbitmap; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.os.AsyncTask; import com.bane.httputils.HttpURLUtils; import com.bane.myinterface.InterfaceBitmap; /** * Created by Bane on 2016/8/7 0007. * 異步任務類 * 用於下載網址對應的圖片 並用接口回調返回bitmap對象 * (詳細解析見上篇博文代碼) */ public class AsyncTaskOfBitmap extends AsyncTask{ private InterfaceBitmap interfaceBitmap; private String url; public AsyncTaskOfBitmap(InterfaceBitmap interfaceBitmap){ this.interfaceBitmap = interfaceBitmap; } @Override protected Bitmap doInBackground(String... params) { url = params[0]; byte[] buf = HttpURLUtils.getDataOfByte(url); Bitmap bitmap = BitmapFactory.decodeByteArray(buf,0,buf.length); return bitmap; } @Override protected void onPostExecute(Bitmap bitmap) { if(bitmap != null){ interfaceBitmap.getDataOfBitmap(url,bitmap); } } }
4.MyInterface接口
package com.bane.myinterface;
import android.graphics.Bitmap;
/**
* Created by Bane on 2016/8/7 0007.
* 將得到的bitmap對象和對應網址回傳
*/
public interface InterfaceBitmap {
public void getDataOfBitmap(String url,Bitmap bitmap);
}
package com.bane.myinterface;
import com.bane.bean.DataBean;
import java.util.List;
/**
* Created by Bane on 2016/8/7 0007.
* JSON解析數據回傳接口
*/
public interface InterfaceData {
public void getDataOfJson(List list);
}
5.DataBean對象類
package com.bane.bean;
/**
* Created by Bane on 2016/8/7 0007.
* bean類 根據JSON數據類型定義的類 存儲對應數據
*/
public class DataBean {
private String stitle;
private String imgsrc2;
public DataBean(String stitle, String imgsrc2) {
this.stitle = stitle;
this.imgsrc2 = imgsrc2;
}
public String getStitle() {
return stitle;
}
public String getImgsrc2() {
return imgsrc2;
}
public void setStitle(String stitle) {
this.stitle = stitle;
}
public void setImgsrc2(String imgsrc2) {
this.imgsrc2 = imgsrc2;
}
@Override
public String toString() {
return "DataBean{" +
"stitle='" + stitle + '\'' +
", imgsrc2='" + imgsrc2 + '\'' +
'}';
}
}
6.HttpURLUtils類網絡請求下載工具類
package com.bane.httputils;
import android.util.Log;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
/**
* Created by Bane on 2016/8/7 0007.
* 封裝網絡請求工具類用於可以調用類中靜態方法
* 分別返回網址對應的String數據和byte[]類型數據
* (詳細解析見上篇博文代碼)
*/
public class HttpURLUtils {
private static final String TAG = "===";
public static String getDataOfString(String path){
StringBuffer sb =null;
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(100000);
conn.connect();
if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){
InputStream is = conn.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
sb = new StringBuffer();
while ((len = is.read(buf))!=-1){
String info = new String(buf,0,len);
sb.append(info);
}
// Log.i(TAG, "getDataOfString: "+sb.toString());
return sb.toString();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
public static byte[] getDataOfByte(String path){
ByteArrayOutputStream baos = null;
try {
URL url = new URL(path);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(100000);
conn.connect();
if(conn.getResponseCode() == HttpURLConnection.HTTP_OK){
InputStream is = conn.getInputStream();
byte[] buf = new byte[1024];
int len = 0;
baos = new ByteArrayOutputStream();
while ((len = is.read(buf))!=-1){
baos.write(buf,0,len);
baos.flush();
}
// Log.i(TAG, "getDataOfString: "+baos.toByteArray());
return baos.toByteArray();
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
7.MyBaseAdapter自定義適配器類
package com.bane.adapter;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;
import android.util.LruCache;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import com.bane.asynctaskbitmap.AsyncTaskOfBitmap;
import com.bane.bean.DataBean;
import com.bane.myinterface.InterfaceBitmap;
import com.example.administrator.android_picturesave.R;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Created by Administrator on 2016/8/7 0007.
*
* 適配器類
* 用於處理網絡上下載的圖片的緩存的操作
* 主要是分三級緩存對網絡上的數據進行處理
* 以防止圖片過大導致的內存洩露
*/
public class MyBaseAdapter extends BaseAdapter{
//log標簽
private static final String TAG = "===";
//創建一個list集合用於接受構造函數傳來的list數據的集合
private List list = new ArrayList<>();
//用於接受傳來的context上下文對象
private Context context;
//首先定義好要存儲圖片的SD卡的路徑方便引用
private String path = Environment.getExternalStorageDirectory().getAbsolutePath()+"/zcr_xwz";
//聲明強引用LruCache引用 范型為String此鍵用來存儲圖片對應網址作為鍵,bitmap對象為值
private LruCache lrucache;
//創建一個HashMap對象 同理范型為String此鍵用來存儲圖片對應網址作為鍵,值為軟引用並存儲bitmap對象
private HashMap> map = new HashMap<>();
//聲明一個線程池用來同時下載多張圖片
private Executor executor;
//構造方法傳參 並初始化聲明的引用
public MyBaseAdapter(Context context, List list) {
this.context = context;
this.list = list;
//創建一個容量為3的線程池
executor = Executors.newFixedThreadPool(3);
//定義出強引用的最大大小為當前運行時內存的八分之一
int maxSize = (int) (Runtime.getRuntime().maxMemory()/8);
//創建強引用lrucache對象並指明大小和重寫方法
lrucache = new LruCache(maxSize){
/**
* 當LruCache刪除圖片時就會運行此方法
* @param evicted 是否為系統自動刪除
* @param key 被刪除的鍵
* @param oldValue 被刪除的值
* @param newValue 新加入數據
* @return
*/
@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
super.entryRemoved(evicted, key, oldValue, newValue);
//判斷是否被系統自動刪除
if(evicted) {
//將刪除的圖片存入軟引用中
SoftReference bitmapSoftReference = new SoftReference(oldValue);
map.put(key,bitmapSoftReference);
Log.i(TAG, "saveBitmap: 往SoftReference中存數據!!!!");
}
}
/**
* 參數代表要新加入LruCache的鍵值對
* @param key 加入數據的鍵
* @param value 加入數據的值
* @return 一般返回新加入的數據(圖片)的大小
*/
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
//重寫適配器中的方法返回對應list集合中的數據
@Override
public int getCount() {
return list.size();
}
@Override
public Object getItem(int position) {
return list.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
//聲明ViewHolder的應用為空
ViewHolder mHolder =null;
if(convertView==null){
//判斷如果該position下的view不存在則自主創建一個
mHolder = new ViewHolder();
//加載寫好的適配器的布局文件
convertView = LayoutInflater.from(context).inflate(R.layout.listview_layout,parent,false);
//通過convertView初始化控件對象
mHolder.textView = (TextView) convertView.findViewById(R.id.textview);
mHolder.imageView = (ImageView) convertView.findViewById(R.id.imageview);
//將創建的mHolder對象作為tag唯一標識便於復用
convertView.setTag(mHolder);
}else{
//一旦判斷view對象存在 進行復用通過tag找到對應控件對象不再創建新的對象
mHolder = (ViewHolder) convertView.getTag();
}
//此時將對應position下的控件設置屬性值
//文字為得到的list中的數據
mHolder.textView.setText(list.get(position).getStitle());
//將每個imageview對應所放的圖片的網址作為tag標識
mHolder.imageView.setTag(list.get(position).getImgsrc2());
//在數據下載回來之前要先默認將圖片設置為小機器人圖片
mHolder.imageView.setImageResource(R.mipmap.ic_launcher);
//通過getBitmap方法獲取圖片 傳入參數是相應的網址tag唯一標示取得bitmap對象
Bitmap bitmap = getBitmap(list.get(position).getImgsrc2());
if(bitmap ==null){
//若取得的bitmap對象為空則圖片從未下載過進行loadBitmap下載圖片
//傳入的參數是當前的position和復用的mHolder對象
loadBitmap(position,mHolder);
}else{
//若能從強或者軟引用或者sd卡中取得圖片則已經下載過直接將取得的圖片設置
mHolder.imageView.setImageBitmap(bitmap);
}
//返回加載了布局文件的view對象
return convertView;
}
//創建一個ViewHolder類用來listview中每個position下的view的復用
class ViewHolder{
private ImageView imageView;
private TextView textView;
}
//下載圖片的方法,將下載的圖片設置再imageview上顯示
public void loadBitmap(int position , final ViewHolder holder){
//開啟異步任務下載圖片
new AsyncTaskOfBitmap(new InterfaceBitmap() {
@Override
public void getDataOfBitmap(String url, Bitmap bitmap) {
//通過接口對調回傳bitmap對象和對應網址
//為防止復用導致的切換圖片現象
//將每次復用的holder做出判斷是否是網址對應的imageview如是則下載之後設置
if(holder.imageView.getTag().toString().equals(url)&&holder.imageView.getTag()!=null){
Log.i(TAG, "getDataOfBitmap: 下載圖片");
//將下載好的bitmap設置再imageview上
holder.imageView.setImageBitmap(bitmap);
//將下載好的圖片通過對應網址作為鍵保存
saveBitmap(url,bitmap);
}
}
//在線程池中進行3張圖片同時下載並通過list數據集合和position得到相對應的網址
}).executeOnExecutor(executor,list.get(position).getImgsrc2());
}
//存儲圖片的方法將圖片網址作為鍵bitmap對象為值
public void saveBitmap(String url,Bitmap bitmap){
//首先是存入到強引用lrucache中,lrucache本身可以存儲多組鍵值對
lrucache.put(url,bitmap);
Log.i(TAG, "saveBitmap: 往lruCache中存儲數據!!!!");
//由於網址對應的 / 會造成多級文件夾所以替換為 _
String urlName = url.replace("/","_");
//創建文件夾
File file = new File(path);
if(!file.exists()){
file.mkdirs();
}
try {
//之後也要將下載好的圖片通過io流存入到本地的內存卡中
//以便於當程序關閉之後再次開啟時能從本地讀取圖片
FileOutputStream fos = new FileOutputStream(file+"/"+urlName);
//將bitmap對象壓縮存入對應路徑的內存卡中
bitmap.compress(Bitmap.CompressFormat.PNG,100,fos);
Log.i(TAG, "saveBitmap: 往內存卡中存儲數據!!!!");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//從緩存中獲取bitmap對象的方法
public Bitmap getBitmap(String url){
//首先是通過對應網址為鍵從運行時內存中找
Bitmap result = lrucache.get(url);
//若有會返回
if(result ==null){
//若為空再從軟引用中找
SoftReference soft = map.get(url);
if(soft !=null){
//若存在則result獲取對應bitmap對象
result = soft.get();
Log.i(TAG, "getBitmap:SoftReference中取出數據: ");
}else {
//若沒有 再從本地內存卡中讀取
result = BitmapFactory.decodeFile(path+"/"+url.replace("/","_"));
if(result!=null) {
Log.i(TAG, "getBitmap:從內存卡中取出數據");
}
//若都沒有證明圖片從未下載過
}
}else{
Log.i(TAG, "getBitmap:lrucache中取出數據");
}
//將得到的bitmap對象返回
return result;
}
}
8.xml布局文件MyMainActivity和MyBaseAdapter布局文件
9.xml清單配置文件,添加網絡請求訪問權限和本地SD卡讀寫權限。
10.實現效果如圖面和log打印顯示,讀取和存儲的順序可以見緩存效果。







android listview適配器裡區分不同按鈕的點擊事件
一、適配器 ListItemClickAdapterpublic class ListItemClickAdapter extends BaseAdapter { pr
Android中HttpURLConnection使用詳解
認識Http協議 Android中發送http網絡請求是很常見的,要有GET請求和POST請求。一個完整的http請求需要經歷兩個過程:客戶端發送請求到服務器,然後服務
Android學習——在Android中使用OpenCV的第一個程序
剛開始學習Android,由於之前比較熟悉OpenCV,於是就想先在Android上運行OpenCV試試 ================================
使用Xcode的Targets進行多版本構建
通常,我們在開發過程中,總是需要兩套以上的環境進行測試、生產發布。如果只是簡簡單單的進行一個API的切換,那麼只是進行不同的宏定義即可,但是要求應用的不同版本將使用相同的