編輯:關於Android編程
上一次我在學習過程中,寫了一篇關於緩存網絡圖片的學習筆記,在那一篇博客中使用的是AsyncTask異步任務請求的方式緩存的,這一次我從學習中,學會了一種新的緩存方法,就是通過LruCache去緩存數據,LruCache是一種內存緩存機制,采用了最近最少LRU算法,這樣的效率比直接去判斷從本地出數據效率相對更高一點。下面開始貼代碼,具體的功能在代碼中注釋。
整體框架:
涉及的類有點多,但是,為了養成使用MVP模式的習慣,也只能拼了。這裡只寫幾個具體有實際操作的類,因為代碼會上傳,大家可以下載源碼查看。<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxoMyBpZD0="model層">model層:
MainActivityModelImp.java
public class MainActivityModelImp implements MainActivityModelInter { /** * 處理具體的數據, * @param path json地址 * @param listener presenter中回調接口 */ @Override public void getData(String path,OnCompleteNetDataListener listener) { //這裡發送一個json數據請求 MyNetUtils.getNetData(path,listener, GloableInterface.JSON); } }
MainActivity.java
public class MainActivity extends BaseActivityimplements MainActivityInter > { //請求圖片的json地址,這裡是百度為我們提供的一個美女圖片接口 private String path = "http://apis.baidu.com/txapi/mvtp/meinv?num=10"; //自定義的adpter private MyListAdapter adapter; //顯示的數據集 private List
data; //ListView控件 private ListView mList; //ListView布局的item布局id @LayoutRes private int layoutId; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); init(); event(); //請求數據 basePresenter.load(path); } private void event() { } /** * 初始化控件或對象 */ private void init() { layoutId = R.layout.list_item; data = new ArrayList<>(); mList = (ListView)findViewById(R.id.mList); //文件管理的布局 MyFileUtils.context = MainActivity.this; } /** * 向adapter提供數據,設置ListView的數據 * @param data */ private void showListData(List data){ if(adapter == null){ adapter = new MyListAdapter(data,layoutId); mList.setAdapter(adapter); }else{ data.addAll(data); adapter.notifyDataSetChanged(); } } /** * 關聯MainActivityPresenter * @return */ @Override public MainActivityPresenter getPresenter() { return new MainActivityPresenter(); } /** * presenter返回的數據 * @param newslistBeen 數據集 */ @Override public void showData(List newslistBeen) { data = newslistBeen; showListData(data); } }
MainActivityPresenter.java
public class MainActivityPresenter extends BasePresenter{ @Override public MainActivityModelImp createModel() { return new MainActivityModelImp(); } /** * 處理view的請求,獲取model的數據返回給view * @param path */ public void load(String path) { model.getData(path, new MainActivityModelInter.OnCompleteNetDataListener() { @Override public void onCompleteNetData(byte[] bytes, String path) { if (bytes != null) { PicBean picBean = new Gson().fromJson(new String(bytes), PicBean.class); getView().showData(picBean.getNewslist()); }else{ Log.i("IT_Real", "onCompleteNetData: 數據沒有訪問到"); } } }); } }
OnLoadImage.java
public class OnLoadImage implements MainActivityModelInter.OnCompleteNetDataListener{ private ImageView imageView; public OnLoadImage(ImageView imageView){ this.imageView = imageView; } @Override public void onCompleteNetData(byte[] bytes, String path) { //避免圖片對應位置請求錯誤。出現一閃一閃的圖片或者,連續不間斷顯示圖片的問題 if(bytes != null && imageView.getTag().equals(path)){ Bitmap bm = BitmapFactory.decodeByteArray(bytes,0,bytes.length); //TODO 緩存圖片、 //添加到內存緩存中 MyLruCache.getInstance().put(path.replaceAll("\\W",""),bm); //緩存到本地 MyFileUtils.saveCache(imageView.getContext().getExternalCacheDir(),bm,path); //設置圖片數據 imageView.setImageBitmap(bm); } } }
MyListAdapter.java
public class MyListAdapter extends MyBaseAdapter{ public MyListAdapter(List data, @LayoutRes int layoutId) { super(data, layoutId); } /** * 具體的設置item數據操作 * @param holder ViewHolder持有對象 * @param picBean 數據集 */ @Override public void setData(ViewHolder holder, PicBean.NewslistBean picBean) { //獲取圖片對應的標題 String title = picBean.getTitle(); //獲取圖片的地址 String path = picBean.getPicUrl(); //去掉非法的字符,然後作為文件名保存到本地 String fileName = path.replaceAll("\\W",""); //設置標題 holder.setText(R.id.mTvTitle, title); //通過內存緩存機制去獲取Bitmap數據 Bitmap bm = MyLruCache.getInstance().get(fileName); //自定義一個標記,看看數據是從內存緩存中拿到的還是從本地拿到的 String tag = "從內存緩存中拿了數據"; //如果內存緩存中沒有數據 if (bm == null) { //先看看本地內存中有沒有數據,如果有就直接從本地拿數據 if(MyFileUtils.isCachedToLocal(path)){ tag = "從本地拿了數據"; fileName = fileName + path.substring(path.lastIndexOf("."),path.length()); bm = BitmapFactory.decodeFile(MyFileUtils.context.getExternalCacheDir() + "/" + fileName); }else{//否則就去網絡請求數據 if(MyFileUtils.isEnoughSpace())//判斷內存空間是否足夠,足夠就去請求數據,然後緩存 holder.setImageURL(R.id.mImage, path); else{//空間不足則刪除一些已經緩存的圖片 Toast.makeText(MyFileUtils.context,"空間不足了,緩存不了圖片了,正在請求刪除圖片...",Toast.LENGTH_SHORT).show(); MyFileUtils.deletePic(holder.getConverView().getContext().getExternalCacheDir()); } } } //不為空,就直接去設置圖片 if(bm != null){ Toast.makeText(MyFileUtils.context, tag, Toast.LENGTH_SHORT).show(); holder.setImageBitmap(R.id.mImage,bm); } } }
ViewHolder.java
public class ViewHolder { private View converView; private int position; private Context context; private SparseArrayviews = new SparseArray<>(); public ViewHolder(Context context, int position, @LayoutRes int layoutId) { if (context == null) this.context = context; this.position = position; converView = LayoutInflater.from(context).inflate(layoutId, null); converView.setTag(this); } public T getView(int viewId) { View view = views.get(viewId); if (view == null) { view = converView.findViewById(viewId); views.put(viewId, view); } return (T) view; } public ViewHolder setText(int tvId, String data) { TextView textView = getView(tvId); textView.setText(data); return this; } /** * 通過圖片地址請求圖片數據 * @param viewId 設置圖片控件的id * @param path 請求圖片的地址 * @return 返回ViewHolder,為了鏈式操作 */ public ViewHolder setImageURL(int viewId, String path) { final ImageView imageView = getView(viewId); imageView.setImageBitmap(null); //每個id控件對應一張圖片的地址,只有一一對應才會請求網絡,避免了不必要的網絡請求 imageView.setTag(path); //請求網絡數據 MyNetUtils.getNetData(path, new OnLoadImage(imageView), GloableInterface.IMAGEURL); return this; } /** * 通過Bitmap設置圖片 * @param viewId 圖片控件id * @param bm 圖片數據 * @return */ public ViewHolder setImageBitmap(int viewId, Bitmap bm) { ImageView imageView = getView(viewId); imageView.setImageBitmap(bm); return this; } public static ViewHolder getViewHolder(View convertView, Context context, int position, @LayoutRes int layoutId) { ViewHolder holder = null; if (convertView == null) { holder = new ViewHolder(context, position, layoutId); } else { holder = (ViewHolder) convertView.getTag(); holder.position = position; } return holder; } public View getConverView() { return converView; } }
MyFileUtils.java
public class MyFileUtils { //上下文布局 public static Context context; //獲取指定位置存儲狀態的類對象 private static StatFs statFs; //私有化該對象,外部不能創建該實例,大量的操作只需要通過靜態方法獲取即可 private static MyFileUtils myFileUtils = new MyFileUtils(); private MyFileUtils(){ } /** * 為外部提供一個獲取實例的方法 * @return */ public static MyFileUtils getInstance(){ return myFileUtils; } /** * 判斷本地內存中是否有當前文件名的緩存路徑 * @param path 圖片的地址,將圖片地址去掉非法字符作為文件名 * @return */ public static boolean isCachedToLocal(String path){ String fileName = path.replaceAll("\\W",""); fileName = fileName + path.substring(path.lastIndexOf("."),path.length()); File file = new File(context.getExternalCacheDir(),fileName); return isMount() && file.exists(); } /** * 是否有足夠的空間 * @return */ public static boolean isEnoughSpace(){ if(statFs == null){ statFs = new StatFs(Environment.getExternalStorageDirectory().getPath()); } int availableBlocks = statFs.getAvailableBlocks(); int blockSize = statFs.getBlockSize(); return availableBlocks * blockSize >= 10 * 1024 * 1024; } /** * 判斷是否判斷內存卡是否具有讀寫權限,是不是掛載狀態 * @return */ public static boolean isMount(){ return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED); } /** * 遞歸刪除緩存的文件 * @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(); } } /** * 保存緩存的圖片 * @param cacheDir 緩存的路徑 * @param bm * @param path 文件的名字 */ public static 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(); } } }
MyLruCache.java
/** * 內存緩存類,這個類使用了最近最少算法來操作緩存 */ public class MyLruCache extends LruCache{ private static MyLruCache myLruCache = new MyLruCache((int)Runtime.getRuntime().maxMemory()/1024/8); public static MyLruCache getInstance(){ return myLruCache; } /** * 緩存的大小,默認給他分配最大空間的1/8即可 * @param maxSize */ private MyLruCache(int maxSize) { super(maxSize); } /** * 返回每個緩存對象 * @param key 存放的key * @param value 對應的圖片數據 * @return */ @Override protected int sizeOf(String key, Bitmap value) { return value.getRowBytes() * value.getHeight() / 1024;//返回大小 KB單位 } }
MyNetUtils.java
/** * 網絡請求工具 */ public class MyNetUtils { //創建okHttpClient對象 private static OkHttpClient client = new OkHttpClient(); //創建一個運行在主線程的handler private static Handler handler = new Handler(Looper.getMainLooper()); private MyNetUtils(){ } /** * 獲取網絡數據 * @param path * @param listener * @param flag */ public static void getNetData(final String path, final MainActivityModelInter.OnCompleteNetDataListener listener, String flag){ //創建一個builder實例 Request.Builder builder = new Request.Builder(); Request request = null; if("json".equals(flag)){ //設置對應的請求地址,header request = builder.url(path).get().addHeader("apikey","自己申請的百度apiStore中的APIKEY").build(); }else if("imageURL".equals(flag)){ request = builder.url(path).get().build(); } //開始請求數據 client.newCall(request).enqueue(new Callback() { /** * 當服務器中斷,或者出現不可避免的故障,不能正常訪問服務器,執行該方法 * * @param call * @param e */ @Override public void onFailure(Call call, IOException e) { } /** * 請求成功,或者404等錯誤都會執行這個方法 * @param call * @param response * @throws IOException */ @Override public void onResponse(Call call, Response response) throws IOException { //獲取到請求的字節數組,這個請求是在異步任務中請求的 final byte[] bytes = response.body().bytes(); //通過handler將數據回調到主線程 handler.post(new Runnable() { @Override public void run() { if(listener != null){ listener.onCompleteNetData(bytes,path); } } }); } }); } }
以上就是一些具體的實現。。。上面涉及的知識點不是很多,這裡只是一些具體的實現,還又很多類沒有貼上,因為大部分都是一些重復的類,大家想看的下載源碼去看吧。
今日頭條包含以下模塊:首頁 視頻 天氣 和 我的 其中 首頁用於加載實時的新聞頻道及內容,可以實現點擊圖片查看圖片詳情,並且可以實現內容的收藏與取消收藏視頻模塊暫時未加入
一個讓人賞心悅目的界面對軟件來說非常重要,因此圖形圖像資源也顯得非常重要。本講就要談一談Android中處理圖形圖像的最重要的一個類Drawable。Drawable就是
廢話就不多說了,開始今天的正題,帶你實現開發者頭條APP的啟動頁。一.老規矩,先上效果圖從效果圖中我們可以看出,整個滑動的界面就是一個ViewPager實現,然後監聽Vi
Android 4.4發布了一個ART運行時,准備用來替換掉之前一直使用的Dalvik虛擬機,希望籍此解決飽受诟病的性能問題。老羅不打算分析ART的實現原理,只是很有興趣