纱要是android客户端好重点的一部分。下面从入门级开始介绍下团结Android网络要的实行过程。希望会吃正接触Android网络有的情侣有帮扶。
本文包含:
- HTTP请求&响应
- Get&Post
- [HttpClient & HttpURLConnection](#HttpClient & HttpURLConnection)
- 同步&异步
- HTTP缓存机制
- Volley&OkHttp
- Retrofit&RestAPI
- 纱图片加载优化
- Fresco&Glide
- 图管理方案
HTTP请求&响应
既是说自入门级开始就说说Http请求包的布局。
相同破呼吁虽是朝着目标服务器发送一弄错文本。什么样的公文?有下面结构的公文。
HTTP请求保管结构
请求包
例子:
POST /meme.php/home/user/login HTTP/1.1
Host: 114.215.86.90
Cache-Control: no-cache
Postman-Token: bd243d6b-da03-902f-0a2c-8e9377f6f6ed
Content-Type: application/x-www-form-urlencoded
tel=13637829200&password=123456
伸手了就会见接响应包(如果对面有HTTP服务器)
HTTP响应包结构
响应包
例子:
HTTP/1.1 200 OK
Date: Sat, 02 Jan 2016 13:20:55 GMT
Server: Apache/2.4.6 (CentOS) PHP/5.6.14
X-Powered-By: PHP/5.6.14
Content-Length: 78
Keep-Alive: timeout=5, max=100
Connection: Keep-Alive
Content-Type: application/json; charset=utf-8
{"status":202,"info":"\u6b64\u7528\u6237\u4e0d\u5b58\u5728\uff01","data":null}
Http请求方式产生
方法 | 描述 |
---|---|
GET | 请求指定url的数据,请求体为空(例如打开网页)。 |
POST | 请求指定url的数据,同时传递参数(在请求体中)。 |
HEAD | 类似于get请求,只不过返回的响应体为空,用于获取响应头。 |
PUT | 从客户端向服务器传送的数据取代指定的文档的内容。 |
DELETE | 请求服务器删除指定的页面。 |
CONNECT | HTTP/1.1协议中预留给能够将连接改为管道方式的代理服务器。 |
OPTIONS | 允许客户端查看服务器的性能。 |
TRACE | 回显服务器收到的请求,主要用于测试或诊断。 |
常用只有Post与Get。
Get&Post
网络请求中我们经常因此键值对来导参数(少部分api用json来传递,毕竟不是主流)。
透过地方的牵线,可以见到虽然Post与Get本意一个是表单提交一个凡是要页面,但本质并没有啊界别。下面说说参数在及时2者的岗位。
-
Get方式
以url中填入参数:http://xxxx.xx.com/xx.php?params1=value1¶ms2=value2
甚至采取路由
http://xxxx.xx.com/xxx/value1/value2/value3
这些就是是web服务器框架的从业了。
-
Post方式
参数是透过编码在请求体中的。编码包括x-www-form-urlencoded
与
form-data
。
x-www-form-urlencoded
的编码方式是这般:
tel=13637829200&password=123456
form-data
的编码方式是这样:
—-WebKitFormBoundary7MA4YWxkTrZu0gW
Content-Disposition: form-data; name=”tel”13637829200 ----WebKitFormBoundary7MA4YWxkTrZu0gW Content-Disposition: form-data; name="password" 123456 ----WebKitFormBoundary7MA4YWxkTrZu0gW
x-www-form-urlencoded
的优越性就挺明显了。不过x-www-form-urlencoded
不得不传键值对,但是form-data
足传二进制
因为url是存在被要求行中之。
据此Get与Post区别本质就是是参数是置身请求行丁要么在请求体中
理所当然无论用哪种都能放在请求头备受。一般以伸手求头中加大有殡葬端的常量。
有人说:
- Get是明文,Post隐藏
移步端不是浏览器,不用https全都是公开。 - Get传递数据上限XXX
胡说。有限制的是浏览器中之url长度,不是Http协议,移动端请求无影响。Http服务器部分发生限制的安装一下即可。 - Get中文需要编码
是真的…要注意。URLEncoder.encode(params, "gbk");
或者建议用post规范参数传递方式。并从未啊更出色,只是大家还如此社会再和谐。
地方说的凡伸手。下面说响应。
伸手是键值对,但返回数据我们常常因此Json。
于内存中的布局数据,肯定使因此多少描述语言将对象序列化成文本,再就此Http传递,接收端并于文本还原成结构数据。
对象(服务器)<–>文本(Http传输)<–>对象(移动端) 。
服务器返回的数码大部分且是扑朔迷离的结构数据,所以Json最适合。
Json解析库有很多Google的Gson,阿里的FastJson。
Gson的用法看这里。
HttpClient & HttpURLConnection
HttpClient早让废了,谁又好这种题材吧只有经历落后的面试官才会咨询。具体原因可以拘留这里。
脚说说HttpURLConnection的用法。
极端开始接触的尽管是是。
public class NetUtils {
public static String post(String url, String content) {
HttpURLConnection conn = null;
try {
// 创建一个URL对象
URL mURL = new URL(url);
// 调用URL的openConnection()方法,获取HttpURLConnection对象
conn = (HttpURLConnection) mURL.openConnection();
conn.setRequestMethod("POST");// 设置请求方法为post
conn.setReadTimeout(5000);// 设置读取超时为5秒
conn.setConnectTimeout(10000);// 设置连接网络超时为10秒
conn.setDoOutput(true);// 设置此方法,允许向服务器输出内容
// post请求的参数
String data = content;
// 获得一个输出流,向服务器写数据,默认情况下,系统不允许向服务器输出内容
OutputStream out = conn.getOutputStream();// 获得一个输出流,向服务器写数据
out.write(data.getBytes());
out.flush();
out.close();
int responseCode = conn.getResponseCode();// 调用此方法就不必再使用conn.connect()方法
if (responseCode == 200) {
InputStream is = conn.getInputStream();
String response = getStringFromInputStream(is);
return response;
} else {
throw new NetworkErrorException("response status is "+responseCode);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.disconnect();// 关闭连接
}
}
return null;
}
public static String get(String url) {
HttpURLConnection conn = null;
try {
// 利用string url构建URL对象
URL mURL = new URL(url);
conn = (HttpURLConnection) mURL.openConnection();
conn.setRequestMethod("GET");
conn.setReadTimeout(5000);
conn.setConnectTimeout(10000);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
InputStream is = conn.getInputStream();
String response = getStringFromInputStream(is);
return response;
} else {
throw new NetworkErrorException("response status is "+responseCode);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.disconnect();
}
}
return null;
}
private static String getStringFromInputStream(InputStream is)
throws IOException {
ByteArrayOutputStream os = new ByteArrayOutputStream();
// 模板代码 必须熟练
byte[] buffer = new byte[1024];
int len = -1;
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
}
is.close();
String state = os.toString();// 把流中的数据转换成字符串,采用的编码是utf-8(模拟器默认编码)
os.close();
return state;
}
}
只顾网络权限!被坑了略微次。
<uses-permission android:name="android.permission.INTERNET"/>
同步&异步
立马2独概念才设有于多线程编程中。
android中默认只发一个主线程,也于UI线程。因为View绘制只能当这个线程内进行。
用只要您死了(某些操作而这线程在这边运行了N秒)这个线程,这中View绘制将不可知开展,UI就见面卡壳。所以一旦着力避免在UI线程进行耗时操作。
纱要是一个杰出耗时操作。
经过地方的Utils类进行网络要单来一行代码。
NetUtils.get("http://www.baidu.com");//这行代码将执行几百毫秒。
设您这样勾画
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String response = Utils.get("http://www.baidu.com");
}
就会死。。
即时虽是一道方式。直接耗时操作阻塞线程直到数据接受了毕然后返。Android不允许的。
异步方式:
//在主线程new的Handler,就会在主线程进行后续处理。
private Handler handler = new Handler();
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.text);
new Thread(new Runnable() {
@Override
public void run() {
//从网络获取数据
final String response = NetUtils.get("http://www.baidu.com");
//向Handler发送处理操作
handler.post(new Runnable() {
@Override
public void run() {
//在UI线程更新UI
textView.setText(response);
}
});
}
}).start();
}
于子线程进行耗时操作,完成后通过Handler将更新UI的操作发送至主线程执行。这即让异步。Handler是一个Android线程模型中一言九鼎之事物,与网无关便不说了。关于Handler不了解就优先夺Google一下。
关于Handler原理同首对的稿子
但这样描写好丢人。异步通常伴随者他的好基友回调
。
即时是经回调封装的Utils类。
public class AsynNetUtils {
public interface Callback{
void onResponse(String response);
}
public static void get(final String url, final Callback callback){
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
final String response = NetUtils.get(url);
handler.post(new Runnable() {
@Override
public void run() {
callback.onResponse(response);
}
});
}
}).start();
}
public static void post(final String url, final String content, final Callback callback){
final Handler handler = new Handler();
new Thread(new Runnable() {
@Override
public void run() {
final String response = NetUtils.post(url,content);
handler.post(new Runnable() {
@Override
public void run() {
callback.onResponse(response);
}
});
}
}).start();
}
}
然后下方式。
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = (TextView) findViewById(R.id.webview);
AsynNetUtils.get("http://www.baidu.com", new AsynNetUtils.Callback() {
@Override
public void onResponse(String response) {
textView.setText(response);
}
});
举凡无是雅很多。
啊,一个傻乎乎到哭的大网要方案成型了。
懵的地方时有发生那么些:
- 老是都new Thread,new Handler消耗过非常
- 没很处理机制
- 没有缓存机制
- 未曾健全之API(请求头,参数,编码,拦截器等)与调试模式
- 没有Https
HTTP缓存机制
缓存对于移动端是死重要的存在。
- 减掉请求次数,减多少服务器压力.
- 地面数据读取速度还快,让页面不见面空白几百毫秒。
- 于管网的状下提供数据。
缓存一般由服务器控制(通过某些方式可以本地控制缓存,比如向过滤器添加缓存控制信息)。通过以伸手头上加下面几乎单字端:
Request
请求头字段 | 意义 |
---|---|
If-Modified-Since: Sun, 03 Jan 2016 03:47:16 GMT | 缓存文件的最后修改时间。 |
If-None-Match: "3415g77s19tc3:0" | 缓存文件的Etag(Hash)值 |
Cache-Control: no-cache | 不使用缓存 |
Pragma: no-cache | 不使用缓存 |
Response
响应头字段 | 意义 |
---|---|
Cache-Control: public | 响应被共有缓存,移动端无用 |
Cache-Control: private | 响应被私有缓存,移动端无用 |
Cache-Control:no-cache | 不缓存 |
Cache-Control:no-store | 不缓存 |
Cache-Control: max-age=60 | 60秒之后缓存过期(相对时间) |
Date: Sun, 03 Jan 2016 04:07:01 GMT | 当前response发送的时间 |
Expires: Sun, 03 Jan 2016 07:07:01 GMT | 缓存过期的时间(绝对时间) |
Last-Modified: Sun, 03 Jan 2016 04:07:01 GMT | 服务器端文件的最后修改时间 |
ETag: "3415g77s19tc3:0" | 服务器端文件的Etag[Hash]值 |
正式以时按需求或就含有其中有的字段。
客户端要因这些信息囤积这次要信息。
然后以客户端发起呼吁的当儿如果反省缓存。遵循下面步骤:
浏览器缓存机制
注意服务器返回304意是数额尚未改变滚去读缓存信息。
已经年轻的本人为好写的网络要框架添加到了缓存机制,还得意,直到发生同等龙自己看了下2只东西。(/TДT)/
Volley&OkHttp
Volley&OkHttp应该是本最常用之网要求库。用法也老相像。都是故构造请求参加请求队列的计管理网络请求。
先说Volley:
Volley可以经过这个库进展依赖.
Volley在Android 2.3与以上版本,使用的凡HttpURLConnection,而当Android
2.2跟以下版本,使用的是HttpClient。
Volley的主导用法,网上资料无数,这里推荐郭霖大神的博客
Volley存在一个缓存线程,一个大网请求线程池(默认4单线程)。
Volley这样一直用出效率会于小,我以自我使用Volley时之各种技能封装成了一个库RequestVolly.
自以此库房中将构造请求的不二法门封装为了函数式调用。维持一个大局的恳求队列,拓展一些有利的API。
可是再怎么封装Volley在效能拓展性上总无法和OkHttp相比。
Volley停止了履新,而OkHttp得到了官方的确认,并在持续优化。
用自最终替换为OkHttp
OkHttp用法见这里
死温馨之API与详尽的文档。
当时首文章呢描绘的非常详细了。
OkHttp使用Okio开展数量传。都是Square家的。
然而连无是直用OkHttp。Square公司还闹了一个Retrofit库配合OkHttp战斗力翻倍。
Retrofit&RestAPI
Retrofit大幅度的简化了网络要的操作,它应当说只是是一个Rest
API管理库,它是一直利用OKHttp进行网络要并无影响你针对OkHttp进行部署。毕竟都是Square公司出品。
RestAPI是一模一样栽软件设计风格。
服务器作为资源存放地。客户端去请GET,PUT,
POST,DELETE资源。并且是不管状态的,没有session的参与。
挪动端与服务器交互最重点的哪怕是API的宏图。比如就是一个专业的报到接口。
Paste_Image.png
你们当看的发是接口对应的乞求保管跟应包约是啊则吧。
恳请方式,请求参数,响应数据,都老清晰。
采取Retrofit这些API可以直观的体现在代码中。
Paste_Image.png
下一场使用Retrofit提供于你的之接口的兑现类
就会直接进行网络要获得结构数据。
在意Retrofit2.0交互较1.9进展了大量休匹配更新。google上大部分科目都是因1.9底。这里有个2.0的教程。
学科里进行异步请求是采用Call。Retrofit最劲的地方在于支持RxJava。就像自家及图备受回到的凡一个Observable。RxJava上亲手难度比较高,但用了就再度为离开不起了。Retrofit+OkHttp+RxJava配合框架打起成吨的输出,这里不再多说。
网要学习及此地我觉得就交到了。。
纱图片加载航天科技优化
对图片的传,就比如上面的报到接口的avatar字段,并无会见一直将图纸写以回去内容里,而是于一个图形的地点。需要时再次失加载。
如果你直接用HttpURLConnection去取得一摆放图片,你办得,不过并未优化就独自是个BUG不断demo。绝对免能够正式使用。
顾网络图片有些特点:
-
她永远不见面更换
一个链接对应的图形一般永远不会见换,所以当第一潮加载了图片时,就应与永久缓存,以后就是不再网要。 -
她不行占内存
同摆图纸小之几十k多之几M高清无码。尺寸为是64*64及2k图。你无能够就这样直接显示到UI,甚至无克一直放大上内存。 - 它而加载很老
加载一布置图纸需要几百ms到几m。这里面的UI占个图功能为是得考虑的。
说说我以方提到的RequestVolley里举行的图样请处理(没错我举行了,这部分的代码可以错过github里看源码)。
三级缓存
网上时不时说三级缓存--服务器,文件,内存。不过我觉着服务器无算是一级缓存,那便是数量源嘛。
-
外存缓存
先是内存缓存使用LruCache。LRU是Least Recently Used
近期最少使用算法,这里确定一个轻重缓急,当Map里对象大小总和超此非常时将运用效率低于的目标释放。我以内存大小限制为经过可用内存的1/8.
外存缓存里读博的多寡就是直回到,读不交之通往硬盘缓存要数。 -
硬盘缓存
硬盘缓存使用DiskLruCache。这个类似不在API中。得复制利用。
看见LRU就了解了吧。我用硬盘缓存大小设置为100M。@Override public void putBitmap(String url, Bitmap bitmap) { put(url, bitmap); //向内存Lru缓存存放数据时,主动放进硬盘缓存里 try { Editor editor = mDiskLruCache.edit(hashKeyForDisk(url)); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, editor.newOutputStream(0)); editor.commit(); } catch (IOException e) { e.printStackTrace(); } } //当内存Lru缓存中没有所需数据时,调用创造。 @Override protected Bitmap create(String url) { //获取key String key = hashKeyForDisk(url); //从硬盘读取数据 Bitmap bitmap = null; try { DiskLruCache.Snapshot snapShot = mDiskLruCache.get(key); if(snapShot!=null){ bitmap = BitmapFactory.decodeStream(snapShot.getInputStream(0)); } } catch (IOException e) { e.printStackTrace(); } return bitmap; }
DiskLruCache的原理不再说了(我还缓解了其有的一个BUG,向Log中长的数码增删记录时,最后一长没有出口,导致最终一漫长缓存一直失效。)
- 硬盘缓存也从不数据就回到空,然后就于服务器请求数据。
及时虽是周流程。
可是自己这么的处理方案要来无数受制。
- 图未经压缩处理直接存储使用
- 文件操作以主线程
- 从来不全面之图纸处理API
以前为认为这样就够好直到我碰到下面俩。
Fresco&Glide
莫用想为晓得它还举行了怪全面之优化,重复过去轮子的行为非常愚蠢。
Fresco是Facebook公司的越轨科技。光看作用介绍就盼非常强劲。使用办法官方博客说的够用详细了。
实在三级缓存,变换后的BItmap(内存),变换前之原来图片(内存),硬盘缓存。
在内存管理上落成了最为。对于重度图片采取的APP应该是挺好之。
它们一般是直动用SimpleDraweeView
来替换ImageView
,呃~侵入性较强,依赖上其apk包直接杀1M。代码量惊人。
从而自己再次爱好Glide,作者是bumptech。这个库房被广泛的运在google的开源项目遭到,包括2014年google
I/O大会上宣布的官方app。
这里发详实介绍。直接下ImageView即可,无需初始化,极简的API,丰富的拓展,链式调用都是自个儿爱好的。
增长的进行指的就是这个。
除此以外自也就此了Picasso。API与Glide简直一模型一样,功能略少,且产生一半年未修复的BUG。
图片管理方案
加以说图片存储。不要在好服务器上面,徒增流量压力,还从未图处理功能。
推荐七牛与阿里云存储(没因此了任何
π__π
)。它们都出不行关键之同样起图片处理。在图纸Url上助长参数来对图纸展开部分甩卖又传。
于是乎(七牛之处理代码)
public static String getSmallImage(String image){
if (image==null)return null;
if (isQiniuAddress(image)) image+="?imageView2/0/w/"+IMAGE_SIZE_SMALL;
return image;
}
public static String getLargeImage(String image){
if (image==null)return null;
if (isQiniuAddress(image)) image+="?imageView2/0/w/"+IMAGE_SIZE_LARGE;
return image;
}
public static String getSizeImage(String image,int width){
if (image==null)return null;
if (isQiniuAddress(image)) image+="?imageView2/0/w/"+width;
return image;
}
既然如此好加速请求速度,又能压缩流量。再配合Fresco或Glide。完美的图样加载方案。
但是就便待而拿具有图片都存放于七牛要阿里云,这样吗无可非议。
图片/文件上传也还是行使它们第三方存储,它们还起SDK与法定文档教你。
但图片一定要是减了后上传。上传1-2M大之高清照片未曾意义。