这篇写自己在进行Android学习的时候是如何进行网络请求的。
在进行开发的时候,无论如何都是绕不开网络请求的。我们一般都是Android - 服务器 - 数据库,然后再接收返回的数据。这一套流程走下来就是一个Android的请求。下面就开始讲如何实现这个请求。
在讲这个之前,首先说一个Android开发者必须知道的知识:Android的主线程不要做耗时操作,否则会发生ANR(Application Not Responding)。而网络请求就是一个耗时操作。
因此,针对于Android的网络请求,我们都是开辟一个新的线程进行,如下:
new Thread(new Runnable() {@Overridepublic void run() {HttpClient client = new DefaultHttpClient();HttpPost post = new HttpPost(url);try{UrlEncodedFormEntity reqEntity = new UrlEncodedFormEntity(params, "utf-8");post.setEntity(reqEntity);HttpResponse httpResponse = client.execute(post);if(httpResponse.getStatusLine().getStatusCode() == HttpURLConnection.HTTP_OK) {HttpEntity entity = httpResponse.getEntity(); // 取出报文的具体内容String response = EntityUtils.toString(entity, "utf-8"); // 报文编码listener.tackAction(response);}}catch (IOException ioe){ioe.printStackTrace();}}}).start();
那么,我们怎么去更改UI呢?Android的UI都是由主线程进行控制的,因此,子线程的网络请求结果应该反馈给主线程。
现在,注意这行代码:listener.tackAction(response);
这个很重要,这是一个监听器,它在我们访问网络请求成功后,执行一个方法,该方法的参数就是返回结果,那么我们就可以利用这个方法进行修改UI(这其实是一种设计模式,读者可以去查查属于哪种设计模式。)
这里说错了,这里不能直接更改UI,因为这里仍属于子线程,他是子线程中执行的方法,所以不是主线程,我们只是能够通过回调机制获取网络请求的数据,那么如果要更改UI,则需要主线程,因此我们需要将数据给主线程,这个一般都是通过Handler。代码大概如下:
private Handler handler = new Handler(new Handler.Callback() {@Overridepublic boolean handleMessage(@NonNull Message message) {//让主线程进行UI修改,这个handler可以设置为全局量。return true;}});)
然后在回调方法中进行使用handler:
下面的doAction是我自己某个程序中的回调方法。
@Overridepublic void doAction(ReceiveBean receive) {String from = receive.getFrom();String message = receive.getMessage();Log.i("ChatActivity", "doAction: "+message);ViewBean viewBean = new ViewBean(message, from, HTalk.RECEIVE);arrayList.add(viewBean);Message message = new Message();handler.sendMessage(message);}
可以注意这些语句。这是调用前面的handler,将消息传递给主线程,让主线程进行一些操作。message也可以存储一些数据。
Message message1 = new Message();
handler.sendMessage(message);
因此,整个网络请求的流程就出来了。
主线程传一个监听接口(观察者)给子线程,
然后activity实现这个接口的方法,
子线程进行网络请求,
网络请求成功之和,接口就会执行实现的方法。
然后在回调方法中通过Handler将数据传递给主线程,让主线程完成一些操作
这是一种最基本的访问网络请求的方式。我经常用这种方式,因为感觉这种方式还可以,不算很麻烦,还能实现目的。
这种网络请求是短连接方式,长连接方式比较复杂,但是也是必须的。可惜我没实现。为什么说是必须的呢?因为,有时候我们需要服务器“主动”发过来的消息,比如更新数据。如果,我把长连接写好了我就来更新(估计大概率会使用轮询实现)。
轮询实现(十分简易的轮询):
就是在每次进行网络申请是,利用sleep进行休眠,然后一直while循环。知道连接失败跳出。
这种轮询是十分暴力的。似乎有优化的方法但是我不知道。
@Overridepublic void run() {try{final URL url = new URL("your host ip");while(true){try {Thread.sleep(3000);HttpURLConnection connection = (HttpURLConnection)url.openConnection();connection.setConnectTimeout(5 * 1000);connection.setReadTimeout(5 * 1000);connection.setRequestMethod("POST");connection.connect();if (connection.getResponseCode() == 200) {String data = StreamUtils.getData(connection.getInputStream());mListener.doAction(data);}else {break;}Log.i("LoopRunnable", "run: 1");connection.disconnect();}catch (InterruptedException ie){ie.printStackTrace();}}}catch (IOException ioe){ioe.printStackTrace();}}
我上次好像不知道从哪看到,现在好像Android的网络请求一般都用HttpURLConnection,很少用HttpClient。我下次再去查查是不是对的,我也不太确定。
最近使用了一次HttpURLConnection 进行网络请求。有几个错误,写下来记录一下。
首先HttpURLConnection的使用步骤:
1、创建一个URL对象,如下:
URL url = new URL(serverHost);
我自己写的时候发生了一个连接错误。首先,serverHost可以是域名,比如www.baidu.com,也可以是IP加端口号,但是如果是IP:port的形式,必须注明以什么传输协议进行连接,也就是说应该在你的IP前面加上http://或者https://。
2、就可以进行一些基本的HttpURLConnection设置。如下:(不是都需要设置)
HttpURLConnection connection = (HttpURLConnection)url.openConnection();// 设置连接超时时间connection.setConnectTimeout(5 * 1000);//设置从主机读取数据超时connection.setReadTimeout(5 * 1000);// Post请求必须设置允许输出 默认false/connection.setDoOutput(true);//设置请求允许输入 默认是trueconnection.setDoInput(true);// Post请求不能使用缓存connection.setUseCaches(false);// 设置为Post请求connection.setRequestMethod("POST");//设置本次连接是否自动处理重定向connection.setInstanceFollowRedirects(true);// 配置请求Content-Typeconnection.setRequestProperty("Content-Type", "application/json");// 开始连接connection.connect();
3、上面的结束之后就可以进行真正的网络请求,如下:
// 发送请求参数DataOutputStream dos = new DataOutputStream(connection.getOutputStream());dos.write(bytes);dos.flush();dos.close();
用过httpClient的都知道,我们习惯的参数是K-V的形式,但是这里传输的是bytes。其实bytes就是K-V转化来的,他的过程是这样的:
先将K-V转换成 K1 = V1 & K2 = V2。。。的形式(就是get的形式)
然后将这个字符串转换成bytes。
这里需要注意:1)转换过程中的编码(需要统一,我设置的是UTF-8),2)在servlet中,request进行解码时,需要加上下面这行,设置request的解析编码,和你之前的统一就行
try{request.setCharacterEncoding("UTF-8");}catch (UnsupportedEncodingException uee){System.out.println("encoding error");uee.printStackTrace();}