好久没更新博客,最近在完成实习任务,自己研究出了一套头像加载机制,虽然谈不上高大上,但是使用起来确实比较方便,可以用在任何Android应用中需要头像的地方。
先说一下这个头像加载的思路:
首先加载本地
如果本地加载成功
检查本地头像是不是最新的,如果是最新的,不操作,如果不是最新的,加载网络最新
如果本地加载失败
加载网络最新头像
当然实现这个逻辑比较方便,但是考虑到代码的耦合度以及面向对象的单一原则,我单独写了加载头像的类。
在贴上加载头像源码前,需要说明一下上传头像的设计:
上传头像给服务器,首先会给头像命名,命名规则采用随机码,这样两次任意的上传头像的名称都不一样,后台数据库存取头像则存取头像图片的绝对路径,比如每次上传成功之后都存在服务器的linux目录的/opt/headImage/xxxx.jpg。xxxx为生成的随机名称。
上传图片的过程中,会在本地也缓存一份,命名为head.jpg。
还会在本地缓存一份最新生成的随机码,也就是头像的名称。
这样,首先加载本地头像,会先检查缓存目录下有没有head.jpg。如果有,先在ImageView上显示出来,显示出来不能确定是不是最新的,所以这时候还要请求服务器后台,服务器头像的最新名称,检查与本地缓存的名称是否一致,如果不一致,还要加载服务器的头像。
大概逻辑就是这样。
现在贴上源码
package com.example.ev.manage;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.util.Log;
import android.widget.ImageView;
import com.example.ev.R;
import com.example.ev.utils.HttpUtil;
import org.json.JSONObject;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
/**
* 头像加载框架 v1.1
* 用于加载头像
* 回调接口可以返回最新的图像bitmap
* @author 钱海峰
*/
public class HeadImgManager {
private static final String NETWORK_ERROR = "NETWORK_ERROR";
private String TAG = "HeadImgManager";
private Activity mActivity;
private SharedPreferences mSpData;
private ImageView mIvPhoto;
private String path;
private String mUserAccount;
private String imgFileName;// = mUserAccount + "_head.jpg";
private HeadImgManager(Activity ac,ImageView imageView,String mUserAccount) {
mActivity = ac;
mSpData = mActivity.getSharedPreferences("data", Context.MODE_PRIVATE);
mIvPhoto = imageView;
this.path = getDiskCacheDir(mActivity) + "EChargerHead/";//路径写死
this.mUserAccount = mUserAccount;
imgFileName = mUserAccount + "_head.jpg";
}
private static HeadImgManager instance;//单例模式
public static HeadImgManager getInstance(Activity ac,ImageView imageView,String mUserAccount) {
if(instance == null) {
instance = new HeadImgManager(ac,imageView,mUserAccount);
return instance;
} else {
instance.mActivity = ac;
instance.mIvPhoto = imageView;
instance.mUserAccount = mUserAccount;
instance.imgFileName = mUserAccount + "_head.jpg";
return instance;
}
}
private String getDiskCacheDir(Context context) {
boolean externalStorageAvailable = Environment
.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
final String cachePath;
if (externalStorageAvailable) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return cachePath + File.separator;
}
public void loadHeadImg() {
loadHeadImg(null);
}
/**
* 全部运行在子线程
* 逻辑
* 首先加载本地
* 如果本地加载成功
* 检查本地头像是不是最新的,如果是最新的,不操作,如果不是最新的,加载网络最新
* 如果本地加载失败
* 加载网络最
*/
public void loadHeadImg(final HeadImgBitapCallBack callBack) {
//首先加载本地的
ThreadManager.THREAD_POOL_EXECUTOR.execute(new Runnable() {
@Override
public void run() {
try {
if(loadLocalHeadImg(callBack)) {//本地加载成功,检查头像更新
/本地加载成功
String imgName = getImgName();
Log.e(TAG,imgName + "----");
if(imgName == null) {
// imgName == null 网络获取头像失败
//本地有缓存,但是网络获取为空,表明切换的账号没有头像
Log.e(TAG,"头像为空");
File headFile = new File(path + imgFileName);
headFile.delete();
Log.e(TAG,"删除本地缓存");
Resources res= mActivity.getResources();
final Bitmap bmp =BitmapFactory.decodeResource(res, R.drawable.found_add_icon);
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if(callBack != null) {
callBack.onGetBitmap(bmp);
}
if(mIvPhoto != null) {
mIvPhoto.setImageBitmap(bmp);
}
}
});
} else if(imgName.equals(NETWORK_ERROR)) {//网络错误
Log.e(TAG,"网络错误,就加载本地的");
} else { //获取到网络图片名称
if (!mSpData.getString("headImgName", "").equals(imgName)) {//本地缓存过老
Log.e(TAG, "本地缓存过老");
loadImgByUrl(imgName,callBack);
} else {//本地是最新的
}
}
/本地加载成功
} else {//本地加载失败,加载网络
Log.e(TAG, "本地没有图片,加载网络图片");
String imgName = getImgName();
if(imgName != null && !imgName.equals(NETWORK_ERROR)) {
loadImgByUrl(imgName,callBack);
} else {
//本地加载失败,网络加载也失败
Log.e(TAG,"本地加载失败,网络加载也失败");
if(callBack != null) {
Resources res= mActivity.getResources();
final Bitmap bmp =BitmapFactory.decodeResource(res, R.drawable.found_add_icon);
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
callBack.onGetBitmap(bmp);
}
});
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
/**
* 加载本地图片 运行在子线程
* @return
*/
private boolean loadLocalHeadImg(final HeadImgBitapCallBack callBack) {
FileInputStream fis = null;
try {
fis = new FileInputStream(path + imgFileName);
final Bitmap localHead = BitmapFactory.decodeStream(fis);
if (localHead != null) {
Log.e(TAG, "localHead: " + localHead);
Log.e(TAG, "本地有头像文件,先加载本地图片");
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if(callBack != null) {
callBack.onGetBitmap(localHead);
}
if(mIvPhoto != null) {
mIvPhoto.setImageBitmap(localHead);
}
}
});
return true;
} else {
Log.e(TAG, "本地没有图片,加载网络图片");
return false;
}
} catch (FileNotFoundException e) {
Log.e(TAG, "本地没有图片");
e.printStackTrace();
return false;
}
}
/**
* 通过Image名称加载图片 运行在子线程
* @param imageName 图片名称
* @throws IOException
*/
private void loadImgByUrl(String imageName, final HeadImgBitapCallBack callBack) throws IOException {
if(imageName == null) {
return;
}
String imgUrl = HttpUtil.URL + "upload/image/" + imageName;
Log.e(TAG, "imgUrl: " + imgUrl);
URL url = new URL(imgUrl); //path图片的网络地址
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) {
final Bitmap bitmap = BitmapFactory.decodeStream(httpURLConnection.getInputStream());
mActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
if(callBack != null) {
callBack.onGetBitmap(bitmap);
}
if(mIvPhoto != null) {
mIvPhoto.setImageBitmap(bitmap);
}
}
});
if(savePicToSd(bitmap)) {
Log.e(TAG,"缓存到本地成功");
SharedPreferences.Editor edit = mSpData.edit();
edit.putString("headImgName", imageName);
edit.apply();
} else {
Log.e(TAG,"缓存到本地失败");
}
//缓存文件名称一份到本地
Log.e(TAG, "加载网络图片完成");
}
}
//获得个人的头像信息
private JSONObject getHeadImgName(String phoneNumber) throws Exception {
//使用Map封装请求参数 gender
Map<String, String> map = new HashMap<>();
map.put("phoneNumber", phoneNumber);
String url = HttpUtil.URL + "getIamge";
Log.e(TAG, url);
return new JSONObject(HttpUtil.postRequest(url, map));
}
/**
* 获得 最新头像的名称 没有返回null
* @return 最新头像的名称
*/
private String getImgName() {
try {
JSONObject jsonImageName = getHeadImgName(mUserAccount);
Log.e(TAG, jsonImageName.toString());
if (jsonImageName.getString("errcode").equals(HttpUtil.SUCCESS)) {
String imageName = jsonImageName.getString("content");
return imageName;
/**/
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return NETWORK_ERROR;
}
}
private boolean savePicToSd(Bitmap mBitmap) {
String sdStatus = Environment.getExternalStorageState();
if (!sdStatus.equals(Environment.MEDIA_MOUNTED)) { // 检测sd是否可用
Log.e(TAG, "保存失败");
return false;
}
FileOutputStream b = null;
File file = new File(path);
file.mkdirs();// 创建文
String fileName = path + imgFileName;//图片名字
Log.e(TAG, "imgFileName: " + fileName);
try {
b = new FileOutputStream(fileName);
mBitmap.compress(Bitmap.CompressFormat.JPEG, 100, b);// 把数据写入文件
Log.e(TAG, "保存成功");
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
Log.e(TAG, "保存失败");
return false;
} finally {
try {
//关闭流
b.flush();
b.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 回调接口 用于返回bitmap对象
* @author 钱海峰
*/
public interface HeadImgBitapCallBack {
void onGetBitmap(Bitmap img);
}
}
现在对代码作一些解读,我在代码中加入了一个回调接口,HeadImgBitapCallBack,里面只有一个方法onGetBitmap,目的很明确就是用于获取最新的头像bitmap对象的,用回调的方式将结果返回,如果需要返回bitmap对象,可以调用方法loadHeadImg(HeadImgBitapCallBack callBack),如果不需要返回bitmap对象,可以调用方法loadHeadImg();
使用这个头像加载框架也是很方便;
如下为不需要返回bitmap对象的,this为上下文,mIvPhoto对ImageView,mUserAccount为当前账户手机号,用于请求后台服务器的。
HeadImgManager imgManager = HeadImgManager.getInstance(this, mIvPhoto, mUserAccount);
imgManager.loadHeadImg();
如果需要返回bitmap对象
HeadImgManager imgManager = HeadImgManager.getInstance(this, null, mUserAccount);
imgManager.loadHeadImg(new HeadImgManager.HeadImgBitapCallBack() {
@Override
public void onGetBitmap(Bitmap img) {
myHeadBitmap = img;
}
});
比如我在意见反馈界面就用到这个头像bitmap对象,大概效果如下:
将最新的头像显示在ListView中。
也可以是这样