編輯:關於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 List2.AsyncTaskOfJson異步任務解析JSON數據類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(); } }
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(Listlist); }
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 Listlist = 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打印顯示,讀取和存儲的順序可以見緩存效果。
一、適配器 ListItemClickAdapterpublic class ListItemClickAdapter extends BaseAdapter { pr
認識Http協議 Android中發送http網絡請求是很常見的,要有GET請求和POST請求。一個完整的http請求需要經歷兩個過程:客戶端發送請求到服務器,然後服務
剛開始學習Android,由於之前比較熟悉OpenCV,於是就想先在Android上運行OpenCV試試 ================================
通常,我們在開發過程中,總是需要兩套以上的環境進行測試、生產發布。如果只是簡簡單單的進行一個API的切換,那麼只是進行不同的宏定義即可,但是要求應用的不同版本將使用相同的