OkHttp
Android基础网络编程:socket,HttpURLConnection,HttpClient
socket
socket通信模型
socket是什么
- 是一个对TCP/IP协议进行封装的编程调用接口(不是协议,属于传输层,是api)
- 成对出现,一对套接字:包括ip地址和端口号
Socket与Http区别
- Http采用Request-Response方式,Http协议属于应用层
- Socket采用服务器主动发送数据的方式,Socket属于传输层
Socket如何实现(TCP)
//客户端
public void TCPSendMessage(String msg){
Socket socket = null;
OutputStream output = null;
InputStream input = null;
try{
socket = new Socket("192.168.1.100",8888);
output = socket.getOutputStream();
output.write(msg.getBytes());
socket.shutdownOutput();
input = socket.getInputStream();
byte[] b = new byte[1024];
int len = -1;
StringBuffer sb = new StringBuffer();
while(len = input.read(b) != -1){
sb.append(new String(b,0,len,Charset.forName("gbk")));
}
//todo 在主线程中更新UI
}catch(UnknownHostException e){
e.printStackTrace();
}catch(IOException e){
e.printStackTrace();
}finally{
try{
if(socket != null){
//输出流对象outputStream不需要关闭,因为并没有创建输出流,只是从Socket获取的
socket.close();
}
}catch(IOException e){
}
e.printStackTrace();
}
}
//服务器端
public void ReceiveMessage(){
ServerSocket server = null;
Socket socket = null;
try{
server = new ServerSocket(8888);
while(true){
socket = server.accept();
InputStream input = socket.getInputStream();
BufferInputStream bis = new BufferInputStream(input);
byte[] b = new byte[1024];
int len = -1;
while(len = bis.read(b) != -1){
System.out.println(new String(b,0,len,"UTF-8"));
}
socket.shutdownInput();
OutputStream outputResult = socket.getOutputStream();
outputResult.write("The message has received".getBytes());
bis.close();
socket.close();
socket = null;
}
}catch(IOException e){
e.printStackTrace();
}
}
WebSocket
Http轮询
轮询是在特定的时间间隔,由浏览器(客户端)对服务器发出Http请求,然后由服务器返回最新的数据给浏览器
-
短轮询
缺点:在某个时间段内server没有更新数据,但client仍然每隔一段时间发送请求来询问,所以这段时间内的询问都是无效的 -
长轮询
收到request会查看数据是否更新,除非更新再返回response
轮询缺点:
- 浪费带宽(Http Head比较大)
- 消耗服务器CPU资源
Websocket与Http
- 与Http同等的网络协议
- 双向通信协议(服务器可以主动发送数据给客户端)
Websocket与Socket
- Socket并不是一个网络协议
总结
- 本质上是一个基于TCP的协议
- 建立WebSocket连接需要向服务器发起一个Http请求,Head中加入"Upgrade:WebSocket"
- 服务器端解析这些附加的头信息
okhttp连接websocket
void sendWebSocketReq(final String hostName, final String jsonData, final WebSocketCallBack callBack) {
//构建request
final Request request = new Request.Builder().url(hostName).build();
//建立websocket
client.newWebSocket(request, new WebSocketListener() {
@Override
public void onOpen(WebSocket webSocket, Response response) {
super.onOpen(webSocket, response);
//onOpen代表连接服务器成功,在此方法发送数据
Log.i(hostName, "websocket Json: " + jsonData);
webSocket.send(jsonData);
Log.i(hostName, "onOpen: " + "message: " + request);
}
@Override
public void onMessage(WebSocket webSocket, String text) {
super.onMessage(webSocket, text);
Log.i(hostName, "onMessage: " + text);
callBack.onSuccess(webSocket, text);
webSocket.cancel();
}
@Override
public void onMessage(WebSocket webSocket, ByteString bytes) {
super.onMessage(webSocket, bytes);
Log.i(hostName, "onMessage: " + bytes.toString());
callBack.onSuccess(webSocket, bytes.toString());
webSocket.cancel();
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
super.onClosing(webSocket, code, reason);
Log.i(hostName, "onClosing: " + "code: " + code + "reason:" + reason);
}
@Override
public void onClosed(WebSocket webSocket, int code, String reason) {
super.onClosed(webSocket, code, reason);
Log.i(hostName, "onClosed: " + "code: " + code + "reason:" + reason);
callBack.onClosed();
}
@Override
public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) {
super.onFailure(webSocket, t, response);
Log.i(hostName, "onFailure: " + "error: " + t.getMessage());
callBack.onFailure(t.toString());
webSocket.cancel();
}
});
client.dispatcher().executorService();
}
//Websocket外部回调接口,对应WebSocketListener
public interface WebSocketCallBack {
void onSuccess(WebSocket webSocket,String response);
void onFailure(String errorInfo);
void onClosed();
}
Http缓存
强制缓存
-
Expires(Http1.0)
Expires的值为服务器返回的到期时间
存在问题:到期时间是由服务端生成的,有可能造成缓存读取的误差 -
Cache-Control
是由服务器返回的Response中添加的头信息 -
private 客户端可以缓存
-
public 客户端和代理服务器都可以缓存
-
max-age 缓存将在若干秒后失效
-
no-cache
-
no-store
对比缓存
- ETag:服务器响应请求时,告诉浏览器当前资源在服务器的唯一标识
- If-None-Match:再次请求服务器时,通过此字段通知服务器客户端缓存数据的唯一标识
- Last-Modified:服务器在响应请求时,告诉浏览器资源的最后修改时间
- If-Modified-Since:再次请求服务器时,通过此字段通知服务器上次请求时,服务器返回的资源的最后修改时间
断点续传
public void downloadWithOkHttp(){
InputStream is = null;
long downloadLength = 0;
String downloadUrl = "www.morganchain.xxx.apk";
String fileName = downloadUrl.subString(downloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublishDirectory(Environment.DIRECTORY_DOWNLOADS);
File file = new FIle(directory + fileName);
if(file.exists()){
downloadLength = file.length();
}
long contentLength = getContentLength(downloadUrl);
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder();
.addHeader("RANGE","bytes=" + downloadLength + "-")//断点续传要用到,指示下载区间
.url(downloadUrl)
.build();
try{
Response response = client.newCall(request).execute();
if(response != null){
is = response.body().byteStream();
RandomAccessFile saveFile = new RandomAccessFile(file,"rw");
saveFile.seek(downloadLength);//跳过已经下载的字节
byte[] b = new byte[1024];
int total = 0;
int len = 0;
while((len = is.read(b)) != -1){
total += len;
saveFile.write()b, 0, len);
//计算已经下载的百分比
int progress = (int)((total + downloadLength) * 100 / contentLength);
}
}
}catch(){
}
}
多线程下载
- 每个线程只负责下载文件的一部分
Https对称加密/不对称加密
-
Https是一种基于SSL/TLS的Http协议
-
Http所有传输的内容都是明文,Https所有传输的内容都经过加密(对称+不对称)
-
对称加密是指加密和解密用的是同一密钥
-
不对称加密和解密使用的密钥不是同一密钥
-
对称加密所使用的密钥可以通过非对称加密的方式发送出去
Glide
图片三级缓存&LRUCache
- 内存-本地-网络
首次打开app,加载网络图片时,系统会将该图片在内存和本地各一份,当再次加载该图片时,系统先判断内存中是否有该图片,如果有则加载,没有则在本地中寻找,如果有则加载,如果内存和本地都没有该图片缓存,则去网络中加载 - 内存缓存是如何实现的?
- LRU(Least Recently Used)缓存算法
缓存策略主要包括添加,获取,删除三种操作,内存满时,会删除近期最少使用的缓存