編輯:關於Android編程
從Android爆發以後,自定義的控件如EditTextWithDelete、ActionBar、PullToRresh逐步進入開發者的視野,想起11年的時候,基本項目中使用的UI還全都是Android提供的基礎控件,少一點的動畫+布局,下載和網絡請求都是用HttpClient,圖片加載當然也是下載後再使用,當時的程序資源可沒有現在這麼豐富,看看Github上開源的項目,現在的程序員應該感到幸福。
項目開發從程序上來講,萬古不變兩大事情,一是網絡通信框架,二是圖片加載框架,有關網絡框架上一篇已經介紹了async-http和okhttp,而其他如volly同時擁有網絡請求和圖片加載兩個框架,很多人圖省事就一次性使用了,當然facebook自己的開源框架也是寫的非常不錯,接下來再一一介紹;先貼一張11年我們自己寫的imageloader
public class ImageDownloader {
private static ImageDownloader instance = null;
private static File cacheDir;
public static Map> bitMapCache = new HashMap>();
public static ImageDownloader getInstance() {
if (instance == null) {
instance = new ImageDownloader();
}
return instance;
}
private ImageDownloader() {
// Find the dir to save cached images
if (android.os.Environment.getExternalStorageState().equals(
android.os.Environment.MEDIA_MOUNTED))
cacheDir = new File(
android.os.Environment.getExternalStorageDirectory(),
"WholeMag");
else
cacheDir = WholeMagApplication.getInstance().getCacheDir();
if (!cacheDir.exists())
cacheDir.mkdirs();
}
public void download(String actName, String url, ImageView imageView) {
BitmapDownloaderTask task = new BitmapDownloaderTask(imageView, actName);
task.execute(url);
// return task.doInBackground(url);
}
class BitmapDownloaderTask extends AsyncTask {
// private String url;
// private boolean flag;
private final WeakReference imageViewReference; // 使用WeakReference解決內存問題
private String actName;
public BitmapDownloaderTask(ImageView imageView, String actName) {
imageViewReference = new WeakReference(imageView);
this.actName = actName;
}
@Override
protected Bitmap doInBackground(String... params) { // 實際的下載線程,內部其實是concurrent線程,所以不會阻塞
Bitmap rebmp = getLocalBitmap(params[0], actName);
if (rebmp == null)
rebmp = downloadBitmap(params[0], actName);
if (rebmp == null) {
doInBackground(params[0]);
}
return rebmp;
}
@Override
protected void onPostExecute(Bitmap bitmap) { // 下載完後執行的
if (isCancelled()) {
bitmap = null;
}
if (imageViewReference != null) {
ImageView imageView = imageViewReference.get();
if (imageView != null) {
imageView.setDrawingCacheEnabled(true);
Bitmap temp = imageView.getDrawingCache();
imageView.setDrawingCacheEnabled(false);
if (temp != null) {
temp.recycle();
}
double widthX = (float) WholeMagDatas.getDeviceWidth()
/ bitmap.getWidth(); // 圖片寬度拉伸比例
int bitmapHight = bitmap.getHeight();// 圖片高度
imageView.setImageBitmap(bitmap); // 下載完設置imageview為剛才下載的bitmap對象
if(actName.equals(AppData.NEWS_DETAIL_ACT)){
FrameLayout.LayoutParams ll = new FrameLayout.LayoutParams(
android.view.ViewGroup.LayoutParams.FILL_PARENT,
(int) (bitmapHight * widthX), Gravity.CENTER);
imageView.setLayoutParams(ll);
}
AlphaAnimation alphaAnimation = new AlphaAnimation(0, 1);// 創建一個AlphaAnimation對象
alphaAnimation.setDuration(500);// 設置動畫執行的時間(單位:毫秒)
imageView.startAnimation(alphaAnimation);
}
}
}
}
static Bitmap getLocalBitmap(String url, String actName) {
if (bitMapCache.containsKey(url)) {
return bitMapCache.get(url).get();
}
// String tmp = url;
// String first = url.substring(url.lastIndexOf("/") + 1);
// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
// String second = tmp.substring(tmp.lastIndexOf("/") + 1);
// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
// String third = tmp.substring(tmp.lastIndexOf("/") + 1);
// String filename = third + second + first;
// File f = new File(cacheDir, filename);
File f = Tools.getFile(actName, url);
InputStream inputStream = null;
try {
// decode image size
BitmapFactory.Options o = new BitmapFactory.Options();
o.inPreferredConfig = Bitmap.Config.RGB_565;
o.inDither = false;
o.inPurgeable = true;
// o.inTempStorage = new byte[12 * 1024];
inputStream = new FileInputStream(f);
// Bitmap bitmap = BitmapFactory.decodeFile(f.getAbsolutePath());
Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, o);
bitMapCache.put(url, new SoftReference(bitmap));
return bitmap;
} catch (Exception e) {
} finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (Exception ex) {
}
}
}
return null;
}
static Bitmap downloadBitmap(String url, String actName) {
final AndroidHttpClient client = AndroidHttpClient.newInstance("linux");
final HttpGet getRequest = new HttpGet(url);
try {
HttpResponse response = client.execute(getRequest);
final int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != HttpStatus.SC_OK) {
// Log.e("cwjDebug", "Error " + statusCode
// + " while retrieving bitmap from " + url);
return null;
}
final HttpEntity entity = response.getEntity();
if (entity != null) {
// String tmp = url;
// String first = url.substring(url.lastIndexOf("/") + 1);
// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
// String second = tmp.substring(tmp.lastIndexOf("/") + 1);
// tmp = tmp.substring(0, tmp.lastIndexOf("/"));
// String third = tmp.substring(tmp.lastIndexOf("/") + 1);
// String filename = third + second + first;
// File f = new File(cacheDir, filename);
File f = Tools.getFile(actName, url);
OutputStream os = new FileOutputStream(f);
InputStream inputStream = null;
try {
inputStream = entity.getContent();
BitmapFactory.Options o = new BitmapFactory.Options();
o.inPreferredConfig = Bitmap.Config.RGB_565;
o.inDither = false;
o.inPurgeable = true;
final Bitmap bitmap = BitmapFactory.decodeStream(
inputStream, null, o);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
bitMapCache.put(url, new SoftReference(bitmap));
return bitmap;
} finally {
if (inputStream != null) {
inputStream.close();
}
if (null != os) {
os.close();
}
entity.consumeContent();
}
}
} catch (Exception e) {
getRequest.abort();
} finally {
if (client != null) {
client.close();
}
}
return null;
}
}
當年為了防止圖片爆掉出現OOM,使用了軟引用,覺得還不錯;圖片加載的基本原理就是,把url+imageview拋過來,然後開啟異步線程加載,根據獲取的byte流decode成bitmap,最後在UI線程將圖片加載到imageview上;也沒有說做本地緩存,僅做了應用緩存;沒有對圖片進行壓縮或者設置格式,使占用內存更小,展示也更合理;LruCache基本原理跟上面使用軟引用的過程差不多,只不過多限制了圖片占用內存的大小,計算圖片使用的頻率,對應用層、SD卡層均做了封裝。
上面介紹完,相信你也對圖片加載有個大概的輪廓,我們拿開源的imageloader為例,來講講圖片加載框架的一些細節
public final class ImageLoaderConfiguration {
final Resources resources;//主要給圖片設計寬高時,獲得屏幕寬高使用
final int maxImageWidthForMemoryCache;//內存中最大的圖片寬度
final int maxImageHeightForMemoryCache;//內存中最大的圖片高度
final int maxImageWidthForDiskCache;//SD卡中最大的圖片寬度
final int maxImageHeightForDiskCache;//SD卡中最大的圖片高度
final BitmapProcessor processorForDiskCache;//從SD卡獲得Bitmap的加載器
final Executor taskExecutor;//加載圖片時的執行器
final Executor taskExecutorForCachedImages;//加載緩存時的執行器
final boolean customExecutor;//是否使用默認執行器
final boolean customExecutorForCachedImages;//是否使用默認緩存執行器
final int threadPoolSize;//線程數,可以用來控制展示當前界面的item圖片
final int threadPriority;//線程的執行優先級
final QueueProcessingType tasksProcessingType;//是LILO還是LIFO,默認是前者,但一般喜歡後者
final MemoryCache memoryCache;//內存緩存對象,如不寫可用默認
final DiskCache diskCache;//SD卡緩存對象,如不寫可用默認
final ImageDownloader downloader;//圖片加載器,根據網絡(http/s)、file、content、drawable、asset來加載
final ImageDecoder decoder;//圖片解析器,根據獲取的圖片參數拿到Bitmap
final DisplayImageOptions defaultDisplayImageOptions;//設置圖片加載狀態和結果,見下面源碼
final ImageDownloader networkDeniedDownloader;//不用網絡下載圖片的下載器,可理解為加載SD卡圖片的加載器
final ImageDownloader slowNetworkDownloader;//僅網絡下載圖片的下載器,支持斷點續傳
public final class DisplayImageOptions {
private final int imageResOnLoading;//圖片是否加載中
private final int imageResForEmptyUri;//圖片是否來自於空url
private final int imageResOnFail;//圖片是否加載失敗
private final Drawable imageOnLoading;//加載中的圖片
private final Drawable imageForEmptyUri;//空數據的圖片
private final Drawable imageOnFail;//加載失敗的圖片
private final boolean resetViewBeforeLoading;//加載完是否重置(意味著放棄之前的加載)
private final boolean cacheInMemory;//是否緩存在內存中
private final boolean cacheOnDisk;//是否緩存在SD卡中
private final ImageScaleType imageScaleType;//要多大的圖片,統一設置
private final Options decodingOptions;//Bitmap的options對象
private final int delayBeforeLoading;//是否延遲加載,可用於非當前頁面圖片
private final boolean considerExifParams;//是否支持jpeg圖片的rotate和flip等方法
private final Object extraForDownloader;//額外數據
private final BitmapProcessor preProcessor;//加載不在內存中的圖片
private final BitmapProcessor postProcessor;//加載在圖片中的圖片
private final BitmapDisplayer displayer;//展示圖片
private final Handler handler;//這個就不用講了吧,跟主線程交互必不可少的工具
private final boolean isSyncLoading;//是否同步加載
補充一下,上面框架還支持給圖片設置像素點占位大小;看到這麼多功能,對於現在的項目基本滿足要求,因此就不打算換了,再看看其他幾種圖片加載框架的異同
fresco,facebook出品,最大的優勢在於可展示加載過程,即加載進度、加載前圖片、加載中圖片、加載後圖片、加載失敗圖片等,還可以設置圖片的形狀
picasso,加載更快,因為默認設置圖片格式占內存小
Glide是升級版本的piccaso,支持跟fragment和activity生命周期綁定
volly,基於老的imageloader又做了次封裝,差別不是太大,功能弱化一些
而後面這幾種框架,都是在imageloader興起之後出現的,所以也基本支持它,在我看來也僅有fresco和glide是真正寫出了跟原框架不同的東西。
Android開發筆記之:Splash的實現詳解
什麼是SplashSplash也就是應用程序啟動之前先啟動一個畫面,上面簡單的介紹應用程序的廠商,廠商的LOGO,名稱和版本等信息,多為一張圖片,顯示幾秒鐘後會自動消息,
Android實現帶磁性的懸浮窗體效果
本文實例講述了Android實現帶磁性的懸浮窗體效果。分享給大家供大家參考,具體如下:帶磁性的懸浮窗體,類似於360綠色小人主要實現的是:1.懸浮所有窗體之上2.有吸引力
Android拆輪子系列之寫驗證碼控件的方法
前言先看看效果怎麼樣不錯吧?別急下面我就一步一步的教你實現。用到的知識點總結:1.Canvas和pint的使用,我們用它畫點,線,字2.View的基本用法其實做這個東西還
Android屏幕適配全攻略
Android屏幕適配出現的原因在我們學習如何進行屏幕適配之前,我們需要先了解下為什麼Android需要進行屏幕適配。由於Android系統的開放性,任何用戶、開發者、O
從源碼角度帶你分析 Android View 事件分發 dispatchTouchEvent,onTouch,onTouchEvent,onClick邏輯順序過程(一)
關於Android View 事件分發過程的文章網絡上可以搜到一把大,這