### 01.课程介绍
> 0.开源项目介绍
> 1.Volley四大请求
> 2.LruCache实现图片缓存
> 3.缓存更新策略:什么时候更新(模块间跳转清空缓存),豌豆荚、京东商城
> 4.自定义Volley请求
> 5.二次封装Volley请求:
> 6.Volley绑定Activity生命周期
> MVP框架
### 02.开源项目介绍
> 以后开发:开发速度,UI和业务的问题
> 以后遇到UI的问题,90%github都可以找到解决方案
> 1.GridView拖拽排序:[https://github.com/fishCoder/DragGridView](https://github.com/fishCoder/DragGridView)
> 2.绘制图表:[https://github.com/HackPlan/AndroidCharts](https://github.com/HackPlan/AndroidCharts)
> 3.视差效果:parallax ,[https://github.com/kanytu/android-parallax-recyclerview](https://github.com/kanytu/android-parallax-recyclerview)
> 带粘性头部、并且排序:[https://github.com/jiyouliang/StickyHeaderListView](https://github.com/jiyouliang/StickyHeaderListView)
> zxing生成和扫描,扫描本地图片二维码:[https://github.com/open-android/Zxing](https://github.com/open-android/Zxing)
> 商城类型首页:淘宝[https://github.com/alibaba/vlayout](https://github.com/alibaba/vlayout)
### 03.Volley简单介绍
> 2013年IO大会推出网络请求框架
> 为了解决一切网络请求:UrlConnection、HttpClient,很简单,企业开发需要封装一层,3000-5000还代码,代码不统一
> 特点:自己处理异步,回调UI;支持图片缓存;支持绑定Activity声明周期
> Volley:齐射并发,并发访问服务器
### 04.Volley2个核心类和三部曲
> 2个核心类:Request请求对象、RequestQueue请求队列
> Volley三部曲:
1.创建请求对象
2.创建请求队列
3.将请求对象添加到队列中
### 05.Volley四大请求-StringRequest
StringRequest:返回字符串
### 06.Volley四大请求-JsonObjectRequest
JsonObjectRequest返回json对象
### 07.Volley四大请求-JsonArrayRequest
JsonArrayRequest返回json数组对象
### 08.Volley四大请求-ImageRequest
返回Bitmap对象
### 09.Volley四大请求-ImageRequest大图压缩
//图片采样率:图片分辨率是原来的 width/8 * height/8 = 原来的1/8²
decodeOptions.inSampleSize = 8;
google Volley计算采样率
static int findBestSampleSize(
int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
double wr = (double) actualWidth / desiredWidth;
double hr = (double) actualHeight / desiredHeight;
double ratio = Math.min(wr, hr);
float n = 1.0f;
while ((n * 2) <= ratio) {
n *= 2;
}
return (int) n;
}
//true:加载到内存,false:先计算压缩比率,先获取宽度,先不加载到内存
decodeOptions.inJustDecodeBounds = false
### 10.Volley四大请求ImageRequest参数Config.RGB
Bitmap.Config decodeConfig = Bitmap.Config.RGB_565;
/*
//计算一张图片大小
Bitmap.Config.RGB_565:一个像素点16位,一个Byte占8位,2个字节
Bitmap.Config.ALPHA_8:一个像素点8位,1个字节
Bitmap.Config.ARGB_4444:一个像素点16位,2个字节
Bitmap.Config.ARGB_8888:一个像素点32位,4个字节
有一张图片480 * 800 ,请计算RGB_565图片大小
一共有480 * 800个像素点,一个像素点占2个字节,总Byte数=480*800*2byte =
开发中到底使用哪种格式?模糊图片、很小图片,建议使用ARGB_8888;高清大图RGB_565
Volley中常用RGB_565
UIL:ARGB_8888
*/
### 11.Volley请求-Map提交参数
//重写方法处理参数
@Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> params = new HashMap<String, String>();
params.put("username", "用户名");
params.put("password", "密码");
return params;
}
### 12.NetworkImageView加载网络图片
RequestQueue queue = Volley.newRequestQueue(this);
ImageLoader.ImageCache imageCache = new MyImageCache();
ImageLoader imageLoader = new ImageLoader(queue, imageCache);
mNetImg.setImageUrl(url, imageLoader);
### 13.图片缓存LruCache算法介绍
> 几种引用级别:
* 强引用:我们平时创建的集合(Map、Set、List)当内存不足,不会释放对象
* 软引用:当内存不足,会释放对象。以前缓存图片使用,现在Avoid Soft References for Caching,避免使用软引用缓存,谷歌建议使用LruCache图片缓存
* 弱引用
* 虚引用
> LRU:最近最少使用,当空间不足,移除最近最少使用元素
> 缓存图片:使用集合,集合众多,到底使用哪个?url-bitmap,key-value,只有Map符合key-value
> 算法决定哪个Map,算法LRU,开LruCache文档
new LinkedHashMap<K, V>(0, 0.75f, true);
//initialCapacity :初始化容器大小,和数据库可变长度类似,气球
//loadFactor:负载因子,每次增加比例值
//accessOrder :排序规则,true根据访问顺序排序,false根据插入顺序
> 谷歌使用LinkedHashMap是Map集合,符合key-value形式,并且最后一个参数true,根据访问顺序排序
### 14.LruCache实现图片缓存
private class MyImageCache implements ImageLoader.ImageCache{
private static final int MAX_SIZE = 5 * 1024 * 1024;//最大缓存5M
//创建LruCache
LruCache<String, Bitmap> lruCache = new LruCache<String, Bitmap>(MAX_SIZE){
//计算每张图片大小
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();//注意:单位要统一
}
};
//从缓存读取
@Override
public Bitmap getBitmap(String url) {
return lruCache.get(url);
}
//存储到缓存
@Override
public void putBitmap(String url, Bitmap bitmap) {
lruCache.put(url, bitmap);
}
}
> 三级存储:网络、本地、内存
### 15.哪些公司使用Volley做图片缓存
> 京东商城
> 豌豆荚
### 16.缓存更新策略
> 什么时候更新缓存:参考京东商城
* 最优方式:模块间跳转清空缓存,下次进来从网络获取。举例子,京东商城手机列表,退出手机列表重新进入,如果没有网络,不显示缓存。本模块才使用缓存,退出模块清空
* 根据时间更新:30分钟,很少使用,开发很麻烦,首页退出不清空缓存
* ListView显示巧克力数据,当服务器修改商品价格,客户端要不要修改?在价格修改前,页面加载完成,不要修改。
### 17.自定义Volley请求
> 请求返回的Bean对象
> 发现StringRequst继承Request<String>,我们继承Request<T>
public class MyRequest<T> extends Request<T> {
private final Class<T> clazz;
//成功回调接口
private final Response.Listener<T> listener;
public MyRequest(String url, Class<T> clazz, Response.Listener<T> listener, Response.ErrorListener errorListener) {
super(url, errorListener);
this.clazz = clazz;
this.listener = listener;
}
//解析服务器返回数据
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
//参考StringRequest源代码
T t = null;
String parsed;
try {
//服务器返回字符串
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
//String -> Bean:Gson/fastJson
t = JSON.parseObject(parsed, clazz);
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(t, HttpHeaderParser.parseCacheHeaders(response));
}
//传递数据给UI
@Override
protected void deliverResponse(T t) {
listener.onResponse(t);
}
}
### 18.Volley绑定页面声明周期
> 请求对象设置tag
request.setTag(tag);
request2.setTag(tag);
request3.setTag(tag);
queue.add(request);
queue.add(request2);
queue.add(request3);
> 退出页面取消请求
protected void onDestroy() {
super.onDestroy();
//关闭Volley请求
Log.d("HttpUtils", "onDestroy取消网络请求,tag="+tag);
HttpUtils.getInstance().cancelAll(tag);
}
### 19.Volley二次封装-常规方式
> 网络请求属于业务层:独立封装到另外一个类,不能放到Activity
> 网络数据需要回传给UI,通过接口回调
public void getComputer(final OnNetResponseListener listener){
//UrlConnection
new Thread(new Runnable() {
@Override
public void run() {
try {
HttpURLConnection conn = (HttpURLConnection) new URL(URL_COMPUTER).openConnection();
InputStream is = conn.getInputStream();
String result = IoUtils.convertStreamToString(is);
is.close();
listener.onSuccess(result);
} catch (Exception e) {
e.printStackTrace();
listener.onFailed(e);
}
}
}).start();
}
/**
* 网络请求回调接口
*/
public interface OnNetResponseListener{
void onSuccess(String result);
void onFailed(Exception e);
}
### 20.Volley二次封装完成
> 原则:使用的地方(Activity使用网络请求工具类)不能出现第三方框架包名
> 数据回传使用自己的接口
### 21.AES加密算法封装
> AES:Advanced Encryption Standard,高级数据加密标准(比DES加密复杂度增加)
> AES:对称加密(一个秘钥,有秘钥就可以破解)
public static String encrypt(String input, String password) {
try {
//加密算法3部曲
//1.创建Cipher对象
Cipher cipher = Cipher.getInstance(algorithm);
//秘钥规范:秘钥字符串转成对象
Key key = new SecretKeySpec(password.getBytes(), algorithm);
//2.初始化加密/解密模式
cipher.init(Cipher.ENCRYPT_MODE, key);
//3.加密/解密
byte[] bytes = cipher.doFinal(input.getBytes());
String encrypt = Base64.encode(bytes);
//System.out.println("AES加密="+encrypt);
return encrypt;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* AES解密
* @param input
* @param password
* @return
*/
public static String decrypt(String input, String password) {
try {
//加密算法3部曲
//1.创建Cipher对象
Cipher cipher = Cipher.getInstance(algorithm);
//秘钥规范:秘钥字符串转成对象
Key key = new SecretKeySpec(password.getBytes(), algorithm);
//2.初始化加密/解密模式
cipher.init(Cipher.DECRYPT_MODE, key);
//3.加密/解密
byte[] bytes = cipher.doFinal(Base64.decode(input));
String encrypt = new String(bytes);
//System.out.println("AES加密="+encrypt);
return encrypt;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
### 22.MVP框架介绍

> Model:业务(网络请求、缓存数据),Bean
> View:视图,Activity、Fragment
> Presenter:起到桥梁的作用,相当于经纪人
> MVP出现时为了解耦MVC
#### 23.MVP框架-Model层实现
> 登录需求:登录显示加载进度,登录成功跳转主页;登录失败提示用户
> Model层:业务,登录业务,面向接口编程
public class UserLoginModel implements IUserLoginModel {
@Override
public void login(final String username, final String password, final OnLoginListener listener) {
//访问网络:判断用户名和密码
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if("heima38".equals(username) && "123".equals(password)){
//登录成功
User user = new User(username, password);
listener.loginSuccess(user);
}else{
//登录失败
listener.loginFailed();
}
}
}).start();
}
}
#### 24.MVP框架-View层实现
> View层如何编码?如何判断哪些是View?
* 判断的原则:哪些属于View就是View,2个EidtText、加载进度条,登录结果
* 1.有那些操作:获取用户名、获取密码
* 2.用什么用户交互:显示进度条、隐藏进度条
* 3.结果是什么:成功跳转主页、失败提示用户
> View层是Activity,由Activity实现View层接口
public interface IUserLoginView {
//1.有那些操作:获取用户名、获取密码
/**获取用户名*/
String getUsername();
/**获取密码*/
String getPassword();
//2.有什么用户交互:显示进度条、隐藏进度条
/**显示进度条*/
void showProgressDialog();
/**隐藏进度条*/
void hideProgressDialog();
//3.结果是什么:成功跳转主页、失败提示用户
/**成功跳转主页*/
void jump2Main(User user);
/**失败提示用户*/
void showLoginError();
}
#### 25.MVP框架-Presenter层实现
> presenter如何编码:
* 起到桥梁作用,既然起到桥梁作用,p层肯定持有model和view层对象
public class UserLoginPresenter implements OnLoginListener {
/**
* 既然起到桥梁作用,p层肯定持有model和view层对象:成员变量
* 既然起到桥梁作用,p层应该提供桥梁方法(登录)
*/
private IUserLoginModel model;
private IUserLoginView view;
/**
* 2个参数构造方法对吗?p层构造方法谁调用,View层调用
* 如果是2个参数构造方法,说明Activity肯定持有Model对象,不符合MVP架构
* @param view
*/
public UserLoginPresenter(IUserLoginView view) {
this.model = new UserLoginModel();
this.view = view;
}
/**
* model层和View层通信方法
*/
public void login(){
//显示进度条
view.showProgressDialog();
//调用登录业务方法:下面这行代码集中体现P层起到桥梁作用
model.login(view.getUsername(), view.getPassword(), this);
}
@Override
public void loginSuccess(final User user) {
mHandler.post(new Runnable() {
@Override
public void run() {
//隐藏进度条
view.hideProgressDialog();
//跳转主页
view.jump2Main(user);
}
});
}
@Override
public void loginFailed() {
mHandler.post(new Runnable() {
@Override
public void run() {
//隐藏进度条
view.hideProgressDialog();
//提示用户
view.showLoginError();
}
});
}
private Handler mHandler = new Handler();
}