基于HttpURLConnection的网络访问工具

出于完善我个人的理论了解面,再加上google在6.0之后取消了HttpClient,所以我特地抽了点时间针对HttpUrlConnection做了一点连接,并写了个工具类,方便以后可以用上,那么话不多说,直接上代码,我再代码中注释已经做得很详细了


基于对于代码结构的我个人的结构优化思维,所以并不是直接将下载的代码写好,而是搭建了一下大概的结构,大致来说分为了   模型层、数据层、控制层


首先是模型层

/**
 * 用来存放网络返回数据的类
 * @author yjh
 *
 */
public class UrlResponseBean {
	//id
	private String mId;
	//响应结果:成功、网络出错、服务器出错
	private int mResponseResult;
	//具体的响应返回码
	private int mResponseCode;
	//响应成功后返回的内容,这里返回流
//	private InputStream mIs;
	//获取网的网络数据,已经转化为输出流了
	private ByteArrayOutputStream mBaos;
	
	//响应成功后返回的数据
	private String mResponseData;
	
	private HttpURLConnection mConnectBean;
	
	public int getResponseResult() {
		return mResponseResult;
	}
	public void setResponseResult(int responseResult) {
		this.mResponseResult = responseResult;
	}
	public int getResponseCode() {
		return mResponseCode;
	}
	public void setResponseCode(int responseCode) {
		this.mResponseCode = responseCode;
	}
	public String getId() {
		return mId;
	}
	public void setId(String id) {
		this.mId = id;
	}
	public String getResponseData() {
		return mResponseData;
	}
	public void setResponseData(String responseData) {
		this.mResponseData = responseData;
	}
	protected HttpURLConnection getConnectBean() {
		return mConnectBean;
	}
	protected void setConnectBean(HttpURLConnection connectBean) {
		this.mConnectBean = connectBean;
	}
	public ByteArrayOutputStream getBaos() {
		return mBaos;
	}
	public void setBaos(ByteArrayOutputStream mBaos) {
		this.mBaos = mBaos;
	}
	
	
}
然后是数据层:

public class UrlConnectTool {
	
	private static final int TIMEOUT_IN_MILLIONS = 5000;
	
	
	/**
	 * 在2.3以上,想要卡主进程运行访问网络时,需要设置这个
	 */
	@SuppressLint("NewApi")
	public void downloadInit(){
		StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()  
        .detectDiskReads()  
        .detectDiskWrites()  
        .detectNetwork()  
        .penaltyLog()  
        .build());   
		StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()  
        .detectLeakedSqlLiteObjects()  
        .detectLeakedClosableObjects()  
        .penaltyLog()  
        .penaltyDeath()  
        .build());  
	}
	
	/**
	 * 卡主线程的get下载方式
	 * 这里返回的bean里面是下载结果以及下载内容
	 */
	public UrlResponseBean doGet(String urlStr) {
		UrlResponseBean bean = new UrlResponseBean();
		URL url = null;
		HttpURLConnection conn = null;
		try {
			url = new URL(urlStr);
			conn = (HttpURLConnection) url.openConnection();
			//这里在写的时候我就一直想着在设置好超时之后,当超时时是怎么个反馈方式,网上大都是设置完了就不管了,也没有说超时
			//时是怎么个响应方式,我好不容易看到个说是会在抛出的错误里面返回的,但是也需要验证,但我没有可以用于测试
			//超时的网址!需要测试!
			conn.setReadTimeout(TIMEOUT_IN_MILLIONS);
			conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);
			conn.setRequestMethod("GET");
			conn.setRequestProperty("accept", "*/*");
			conn.setRequestProperty("connection", "Keep-Alive");
			conn.setRequestProperty("Charset", "UTF-8");//设置编码格式
			int connectCode = conn.getResponseCode();
			//这里获取之后,如果需要统计,则可以在这里统计连接之后的错误信息码
			bean.setResponseCode(connectCode);
			bean.setConnectBean(conn);
			//在这里,对于控制层来说,只需要知道是成功,还是网络不好(让用户调整网络状态),或者是连接失败(让用户刷新)
			switch ((connectCode/100)) {
			case 2://表示成功
				bean.setResponseResult(UrlResponseConsts.DOWNLOAD_SUCCESS);
				bean.setBaos(inputToByteArrayOutputStream(conn.getInputStream()));
//				bean.setIs(conn.getInputStream());
				return bean;
			case 4://表示访问网址出错
				bean.setResponseResult(UrlResponseConsts.SERVER_ERROR);
				return bean;
			case 5://表示服务器出错
				bean.setResponseResult(UrlResponseConsts.SERVER_ERROR);
				return bean;
			}
		} catch (ProtocolException e) {
//			如果没有建立连接并执行getInputStream
//			在调用getResponseCode的时候,该函数会自动执行getInputStream并获取Response Code
//			所以你在此函数后再设置Request Type就会造成Connection already established错误。
			e.printStackTrace();
			bean.setResponseResult(UrlResponseConsts.NET_ERROR);
			return bean;
		}
		//这个抛出的错我是针对连接超时时做的处理
		catch (SocketTimeoutException e) {
			//连接超时,Socket读数据的超时,https://twitter.com/	我用的推特来测试出来了
			e.printStackTrace();
			bean.setResponseResult(UrlResponseConsts.NET_ERROR);
			return bean;
		}
		//这个抛出的错我是针对连接超时时做的处理
		catch (ConnectTimeoutException e) {
			//响应超时,通过网络与服务器建立连接的超时
			e.printStackTrace();
			bean.setResponseResult(UrlResponseConsts.NET_ERROR);
			return bean;
		}
		catch (IOException e) {
			//错误1:连接失败,没有网络	在我用推特主页测试的时候,此时我关闭了网络,这里的报错为java.net.UnknownHostException
			e.printStackTrace();
			bean.setResponseResult(UrlResponseConsts.NET_ERROR);
			return bean;
		}
		finally {
			try {
				if (conn.getInputStream() != null)
					conn.getInputStream().close();
			} catch (IOException e) {
			}
			conn.disconnect();
		}
		//此时是既不是成功也不是失败,应该属于服务器问题
		bean.setResponseResult(UrlResponseConsts.SERVER_ERROR);
		return bean;
	}
	
	/**
	 * 采用post方式访问网络
	 */
	public UrlResponseBean doPost(String urlStr,String param){
		PrintWriter out = null;  
//        BufferedReader in = null;  
        UrlResponseBean bean = new UrlResponseBean();
        HttpURLConnection conn = null;
        try  
        {  
            URL realUrl = new URL(urlStr);  
            // 打开和URL之间的连接  
            conn = (HttpURLConnection) realUrl  
                    .openConnection();  
            // 设置通用的请求属性  
            conn.setRequestProperty("accept", "*/*");  
            conn.setRequestProperty("connection", "Keep-Alive");  
            conn.setRequestMethod("POST");  
            //设置文件类型
            // application/x-www-form-urlencoded: 窗体数据被编码为名称/值对。这是标准的编码格式。
            //multipart/form-data: 窗体数据被编码为一条消息,页上的每个控件对应消息中的一个部分。
            //text/plain: 窗体数据以纯文本形式进行编码,其中不含任何控件或格式字符。
            
            //form的enctype属性为编码方式,常用有两种:application/x-www-form-urlencoded和
            //multipart/form-data,默认为application/x-www-form-urlencoded。 
            //当action为get时候,浏览器用x-www-form-urlencoded的编码方式把form数据转换成一个字串(name1=value1&name2=value2...),
            //然后把这个字串append到url后面,用?分割,加载这个新的url。 当action为post时候,浏览器把form数据封装到http body中,然后发送到server。
            //如果没有type=file的控件,用默认的application/x-www-form-urlencoded就可以了。 但是如果有type=file的话,就要用到multipart/form-data了。
            //浏览器会把整个表单以控件为单位分割,并为每个部分加上Content-Disposition(form-data或者file),Content-Type(默认为text/plain),name(控件name)等信息,并加上分割符(boundary)。
            conn.setRequestProperty("Content-Type",  
                    "application/x-www-form-urlencoded");  
            conn.setRequestProperty("charset", "utf-8"); 
            //设置不使用缓存
            conn.setUseCaches(false);  
            // 发送POST请求必须设置如下两行  
            //设置容许输出,设置了之后才能用post
            conn.setDoOutput(true);  
         // 设置是否从httpUrlConnection读入,默认情况下是true;  
            conn.setDoInput(true);  
            conn.setReadTimeout(TIMEOUT_IN_MILLIONS);  
            conn.setConnectTimeout(TIMEOUT_IN_MILLIONS);  
  
            if (param != null && !param.trim().equals(""))  
            {  
                // 获取URLConnection对象对应的输出流  
                out = new PrintWriter(conn.getOutputStream());  
                // 发送请求参数  
                out.print(param);  
                // flush输出流的缓冲  
                out.flush();  
            }  
            int connectCode = conn.getResponseCode();
			//这里获取之后,如果需要统计,则可以在这里统计连接之后的错误信息码
			bean.setResponseCode(connectCode);
			bean.setConnectBean(conn);
			//在这里,对于控制层来说,只需要知道是成功,还是网络不好(让用户调整网络状态),或者是连接失败(让用户刷新)
			switch ((connectCode/100)) {
			case 2://表示成功
				bean.setResponseResult(UrlResponseConsts.DOWNLOAD_SUCCESS);
				bean.setBaos(inputToByteArrayOutputStream(conn.getInputStream()));
//				bean.setIs(conn.getInputStream());
				return bean;
			case 4://表示访问网址出错
				bean.setResponseResult(UrlResponseConsts.SERVER_ERROR);
				return bean;
			case 5://表示服务器出错
				bean.setResponseResult(UrlResponseConsts.SERVER_ERROR);
				return bean;
			}
        } catch (ProtocolException e) {
//			如果没有建立连接并执行getInputStream
//			在调用getResponseCode的时候,该函数会自动执行getInputStream并获取Response Code
//			所以你在此函数后再设置Request Type就会造成Connection already established错误。
			e.printStackTrace();
			bean.setResponseResult(UrlResponseConsts.NET_ERROR);
			return bean;
		}
		//这个抛出的错我是针对连接超时时做的处理
		catch (SocketTimeoutException e) {
			//连接超时,Socket读数据的超时,https://twitter.com/	我用的推特来测试出来了
			e.printStackTrace();
			bean.setResponseResult(UrlResponseConsts.NET_ERROR);
			return bean;
		}
		//这个抛出的错我是针对连接超时时做的处理
		catch (ConnectTimeoutException e) {
			//响应超时,通过网络与服务器建立连接的超时
			e.printStackTrace();
			bean.setResponseResult(UrlResponseConsts.NET_ERROR);
			return bean;
		}
		catch (IOException e) {
			//错误1:连接失败,没有网络	在我用推特主页测试的时候,此时我关闭了网络,这里的报错为java.net.UnknownHostException
			e.printStackTrace();
			bean.setResponseResult(UrlResponseConsts.NET_ERROR);
			return bean;
		} 
        // 使用finally块来关闭输出流、输入流  
        finally  
        {  
                try {
                	if (out != null)  
                    {  
                        out.close();  
                    }
    				if (conn.getInputStream() != null)
    					conn.getInputStream().close();
    			} catch (IOException e) {
    			}
                if(null != conn)
                	conn.disconnect();
        }  
        //此时是既不是成功也不是失败,应该属于服务器问题
      	bean.setResponseResult(UrlResponseConsts.SERVER_ERROR);
        return bean;
	}
	
	private ByteArrayOutputStream inputToByteArrayOutputStream(InputStream is) throws IOException{
		if (null != is) {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			int len = -1;
			byte[] buf = new byte[128];
			while ((len = is.read(buf)) != -1) {
				baos.write(buf, 0, len);
			}
			baos.flush();
			return baos;
		}
		return null;
	}
	
}


目前这个类还没有完善,还差一个下载文件,我会在近期补充完整


下一个是控制层:

public class UrlConnectCenter {

	private UrlConnectTool mConnectTool;
	private DataKeepTool mDataTool;

	public UrlConnectCenter() {
		mConnectTool = new UrlConnectTool();
		mDataTool = new DataKeepTool();
	}

	/**
	 * 卡滞主线程的网络连接,这里处理好post和get方式以及返回来的结果,过滤下载结果 这里是UrlConnectTool之后的第二层处理
	 */
	public UrlResponseBean startConnectUi(UrlConnectDescription description) {
		systemAdjustment();
		UrlResponseBean response = new UrlResponseBean();
		// 这里获取到访问后的结果
		if (!description.getIsPost()) {// get方式
			String connectUrl = description.getConnectUrl();
			// get方式时拼起来
			if(null != description.getConnectData())
				connectUrl +="?"
					+ description.getConnectData();
			response = mConnectTool.doGet(connectUrl);
		} else {// post方式
			response = mConnectTool.doPost(description.getConnectUrl(), description.getConnectData());
		}
		// 设置下载的id
		response.setId(description.getId());
		// 下载成功时
		if (response.getResponseResult() == UrlResponseConsts.DOWNLOAD_SUCCESS) {
			// 在这一层处理好数据,是作为数据返回还是文件存储等
			try {
				if (null == description.getFilePath()) {// 将流转化为字符
					String backInfo = mDataTool.getDataBackInfo(response.getBaos());
					response.setResponseData(backInfo);
				} else {
					// 将流转化为文件并保存
					mDataTool.getDataSaveFile(response.getBaos(),
							description.getFilePath());
				}
			} catch (IOException e) {
				//这里报错就是流转化时的流报错
				e.printStackTrace();
			}
			// 用完之后将流关闭
			try {
				response.getBaos().close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return response;
	}
	
	/**
	 * 在2.3之前可以直接在主进程之中卡主线程下载
	 * 但是2.3之后需要设置过后才能在主线程下载
	 */
	private void systemAdjustment(){
		if (android.os.Build.VERSION.SDK_INT > 9) {
			mConnectTool.downloadInit();
		}
	}
	
}


控制层中也有一个bean,用于存放任务的描述

/**
 * 网络连接描述,用于需要发起网络连接时填充所需数据之后使用
 * @author yjh
 *
 */
public class UrlConnectDescription {
	//id
	private String mId;
	//需要访问的网址
	private String mConnectUrl;
	//是否是post方式
	private boolean mIsPost = false;
	//文件下载的路径,当需要下载为文件时
	private String mFilePath;
	//网址之后需要添加的参数,这里单独传入,以便于内部处理post或者get
	private String mConnectData;
	
	
	public String getId() {
		return mId;
	}
	public void setId(String mId) {
		this.mId = mId;
	}
	public String getConnectUrl() {
		return mConnectUrl;
	}
	public void setConnectUrl(String mConnectUrl) {
		this.mConnectUrl = mConnectUrl;
	}
	public boolean getIsPost() {
		return mIsPost;
	}
	public void setIsPost(boolean mIsPost) {
		this.mIsPost = mIsPost;
	}
	public String getFilePath() {
		return mFilePath;
	}
	public void setFilePath(String mFilePath) {
		this.mFilePath = mFilePath;
	}
	public String getConnectData() {
		return mConnectData;
	}
	public void setConnectData(String mConnectData) {
		this.mConnectData = mConnectData;
	}
	
	
	
}

然后有一个工具类,用于转换流成为String 内容以及保存为文件

/**
 * 处理数据
 * 
 * @author yjh
 * 
 */
public class DataKeepTool {

	/**
	 * 将访问后返回的数据转化为String类型并返回
	 * 
	 * @param conn
	 * @param is
	 * @param baos
	 * @return
	 * @throws IOException
	 */
	public String getDataBackInfo(ByteArrayOutputStream boas) throws IOException {
		if (null != boas) {
			//采用这种方式防止出现乱码
			byte[] lens = boas.toByteArray();
			return new String(lens);
		}
		return null;
	}

	/**
	 * 用于将流保存为文件
	 * 
	 * @param is
	 */
	public void getDataSaveFile(ByteArrayOutputStream baos, String filePath) {
		FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(new File(filePath));
            baos.writeTo(fos);
            fos.close();
            baos.flush();
            baos.close();
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }  finally {
 
        }
	}

}




代码到这边基本就够用了,还有一个的话是常亮类,这个类内容很简单,就三个访问状态的常量而已


由于在代码中注释已经写得很多了,我再这里就不再做多的描述了


这里还差的最后一个文件上传的话我再找时间补充完整





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值