关闭

一个简单的网络框架

396人阅读 评论(0) 收藏 举报

简介:

网络框架现在已经很多了,什么github上,opensource,等等开源代码托管网站上,可以找到很多,最基础的httpurlconnection 或者 Apache的 网络开发包已经会用了,我现在有点儿懒 没时间和意识去研究阅读框架源码,只有项目中用到时才会去看。经过这2三年的工作,我在项目中也大致理了一下项目的网络交互部分,这里拿其中一个开发过的app的网络处理部分来写吧。(由于我水平现在有限,瞎比划两下,代码有丑陋和不合理的地方)


当要介绍或者描述一个东西时,如何给别人能清晰的展现出来还时需要一定描写水平的,以前都不在乎这些,慢慢的自己描述的隔了一段时间自己都读不通顺了,工作中有时也会出现因为沟通问题产生不必要的时间开销。我记得刚开始学编程时一个老师说过:先know how ,再know  why,这样循序渐进的学比较有收获,所以我觉得要去研究一个技术点或者知识点 首先就是要去熟悉 或者使用,然后在去看原理啊 ,代码之类的。我写的这个网络框架第一要求就是方便调用,因为在公司项目中这样用习惯了,当时也觉得组长封装的挺好的,其次有时间的话性能或者扩展性方面再做的好一点儿吧,工作3年了总得追求点儿高级的。这里分3步来描述这个网络框架吧:

一、网络描述

二、如何使用

三、网络总体描述

四、源码细节


一、网络描述

对于这个简单的网络框架,我这里最原始的目的一是易用,二是能扩展,三是性能改良一下。能做到一就可以了,有时间再考虑其他。

二、使用

由于客户端和服务器端交互 都有特定的数据格式,我这里不是说的xml或者json,而是服务端和客户端约定的数据格式,我们将它称为  协议  ,好多客户端都有自己的协议来和服务端交互,因此 使用时我们可以想到有协议对象。光有协议对象还不行,既然有了协议对象,那就得有发送协议的对象,而一旦有了发送的,那就必须得有接收的,这样以来app和后台交互涉及的必要的 元素就是协议,发送者,接收者。


协议么 就是bean了 ,封装各种字段之类的


发送者么  就是httpurlconnecion 把bean给后台,当然复杂的话可能要请求队列啊,网络缓存啊,多线程发送控制啊,我们先不考虑那么复杂,复杂了脑子疼。这里只简单放一个队列,当把请求放到队列中后 就不用管了,只要相信发送者会把bean发给后台就行。


接收者么 就是我们接收数据的地方,可以UI线程或者其他地方,掐指一算应该是个接口回调。

于是简单的使用方式就出来了:


<span style="font-size:18px;">	//协议对象
				Cmdlogin2 cmdlogin = new Cmdlogin2();
				//发送者:就是一个请求
				Request request = new Request(cmdlogin,
						//接收数据者
						new UIResponseListener() {

					@Override
					public void responseExchCallBack(Params params) {
						Object resData = params.getResponseData();
						Toast.makeText(MainActivity.this, resData.toString(), 0).show();
					}

					@Override
					public void errorExchCallBack(Params params) {
						String msg = params.responseErrorMsg;
						String url = params.getUrl().toString();
						Toast.makeText(MainActivity.this, url+"-"+msg, 0).show();

							}
						});
				
				NetWorkContainer.getInstance().add(request);</span>

<span style="font-size:18px;">package com.niuzhihua.dao.domain;


import org.json.JSONException;

import com.niuzhihua.core.config.L;
import com.niuzhihua.core.protocol.Params;


public class Cmdlogin2 extends Params{

	public Cmdlogin2() {
		
	}
	
	/**
	 * 打包
	 */
	@Override
	protected Object initsingleProtocol() {
		String singleProtocol = "初始化具体发送的数据。。。";
		L.i(singleProtocol);
		return singleProtocol;
	}
	
	/**
	 * 解析
	 */
	@Override
	public Object unPackDate(byte[] response) {
		L.i("解析处理。。。");
		responseData = new String(response);
		L.i("解析处理完毕。。。");
		return responseData;
	}
	

}
</span>


如果再简洁的写的话 ,两串代码 连者写下来就可以了。这里我们只需要记住 核心的类NetWorkContainer就可以了,其他的类都可以根据传参来写出来。 NetWorkContainer可以改为形象,大方的名字。 协议对象CmdLogin2 继承子Params类,继承后会复写必须要的方法,也可以空实现,总之可以根据自己的app的协议需要来实现。


三、网络框架总体描述

1:总体描述其实只要说一下NetWorkContainer就可以了,NetWorkContainer内不持有一个阻塞队列 BlockingQueue,我们在UIthread中 利用NetWorkContainer把请求放到阻塞队列中,然后不断的从这个队列中取出请求.

2:发给后台:拿到请求后 ,把这个请求加到线程池中去处理。

3:拿到结果:groupExecuService.take(); // ExecutorCompletionService

4: 再用Handler返回给UIthread就可以了。这就是大致过程。

这整个过程中,所有的数据 都封装在一个Request对象中。当发请求时,发这个Request对象,当处理请求时,把这个Request对象加到线程池,最后返回的结果数据也在Request对象中。


四、主要类代码:

NetWorkContainer 发送者:

<span style="font-size:18px;">package com.niuzhihua.core.net;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import android.os.Handler;
import android.os.Message;

import com.niuzhihua.core.config.GlobalConfig;
import com.niuzhihua.core.config.L;
import com.niuzhihua.core.protocol.Params;

/**
 * 管理网络请求的池子:
 * 包含一个存放请求的 队列(BlockingQueue),包含一个线程池(ExecutorService,ExecutorCompletionService),
 * 此类是单实例的,当创建实例后开启一个线程,并一直监听队列,当队列中有数据(请求)时,
 * 就把这个请求取出,放到线程池中取处理,(注意这个取出请求和处理请求一直在子线程中 ,while(true){1:取 2:处理请求}),
 * 当线程池处理完请求后就已经将返回的数据解析到当次请求对象中。所以处理完请求后取出请求对象即可拿到请求对象中的listener对象,
 * 这个listener对象就是负责回调UI thread的,然后就可以发送message到Handler,在handler中收到message后回调处理即可。
 *
 */
public class NetWorkContainer {
	//单实例的容器
	private static NetWorkContainer t = new NetWorkContainer();
	public static NetWorkContainer getInstance() {
		return t;
	}

	private NetWorkContainer() {
		//开启一个 线程 等待并处理 请求。直到主动关闭当前容器
		Thread thread = new Thread(new Runnable() {

			@Override
			public void run() {
				while (true) {
					try {
						Request request = blockingQueue.take();
						doRequest(request);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		});
		thread.start();
		L.i("********线程已经启动");
	}

	// 能拿到一组任务的结果的池子
	private final static ExecutorService executorService = Executors
			.newFixedThreadPool(GlobalConfig.THREAD_COUNT_IN_POOL);
	private final static ExecutorCompletionService<Request> groupExecuService = new ExecutorCompletionService<Request>(
			executorService);

	/**
	 * 存放请求的队列
	 */
	private BlockingQueue<Request> blockingQueue = new ArrayBlockingQueue<Request>(
			GlobalConfig.THREAD_COUNT_IN_POOL);

	//static int testcount = 0;
	public synchronized void add(Request request) {
		// 如果队列没有满,则加入本次请求
		if (blockingQueue.size() < GlobalConfig.THREAD_COUNT_IN_POOL) {
			blockingQueue.offer(request);
		//	testcount ++;
		//	L.i("处理的请求数目:####"+testcount);
		}

	}

	Handler handlerResponseCallBack = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			Request request  = (Request) msg.obj;
			if (GlobalConfig.RESPONSE_OK == request.getParams().getResponseCode()) {
				request.getListener().responseExchCallBack(request.getParams());
			} else {
				request.getListener().errorExchCallBack(request.getParams());
			}

		}
	};

	/**/

	/*
	 * 处理网络当前网络请求 Request request 封装当前请求的 对象
	 */
	public void doRequest(Request request) {

		doRequestByPool(request);
	}

	/**
	 * @param p
	 *            请求发送的数据
	 * @param theListener
	 *            回调处理的实例
	 */
	private void doRequestByPool(Request request) {
		
		// 创建一个网络任务
//		NetWork n = new NetWork(request.getParams(), request.getListener());
		NetWork n = new NetWork(request);
		// 提交网络任务到线程池
		groupExecuService.submit(n);

		Future<Request> result;

		try {
			// 获取线程执行后的结果
			result = groupExecuService.take();

			Request resultRequestData = result.get();

			//e. 消息缓存:通过 Handler 的 obtainMessage 回收 Message 对象,减少 Message 对象的创建开销
			//Message msg = new Message();
			Message msg  =handlerResponseCallBack.obtainMessage();
			//在这里,resultRequestData和request 都可以,因为数据都保存在这个对象里了。
			msg.obj = resultRequestData;
			handlerResponseCallBack.sendMessage(msg);
			
			
		} catch (InterruptedException e1) {
			e1.printStackTrace();
		} catch (ExecutionException e) {
			e.printStackTrace();
		}

	}

	public void cancel() {
		executorService.shutdown();
	}
	 

}
</span>

NetWork :

<span style="font-size:18px;">package com.niuzhihua.core.net;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.SocketTimeoutException;
import java.net.URL;
import java.util.concurrent.Callable;

import com.niuzhihua.core.config.GlobalConfig;
import com.niuzhihua.core.config.L;
import com.niuzhihua.core.interfaces.UIResponseListener;
import com.niuzhihua.core.protocol.Params;

/**
 * 网络请求类:
 * 一、负责执行http请求。由于用的线程池是可以拿到一组线程的结果。所以实现的是Callable接口。
 * 不管是Callable接口还是Runnable接口,这个类的核心是发送数据(HttpURLConnection),拿到返回的数据后
 * 并解析数据(这个类持有请求对象,请求对象中有一个成员是封装请求参数的对象,这个封装请求参数的对象就有打包数据和解析数据的功能)。
 * 
 * 二、
 * 根据是否添加了除url外的请求数据  来确定是get或者post请求</br>
 * 只有url的:get请求
 * 否则post
 */
public class NetWork implements Callable<Request> {


	/**
	 * 网络请求发送的数据
	 */
	public Params params;
	/**
	 * 回调接口,用来回调网络请求回来执行的响应方法
	 */
	UIResponseListener theListener;

	Request request ;
	
	public NetWork(Params cmd, UIResponseListener theListener) {
		this.params = cmd;
		this.theListener = theListener;
	}
	public NetWork(Request request) {
		this.params = request.getParams();
		this.theListener = request.getListener();
		this.request = request;
	}

	/**
	 * post请求
	 * 
	 * @param url
	 *            请求地址
	 * @param content
	 *            发送的内容
	 */
	public Request request() {
		/*
		 * String language; String currentLanguage; language =
		 * Locale.getDefault().getDisplayLanguage(); if (language.indexOf("中文")
		 * != -1) { currentLanguage = "zh"; } else { currentLanguage = "en"; }
		 */
		
		
		String content = (String) params.getRequestData();
		
		URL rui = null;
		HttpURLConnection conn = null;
		InputStream inputStream = null;
		ByteArrayOutputStream byteArrayOutputStream = null;
		byte[] data = null;
		int resCode = 0;

		try {

			rui = params.getUrl();
			// 设置连接属性
			conn = (HttpURLConnection) rui.openConnection();
			conn.setRequestProperty("Connection", "Keep-Alive");
			// 设置超时时间
			conn.setConnectTimeout(GlobalConfig.CONNECT_TIME_OUT);
			conn.setReadTimeout(GlobalConfig.READ_TIME_OUT);

			// 根据是否添加了请求数据来确定是get或者post请求
			if (content == null || "".equals(content)) {
				conn.setRequestMethod("GET");
				L.i("GET");
				
			} else {
				conn.setRequestMethod("POST");
				L.i("POST");
				conn.setDoInput(true);
				conn.setDoOutput(true);
				conn.setRequestProperty("Content-type",
						"application/octest-stream");
				// conn.setRequestProperty("Content-type", "application/json");
				conn.setRequestProperty("Content-Length",
						String.valueOf(content.getBytes().length));
				conn.connect();
				
				OutputStream os = null;
				try {
					os = conn.getOutputStream();
					os.write(content.getBytes());
					os.flush();
				} catch (Exception e) {
					e.printStackTrace();
				}finally{
					if (os != null) {
						os.close();
					}
				}

			}

			//对于网络异常情况,返回错误码(已经连接服务)或者本地定义的提示信息(未连接服务)。
			
			resCode = conn.getResponseCode();
			L.i("响应码:" + resCode);
			params.setResponseCode(resCode);
			// 判断HTTPCODE是否正常
			if (resCode == HttpURLConnection.HTTP_OK) {
				inputStream = conn.getInputStream();
				/*
				 * byteArrayOutputStream = new ByteArrayOutputStream(); int i;
				 * // 一个个字符读取,2g手机网络一个TCP包较小 while ((i = inputStream.read()) !=
				 * -1) { byteArrayOutputStream.write(i); } data =
				 * byteArrayOutputStream.toByteArray();
				 */

				//2种读取方式
				//data = StreamTool.read(inputStream);
				data = StreamTool.bufferRead(inputStream).getBytes();
				//解析网络返回数据
				params.unPackDate(data);
			}

		} catch (Exception e) {
			if(e instanceof SocketTimeoutException){
				params.responseErrorMsg = GlobalConfig.RESPONSE_TIME_OUT_MSG;
			}else{
				params.responseErrorMsg = e.getMessage();
			}
		} finally {
			if (conn != null) {
				conn.disconnect();
			}
			try {
				if (inputStream != null)
					inputStream.close();
				if (byteArrayOutputStream != null)
					byteArrayOutputStream.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}

		return request;

	}

	/**
	 * 用来执行 网络操作:包括发送数据,以及返回数据后解析数据等
	 */
	@Override
	public Request call() {

		// 发送的请求方式 如果发送的内容为空 则为get请求,否则为post请求
		return request();

	}

}
</span>


Request:  封装协议

<span style="font-size:18px;">package com.niuzhihua.core.net;

import com.niuzhihua.core.interfaces.UIResponseListener;
import com.niuzhihua.core.protocol.Params;
/**
 * 封装一个请求,包括请求数据和回调接口
 * 请求数据:封装请求传递的参数,url,协议字段等。
 * 回调接口:当前请求也有一个属性,就是处理它的接口,这个接口将服务器返回的数据传递到其他组件(UI)
 */
public class Request {

	
	public Params getParams() {
		return params;
	}



	public void setParams(Params p) {
		this.params = p;
	}



	public UIResponseListener getListener() {
		return listener;
	}



	public void setListener(UIResponseListener listener) {
		this.listener = listener;
	}


	/**
	 * 封装请求所传递的数据
	 */
	private Params params;
	/**
	 * 当次请求的回调处理
	 */
	private UIResponseListener listener ;
	
	
	
	public Request(Params p,UIResponseListener listener){
		this.params = p;
		this.listener = listener;
		
	}
	
	
	
	
	
}
</span>

<span style="font-size:18px;">package com.niuzhihua.core.protocol;


import java.net.MalformedURLException;
import java.net.URL;

import com.niuzhihua.core.config.GlobalConfig;
import com.niuzhihua.core.config.L;

/**
 * 封装请求的数据和响应的数据和url等。
 * url:请求地址
 * 请求的数据:发送的字段,可以根据协议规定发送。如json xml 。。。等。
 * 响应的数据:有个object变量存放返回的数据。可以再修改,这里之演示。
 */
public abstract class Params implements DataPackUtil{
	/**
	 * 用来组装协议数据,可以根据具体的业务变化,不一定要用这个类,这里只是模拟.
	 */
	public static StringBuilder packDataTool = new StringBuilder();

	public String getDefaultURL(){
		if(!isNullURL()){
			return url.toString();
		}else{
			return "";
		}
	}
	/**
	 * 判断url是否 为空
	 * @return
	 */
	public boolean isNullURL(){
		if(url!=null && !"".equals(url)){
			return true;
		}else{
			return false;
		}
	}
	
	
	
	private URL url ;
	
	public URL getUrl() {
		return url;
	}

	public void setUrl(URL url) {
		this.url = url;
	}
	/**
	 * 响应码
	 */
	private int responseCode =-1;
	/**
	 * 请求数据
	 */
	private Object requestData ;
	
	
	public int getResponseCode() {
		return responseCode;
	}

	public void setResponseCode(int responseCode) {
		this.responseCode = responseCode;
	}

	public Object getRequestData() {
		return requestData;
	}

	public void setRequestData(Object requestData) {
		this.requestData = requestData;
	}

	public Object getResponseData() {
		return responseData;
	}

	public void setResponseData(Object responseData) {
		this.responseData = responseData;
	}
	/**
	 * 响应数据
	 */
	public Object responseData;
	/**
	 * 响应异常提示
	 */
	public String responseErrorMsg;

	
	public Params(){
		try {
			url = new URL(GlobalConfig.url);
		} catch (MalformedURLException e) {
			e.printStackTrace();
		}
		
		//打包一条协议 准备发送
		packDataTool.append(packBaseDate()).append(packSingleDate());
		setRequestData(packDataTool.toString());
		//情空当前协议数据
		packDataTool.delete(0, packDataTool.length());
	
	}
	
	
	
	/**
	 * 留给每条协议扩展的 用来打包协议数据的方法 
	 * @return
	 */
	protected abstract Object initsingleProtocol();
	
	/**
	 * 初始化 协议的基础数据:协议的共同数据 
	 * @return
	 */
	@Override
	public Object packBaseDate() {
		String basedata = "协议共同的基础数据初始化。。。";
		L.i("协议共同的基础数据初始化。。。");
		return basedata;
	}
	/**
	 * 初始化每条具体协议的数据
	 */
	@Override
	public  Object packSingleDate() {
		return initsingleProtocol();
	}
	/**
	 * 解析网络返回的数据
	 */
	@Override
	public Object unPackDate(byte[] responseData) {
		L.i("解析处理。。。");
		//responseData = parseData();  在这里根据自己的协议格式来解析
		L.i("解析处理完毕。。。");
		return responseData;
	}
	/**
	 * 解析网络返回的数据
	 */
	@Override
	public Object unPackDate(String responseData) {
		L.i("解析处理。。。");
		//responseData = parseData();
		L.i("解析处理完毕。。。");
		return responseData;
	}
	
	
	
}
</span>

<span style="font-size:18px;">package com.niuzhihua.core.protocol;

public interface DataPackUtil {

	
	/**
	 * 打包基础数据
	 * @return
	 */
	public Object  packBaseDate();
	/**
	 * 打包每条具体协议的数据
	 * @return
	 */
	public Object  packSingleDate();
	/**
	 * 解析数据
	 * @return
	 */
	public Object unPackDate(byte[] responseData);
	public Object unPackDate(String responseData);
}
</span>



UIResponseListener: 发送数据后的接收者

<span style="font-size:18px;">package com.niuzhihua.core.interfaces;

import com.niuzhihua.core.protocol.Params;
/**
 * 界面回调接口:一个界面activity实现该接口 ,当做网络请求操作时可以实现界面回调
 * @author Administrator
 *
 */
public interface UIResponseListener {
	
	   /**
	    * 当请求成功响应后回调的方法	
	    * @param theCmd
	    */
	   public void responseExchCallBack(Params params);
	   /**
	    * 当请求响应失败后回调的方法
	    * @param content
	    */
	   public void errorExchCallBack(Params params);
	   
	   /**
	    * 网络异常
	    * @param content
	    */
	  // public void netError(String content);
	   
}</span>

全局设置类:

<span style="font-size:18px;">package com.niuzhihua.core.config;

/**
 * 用来存放全局变量。包括软件配置参数等
 * @author Administrator
 *
 */
public class GlobalConfig {

	/**
	 * 请求后台的地址:客户端向一个地址发送数据,根据发送的数据不同来实现返回的数据不同
	 */
	public static final String url = "http://www.baidu.com/";
//	public static final String url = "http://csdn.com/dd/a.html";
	
	
	/**
	 * 网络请求线程池中线程的数量
	 */
	public static final int THREAD_COUNT_IN_POOL = 3;
	/**
	 * 网络连接超时时间 
	 */
	public static final int CONNECT_TIME_OUT = 15 * 1000;
	/**
	 * 网络数据读取超时时间
	 */
	public static final int READ_TIME_OUT = 10 * 1000;
	
	/**
	 * 响应成功
	 */
	public static final int RESPONSE_OK = 200; 
	/**
	 * 响应错误提示语
	 */
	public static final String RESPONSE_TIME_OUT_MSG = "数据获取超时"; 
	
	public static boolean isDebug = true;
	
}
</span>

工具类:

<span style="font-size:18px;">package com.niuzhihua.core.net;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;

public class StreamTool {
	/**
	 * 读取流中的数据
	 * @param inStream
	 * @return
	 * @throws Exception
	 */
	public static byte[] read(InputStream inStream) throws Exception{
		ByteArrayOutputStream outStream = new ByteArrayOutputStream();
		byte[] buffer = new byte[1024];
		int len = 0;
		while( (len = inStream.read(buffer)) != -1){
			outStream.write(buffer, 0, len);
		}
		inStream.close();
		return outStream.toByteArray();
	}

	public static String bufferRead(InputStream inputStream) throws Exception{
		StringBuilder answer = new StringBuilder();
		 BufferedReader in = new BufferedReader(new InputStreamReader(inputStream));
		    String inputLine;

		    while ((inputLine = in.readLine()) != null) {
		        answer.append(inputLine);
		        answer.append("\n");
		    }
		    in.close();
		    
		return answer.toString();
	}
	
	
	
}
</span>

--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------





0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:56272次
    • 积分:1427
    • 等级:
    • 排名:千里之外
    • 原创:74篇
    • 转载:38篇
    • 译文:5篇
    • 评论:0条