編輯:關於Android編程
訪問網絡圖片是很普遍的事了,在前面的學習中,我也寫過了幾次異步網上請求網絡圖片,但是沒有緩存圖片,那麼我們也都知道,有時候訪問一些經常訪問的網絡圖片,如果不采取緩存的形式,那麼對流量的消耗會非常大,所以,有必要的時候我們可以采取緩存圖片的方式來解決流量消耗問題,下面就通過一個MVP模式的簡單設計來這裡寫代碼片講解一下緩存網絡圖片。
整體的結構如下:
首先對於緩存圖片來說,我們需要掌握一下幾個小點:
1. 當圖片沒有緩存的時候,我們就要去通過網絡異步加載圖片
2. 當存在緩存的時候,我們就不能去通過網絡異步加載圖片,直接獲取緩存中的圖片
3. 當保存緩存圖片時如何確定唯一命名規則,由於網絡下載圖片地址是不同的,所以可根據地址命名
4. 考慮到有些模擬器保存到內存卡的緩存中時,出現在cache中找到不緩存的圖片時,我們將圖片地址
中的非單詞字符(除了a-z A-Z 0 - 9) 以外,全部去掉
5. 考慮緩存的內存是否夠用,當緩存不夠用時我們就要刪除部分(這裡我的代碼是刪除全部)已緩存的圖片
6. 考慮在GridView中復用控件時,出現圖片的閃爍已經圖片錯亂問題
model層:
ModelInter.java
//所有的獲取數據功能都抽取到接口中,讓其子類實現
public interface ModelInter {
void getData(OnCompleteListener listener, String path);
public interface OnCompleteListener {
void onComplete(byte[] bytes, String path);
}
}
ModelImp.java
//實現獲取數據
public class ModelImp implements ModelInter {
@Override
public void getData(OnCompleteListener listener, String path) {
new MyAsyncTask(listener).execute(path);
}
}
view層:
ViewInter.java
//所有更新界面,以及得到數據的功能
public interface ViewInter {
void getAdapterData(List data);
}
MainActivity.java
//主界面啟動,展示數據等。
public class MainActivity extends AppCompatActivity implements ViewInter {
private String path = "http://apis.baidu.com/txapi/mvtp/meinv?num=10";
private GridView mGrid;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
new Presenter(this).load(path);
}
private void init() {
mGrid = (GridView) findViewById(R.id.mGrid);
}
private void showListData(List data) {
if (adapter == null) {
adapter = new MyAdapter(data, R.layout.list_item);
mGrid.setAdapter(adapter);
} else {
}
}
//通過presenter代理類為我們提供數據
@Override
public void getAdapterData(List data) {
showListData(data);
}
}
presenter層:
presenter.java
//為視圖層提供所需要的數據
public class Presenter {
private ViewInter viewInter;
private ModelInter modelInter;
public Presenter(ViewInter viewInter){
this.viewInter = viewInter;
modelInter = new ModelImp();
}
//當view需要數據時,調用該方法就可以獲取數據了
public void load(final String path){
//傳遞了匿名內部類,實現獲取數據的方法
modelInter.getData(new ModelInter.OnCompleteListener() {
@Override
public void onComplete(byte[] bytes, String path) {
//通過gson解析數據
Bean bean = new Gson().fromJson(new String(bytes), Bean.class);
if(bean != null)
viewInter.getAdapterData(bean.getNewslist());
}
},path);
}
}
adapter:
MyAdapter.java
//自定義適配器類,不詳細解說,需要了解的查看我相關知識點即可
public class MyAdapter extends MyBaseAdapter {
public MyAdapter(List data, int layout) {
super(data,layout);
}
@Override
public void setData(ViewHolder holder, Bean.NewslistBean t) {
//初始化上下文
if(MyUtils.context == null)
MyUtils.context = holder.getContext();
//如果不存在緩存,就加載網絡圖片
if(!MyUtils.isCacheImage(t.getPicUrl())){
if(MyUtils.isEnoughForCache())//判斷緩存空間是否足夠
//加載請求網絡圖片
holder.setImageURL(R.id.mImg,t.getPicUrl());
else{
Toast.makeText(MyUtils.context,"空間不足了,緩存不了圖片了,正在請求刪除圖片...",Toast.LENGTH_SHORT).show();
//刪除已經緩存的圖片
MyUtils.deletePic(MyUtils.context.getExternalCacheDir());
}
}else {
//這裡可以通過log日志判斷數據是怎樣獲取的
Log.i("IT_Real", "setData: 直接拿數據了,沒用通過緩存拿數據");
//得到緩存路徑下的文件
String fileName = t.getPicUrl().replaceAll("\\W","");
fileName = fileName + t.getPicUrl().substring(t.getPicUrl().lastIndexOf("."),t.getPicUrl().length());
//通過文件位置獲取一個Bitmap
Bitmap bm = BitmapFactory.decodeFile(holder.getContext().getExternalCacheDir() + "/" + fileName);
//設置圖片
holder.setImageBitmap(R.id.mImg,bm);
}
}
}
MyBaseAdapter.java
public abstract class MyBaseAdapter extends BaseAdapter {
protected List data;
protected int layout;
public MyBaseAdapter(List data,int layout){
this.data = data;
this.layout = layout;
}
@Override
public int getCount() {
return data == null ? 0 : data.size();
}
@Override
public Object getItem(int position) {
return data.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = ViewHolder.getHolder(convertView,parent,position, R.layout.list_item);
setData(holder,data.get(position));
return holder.getConvertView();
}
public abstract void setData(ViewHolder holder,T t);
}
ViewHolder.java
public class ViewHolder {
private int position;
private SparseArray array;
private View convertView;
private Context context;
private ViewHolder(ViewGroup parent, int position, int layout) {
this.position = position;
this.context = parent.getContext();
convertView = LayoutInflater.from(parent.getContext()).inflate(layout, null);
convertView.setTag(this);
array = new SparseArray<>();
}
public static ViewHolder getHolder(View convertView, ViewGroup parent, int position, int layout) {
if (convertView == null) {
return new ViewHolder(parent, position, layout);
} else {
ViewHolder holder = (ViewHolder) convertView.getTag();
holder.position = position;
return holder;
}
}
public T getView(int viewId) {
View view = array.get(viewId);
if (view == null) {
view = convertView.findViewById(viewId);
array.put(viewId, view);
}
return (T) view;
}
public Context getContext() {
return context;
}
public View getConvertView() {
return convertView;
}
public ViewHolder setText(int viewId, String data) {
TextView tv = getView(viewId);
tv.setText(data);
return this;
}
public ViewHolder setImageResource(int viewId, int resId) {
ImageView img = getView(viewId);
img.setImageResource(resId);
return this;
}
public ViewHolder setImageURL(int viewId, String url){
final ImageView image = getView(viewId);
//防止圖片閃爍,每次加載GridView時,設置為空白界面
image.setImageBitmap(null);
new MyAsyncTask(new ImageCallBack(image,url,this.context)).execute(url);
Log.i("IT_Real", "setImageURL: 網絡請求了");
return this;
}
public ViewHolder setImageBitmap(int viewId, Bitmap bm) {
ImageView img = getView(viewId);
img.setImageBitmap(bm);
return this;
}
}
utils:
MyAsyncTask.java
//異步網絡請求數據
public class MyAsyncTask extends AsyncTask {
private String path;
private ModelInter.OnCompleteListener listener;
public MyAsyncTask(ModelInter.OnCompleteListener listener){
this.listener = listener;
}
/**
* 異步請求網絡數據
*/
@Override
protected byte[] doInBackground(String... params) {
path = params[0];
return getBytes(path);
}
/**
* 獲取到數據,然後接口回調傳遞數據
*/
@Override
protected void onPostExecute(byte[] bytes) {
super.onPostExecute(bytes);
if(listener != null) {
listener.onComplete(bytes,path);
}
}
/**
* 網絡請求的數據
*/
private byte[] getBytes(String path){
try {
URL url = new URL(path);
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("apikey","百度APIStore的個人apikey,沒有的可以自己去注冊一個,然後獲取美女圖片json數據");
connection.setConnectTimeout(5000);
connection.setReadTimeout(5000);
connection.connect();
if(connection.getResponseCode() == HttpURLConnection.HTTP_OK){
BufferedInputStream bis = new BufferedInputStream(connection.getInputStream());
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int len = -1;
byte[] bytes = new byte[1024 * 8];
while(-1 != (len = bis.read(bytes))){
baos.write(bytes,0,len);
}
byte[] result = baos.toByteArray();
bis.close();
baos.close();
return result;
}
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
MyUtils.java
//所有自己需要實現的功能工具類
public class MyUtils {
public static Context context;
private static StatFs statFs;
/**
* 判斷是否存在緩存圖片
* @param path 圖片路徑
* @return
*/
public static boolean isCacheImage(String path){
String fileName = path.replaceAll("\\W","");
fileName = fileName + path.substring(path.lastIndexOf("."),path.length());
File file = new File(context.getExternalCacheDir(),fileName);
Log.i("IT_Real", "isCacheImage: " + "file is exists" + file.exists() + " ->" + isMount());
return isMount() && file.exists();
}
/**
* 判斷內存卡是否具有讀寫權限
* @return true有,false沒有
*/
public static boolean isMount(){
return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
}
/**
* 判斷緩存空間是否足夠
* @return
*/
public static boolean isEnoughForCache(){
if(statFs == null)
statFs = new StatFs(Environment.getExternalStorageDirectory().getPath());
int availableBlocks = statFs.getAvailableBlocks();
int blockSize = statFs.getBlockSize();
//如果可用空間小於10兆,就提示用戶內存不足
return availableBlocks * blockSize>= 10 * 1024 * 1024;
}
/**
* 遞歸刪除緩存的文件
* @param file 文件路徑
*/
public static void deletePic(File file){
if(file == null) return;
if(!file.isFile()){//如果不是文件就遍歷
File[] files = file.listFiles();
if(file != null){
for (File file1 : files) {
deletePic(file1);
}
}
}else {
file.delete();
Log.i("IT_Real", "deletePic: 刪除了..");
}
}
}
callback:
ImageCallBack.java
//圖片回調接口實現類
public class ImageCallBack implements ModelInter.OnCompleteListener {
private ImageView image;
private String path;
private Context context;
public ImageCallBack(ImageView image, String path, Context context) {
this.image = image;
this.path = path;
this.context = context;
}
/**
* 得到網絡請求圖片後的圖片數據
* @param bytes
* @param path
*/
@Override
public void onComplete(byte[] bytes, String path) {
//判斷每次加載的網絡數據是否和當前圖片地址的加載數據,不相同則不設置此圖片,防止圖片數據錯亂
if (bytes != null && this.path.equals(path)) {
Bitmap bm = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
image.setImageBitmap(bm);
//開始保存圖片
saveCache(context.getExternalCacheDir(), bm, path);
}
}
/**
* 保存緩存的圖片
* @param cacheDir 緩存的路徑
* @param bm
* @param path 文件的名字
*/
private void saveCache(File cacheDir, Bitmap bm, String path) {
//去掉所有非單詞的字符
String fileName = path.replaceAll("\\W","");
fileName = fileName + path.substring(path.lastIndexOf("."),path.length());
try {
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(new File(cacheDir, fileName)));
bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);
bos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
整體的功能已經實現了,這個小案例中,我使用了MVP模式、萬能通用適配器,關於MVP模式和萬能適配器,這裡都不做介紹了,不了解的可用查看我博客中的相關知識點,所有涉及到的主要問題都已經注釋了,如果有不懂的可用留言下方,我會在第一時間回復。
由於一個項目的需要,我研究了一下android的網絡通信方式,大體和java平台的很相似! android平台也提供了很多的API供開發者使用,請按示例圖:
在Android中,JSBridge已經不是什麼新鮮的事物了,各家的實現方式也略有差異。大多數人都知道WebView存在一個漏洞,見WebView中接口隱患與手機掛馬利用
之前自己的編程完全是在PC上進行的,而且主要是在算法和數據結構上。由於某些需要加之認識到Android的重要性,且大學走到現在基本上沒什麼課了,空閒時間很多,於是就開始學
MSM8909+Android5.1.1鍵盤驅動------概述 采用SN7326帶智能指掃描的鍵盤擴展芯片,通過I2C接口來讀取其狀態寄存器的值就可知道是單按