Android服务器通信的几种方式详解

   来公司上班这么久,一直做的就是通信这块 - -! 好长时间没写博客了,现在把Android通信这块总结下,我会尽可能详细的讲解所有通信技能。
   先列出通信的几种方式:
  (1)webservice调用

   一、Android通过WebService通信
    这也是我们公司Android项目通信的架构,并且封装成一个模块了,具体来说就是:使用HttpURLConnection进行HTTP通信,采用xml数据格式交互。那么问题来了,HttpURLConnection+xml算是WebService通信吗?首先得知道什么是WebService。
   WebService是一种跨编程语言和跨操作系统平台的远程调用技术。也就是一个应用程序向外界暴露出一个能通过Web进行调用的API,也就是说能用编程的方法通过Web来调用这个应用程序。XML+XSD,SOAP和WSDL就是构成WebService平台的三大技术。WebService采用http协议传输数据,采用xml封装数据。
  XML+XSD:解决了数据的封装和数据类型格式的定义。
  SOAP:SOAP协议 = HTTP协议 + XML数据格式。
            WebService通过HTTP协议发送请求和接收结果时,发送的请求内容和结果内容都采用XML格式封装,并增加了一些特定的HTTP消息头,以说明HTTP消息的内容格式,这些特定的HTTP消息头和XML内容格式就是SOAP协议。SOAP提供了标准的RPC方法来调用Web Service。
   上面说到,WebService采用HTTP协议传输数据,那么在Android如何实现HTTP传输呢,通常用的方式有两种:使用Java标准类库的HttpURLConnection和使用Apache的HttpClient接口。
   1、使用HttpURLConnection(官方推荐使用此种方式)
       HttpURLConnection的使用很简单,就是使用URL url = new URL("your service ur"),对象来打开一个Hppt连接(HttpURLConnection对象),即new HttpURLConnection() = url.openConnection();.然后使用该对象设置一系列参数,比如请求方法是get还是post,缓存等等。但是为了支持众多请求参数,以及文件传输,做一个比较好的封装是一个问题,如下:

     * 使用HttpUrlCollection进行Http通信,支持文件传输、post和get提交方式
     * @param url  服务地址
     * @param paras  参数列表
     * @param files  文件列表
     * @param authenticated    
     * @param httpMethod  请求方式
     * @return        返回InputStream输入流
     * @throws IOException
     */
    public InputStream httpRequest(String url,
            List<BasicNameValuePair> paras, ArrayList<File> files,
            boolean authenticated, String httpMethod) throws IOException {
        String end = "\r\n";
        String twoHyphens = "--";
        String boundary = "*****";
        HttpURLConnection con = null;
        DataOutputStream ds = null;
        InputStream is = null;
        int responseCode = -1;
        String error = "";
        try {

            if (httpMethod.equals("GET") && null != paras) {
                if (!url.contains("?")){
                    url += "?";
                }
                for (BasicNameValuePair kv : paras) {
                    url += ("&" + kv.key + "=" + kv.value);
                }                
            }
            
            URL uri = new URL(url);
            con = (HttpURLConnection) uri.openConnection();
            if (this.timeout  > 0) {
                con.setConnectTimeout(this.timeout);
            } else {
                con.setConnectTimeout(30000);
            }
            // con.setReadTimeout(60000);
            /* 允许Input、Output,不使用Cache */
            con.setDoInput(true);
            con.setDoOutput(true);
            con.setUseCaches(false);
            /* setRequestProperty */
            con.setRequestProperty("Connection", "Keep-Alive");
            con.setRequestProperty("Charset", "UTF-8");

            if (httpMethod.equals("POST")) {
                /* 设定传送的method=POST */
                con.setRequestMethod("POST");
                con.setRequestProperty("Content-Type",
                        "multipart/form-data;boundary=" + boundary);
                /* 设定DataOutputStream */
                ds = new DataOutputStream(con.getOutputStream());

                /* 添加键值 */
                if (null != paras) {
                    for (BasicNameValuePair kv : paras) {
                        StringBuilder sb = new StringBuilder();
                        sb.append(end + twoHyphens + boundary + end);
                        sb.append("Content-Disposition: form-data; "
                                + "name=\"" + kv.key + "\"" + end + end);
                        sb.append((null==kv.value)?"":kv.value);
                        // sb.append(twoHyphens + boundary);
                        byte[] bytes = sb.toString().getBytes("utf8");
                        ds.write(bytes);
                    }
                    ds.write((end + twoHyphens + boundary + end)
                            .getBytes("utf8"));
                }
                /* 发送文件 */
                if (null != files) {
                    for (File file : files) {
                        ds.writeBytes(twoHyphens + boundary + end);
                        ds.writeBytes("Content-Disposition: form-data; "
                                + "name=\"" + file.getName() + "\";filename=\""
                                + file.getName() + "\"" + end + end);

                        FileInputStream fStream = new FileInputStream(file);
                        String fileName = file.getName();
                        int bufferSize = 1024;
                        byte[] buffer = new byte[bufferSize];

                        int length = -1;
                        long total = 0;
                        long fileLength = file.length();
                        while ((length = fStream.read(buffer)) != -1) {
                            ds.write(buffer, 0, length);
                            total += length;
                        }
                        ds.write((end + twoHyphens + boundary + end)
                                .getBytes("utf8"));
                        fStream.close();
                    }
                    /* 所有文件上传完成后加上这一行 */
                    ds.writeBytes(twoHyphens + boundary + twoHyphens + end);
                    ds.flush();
                }
            } else if (httpMethod.equals("GET")) {
                //设置为get请求
                con.setRequestMethod("GET");
            }

            responseCode = con.getResponseCode();
            /* 取得Response内容 */
            is = con.getInputStream();

        } catch (MalformedURLException e) {
            responseCode = -2;
            error = e.getMessage();
            e.printStackTrace();
        } catch (IOException e) {
            responseCode = -3;
            error = e.getMessage();
            e.printStackTrace();
        } catch (Exception e) {
            responseCode = -4;
            error = e.getMessage();
            e.printStackTrace();
        } finally {
            if (null != ds) {
                ds.close();
                ds = null;
            }
        }
        return is;
    }

   通过上面的Http连接就可以进行客户端服务器数据之间的传输,当从服务器请求获取数据时,返回的InputStream流,如果采用xml封装数据,下面以Pull解析为例,将InputStream流解析成ContentValues对象:

  

/**
	 * 读取关键词下的节点数据, 以键值对的形式返回
	 * @param is Xml数据流
	 * @param key节点名
	 * @return 键值对
	 */
	public static ContentValues getAsContentValues(InputStream is, String key ){
		try{
			is.reset();
			}catch(Exception e){
				e.printStackTrace();
			}
		
		ContentValues ret = null;;
		
		XmlPullParserFactory factory;
		try {
			factory = XmlPullParserFactory.newInstance();
                        //生成一个Pull解析器
                        XmlPullParser parser = factory.newPullParser();
			parser.setInput(is, "utf-8");
                        // 解析器和xml节点值类解析xml
                        ret = fromXmlContentValues(parser, key);
		} catch (XmlPullParserException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		if (null == ret){
			ret = new ContentValues();
		}		
		return ret;
	}
  

private static ContentValues fromXmlContentValues(XmlPullParser parser, String key) throws XmlPullParserException, IOException{
        ContentValues ret = null;

        Boolean keyTag = false;//是否进入key节点
        Boolean innerTag = false;//是否进入key的子节点
        String subKey = "";    //当前key的子节点名称        
        
        int eventType = parser.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
            String keyName = parser.getName();
            
            if (eventType == XmlPullParser.END_TAG){
                if (keyName.contentEquals(key) && !innerTag){
                    parser.next();
                    break;//退出key节点
                }else if (keyName.contentEquals(subKey)){
                    innerTag = false;
                }
            }
            
            if (eventType == XmlPullParser.START_TAG) {
                if (keyName.contentEquals(key) && !keyTag){
                    keyTag = true;
                }else if (keyTag){
                    //开始读取key的子节点数据
                    eventType = parser.next();
                    if (!innerTag){
                        if (eventType == XmlPullParser.TEXT ||
                            eventType == XmlPullParser.CDSECT ){
                            if (null == ret){
                                ret = new ContentValues();
                            }
                            String value = parser.getText();
                            ret.put(keyName, value);
                        }
                        subKey = keyName;
                        innerTag = true;//进入key的子节点
                        if (eventType == XmlPullParser.END_TAG){//刚进入就退出的子节点, 说明值为空
                            if (null == ret){
                                ret = new ContentValues();
                            }
                            String value = "";
                            ret.put(keyName, value);
                            innerTag = false;////退出key的子节点
                        }
                    }        
                }                    
            }
            eventType = parser.next();
        }
        
        return ret;
    }
    



   2、使用HttpClient通信(在2.2之后官方就不推荐使用这种方式了,使用HttpURLConnection)

     HttpClient通信就更简单了,直接上代码

    

/**
	 * 根据给定的url地址访问网络,得到响应内容(这里为GET方式访问)
	 * 
	 * @param url
	 *            指定天气url地址
	 *            
	 * @param params 请求参数
	 * @return 返回结果数据,返回失败时是null
	 */
	public String getWeatherFromServe(String url,List<String> parameters) {
		// 创建一个http请求对象
		HttpGet request = new HttpGet(url);
		// 创建HttpParams以用来设置HTTP参数
		HttpParams params = new BasicHttpParams();
		//设置布尔参数,
//		params.setBooleanParameter("boolean", false);
		//序列化对象参数
//		params.setParameter("string", "dsa");
		// 设置连接超时或响应超时
		HttpConnectionParams.setConnectionTimeout(params, 3000);
		HttpConnectionParams.setSoTimeout(params, 5000);
		// 创建一个网络访问处理对象
		HttpClient httpClient = new DefaultHttpClient(params);
		try {
			// 执行请求参数项
			HttpResponse response = httpClient.execute(request);
			// 判断是否请求成功
			if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
				HttpEntity entity = response.getEntity();
				// 获得响应信息
				String content = EntityUtils.toString(response.getEntity());
				return content;
			} else {
				// 网连接失败,使用Toast显示提示信息
				Toast.makeText(mContext, "网络访问失败,请检查手机网络状态!",
						Toast.LENGTH_LONG).show();
			}

		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			// 释放网络连接资源
			httpClient.getConnectionManager().shutdown();
		}
		return null;
	}
    上面的例子中使用的是HttpGet请求方式,也可以使用HttpPost请求。服务器响应的HttpResponse对象获取一个HTTPEntity对象通过getEntity()方法,通过HttpEntity对象就可以获取服务器返回的众多信息。

 

    3、Socket通信

    Socket是建立在TCP/IP协议的基础上,就是对TCp/IP的一种封装,Socket提供接口我们使用。关于Socket的文章恐怕一篇两篇也讲不清楚,先放着了,有时间了另开文章详细讲解。



你可能感兴趣的:(Android服务器通信的几种方式详解)