安卓 网络编程
这几天学校实训项目需要开始学习安卓编程,其中有一部分是涉及网络知识,
1 private class SendThread extends Thread 2 { 3 public SendThread(String s) 4 { 5 mURL = s; 6 } 7 public void run() 8 { 9 BasicHttpParams httpParams; 10 httpParams = new BasicHttpParams(); 11 HttpConnectionParams.setConnectionTimeout(httpParams, 3000); 12 HttpConnectionParams.setSoTimeout(httpParams, 3000); 13 // HttpConnectionParams.setStaleCheckingEnabled(httpParams, false); 14 DefaultHttpClient httpClient = new DefaultHttpClient(httpParams); 15 while(true) 16 { 17 String url = mURL; 18 if(mURL == null) 19 continue; 20 HttpPost post = new HttpPost(url); 21 try { 22 HttpResponse response = httpClient.execute(post); 23 Log.i("http","send"); 24 } catch (ClientProtocolException e) { 25 // TODO Auto-generated catch block 26 e.printStackTrace(); 27 } catch (IOException e) { 28 // TODO Auto-generated catch block 29 e.printStackTrace(); 30 } 31 } 32 } 33 }
这是关于向一个服务器使用post方法发送一个请求的线程,在一开始编写时因为粗心的原因将
DefaultHttpClient httpClient = new DefaultHttpClient(httpParams);
这个误写入了while循环体中,结果可想而知,程序不断的新建 httpClient,导致整个CPU和内存的浪费。速度被拖得急慢。
以下是关于从服务器获取视频流的代码
1 public void cameraRec() 2 { 3 new Thread() 4 { 5 public void run() 6 { 7 while(true) 8 {try 9 { 10 URL url =new URL("http://192.168.1.102:1280/snapshot.cgi?user=admin&pwd=123456"); 11 InputStream is = url.openStream(); 12 MainActivity.mBitmap = BitmapFactory.decodeStream(is); 13 Log.i("htp","rec"); 14 if(MainActivity.mBitmap !=null) 15 mHandler.sendEmptyMessage(Base.MESSAGE_PIC); 16 sleep(30); 17 } 18 catch(Exception e) 19 { 20 } 21 } 22 } 23 // @Override 24 // public void run() { 25 // String flag = "Content-Length: "; 26 // String flag1 = "\r\n"; 27 // URL mURL; 28 // ByteArrayOutputStream outStream = new ByteArrayOutputStream(); 29 // 30 // try { 31 // mURL = new URL("http://192.168.1.102:1280" 32 // + "/videostream.cgi" 33 // + "?user=admin&pwd=123456&resolution=32&rate=1"); 34 // 35 // InputStream input = mURL.openStream(); 36 // 37 // //if(input != null) mStopStream = false; 38 // 39 // int readLength = -1; 40 // String strData; 41 // 42 // while (true) { 43 // byte[] buffer = new byte[1024]; 44 // readLength= input.read(buffer,0,1024); 45 // 46 // if(readLength > 0) { 47 // strData= new String (buffer, readLength); 48 // 49 // int index = strData.indexOf(flag); 50 // int index1 = strData.indexOf(flag1,index); 51 // 52 // int streamLength = 0; 53 // 54 // if(index1 != -1) { 55 // streamLength = Integer.parseInt(strData.substring(index+flag.length(), index1)); 56 // } 57 // 58 // if(streamLength > 0) { 59 // if((index1+4) < readLength) { 60 // outStream.write(buffer, index1+4, readLength-index1-4); 61 // streamLength = streamLength - readLength+index1+4; 62 // } 63 // //将剩下读取的视频流存储到buffer1 64 // byte[] buffer1 = new byte[streamLength]; 65 // int length = 0; 66 // while(length < streamLength) { 67 // length += input.read(buffer1,length,streamLength-length); 68 // } 69 // 70 // outStream.write(buffer1,0,streamLength); // 将剩余的stream写入outStream 71 // 72 // byte[] data = outStream.toByteArray(); 73 // MainActivity.mBitmap = BitmapFactory.decodeByteArray(data, 0, data.length); 74 // //System.out.println("bitmap = " + bitmap); 75 // if(MainActivity.mBitmap != null) { 76 // mHandler.sendEmptyMessage(Base.MESSAGE_PIC); //报告到UI界面,更新图像 77 // } 78 // outStream.reset(); 79 // } 80 // } 81 // } 82 // 83 // //input.close(); 84 // 85 // } catch (IOException e) { 86 // e.printStackTrace(); 87 // } 88 // 89 // try { 90 // outStream.close(); 91 // } catch (IOException e) { 92 // e.printStackTrace(); 93 // } 94 // } 95 }.start(); 96 }
*****************************************************************
以下摘自:http://blog.csdn.net/heng615975867/article/details/9012303
(1)采用单例模式(重用HttpClient实例)
对于一个通信单元甚至是整个应用程序,Apache强烈推荐只使用一个HttpClient的实例。例如:
private static HttpClient httpClient = null;
private static synchronized HttpClient getHttpClient() {
if(httpClient == null) {
final HttpParams httpParams = new BasicHttpParams();
httpClient = new DefaultHttpClient(httpParams);
}
return httpClient;
}
(2)保持连接(重用连接)
对于已经和服务端建立了连接的应用来说,再次调用HttpClient进行网络数据传输时,就不必重新建立新连接了,而可以重用已经建立的连接。这样无疑可以减少开销,提升速度。
在这个方面,Apache已经做了“连接管理”,默认情况下,就会尽可能的重用已有连接,因此,不需要客户端程序员做任何配置。只是需要注意,Apache的连接管理并不会主动释放建立的连接,需要程序员在不用的时候手动关闭连接。
(3)多线程安全管理的配置
如果应用程序采用了多线程进行网络访问,则应该使用Apache封装好的线程安全管理类ThreadSafeClientConnManager来进行管理,这样能够更有效且更安全的管理多线程和连接池中的连接。
(在网上也看到有人用MultiThreadedHttpConnectionManager进行线程安全管理的,后查了下Apache的API,发现MultiThreadedHttpConnectionManager是API 2.0中的类,而ThreadSafeClientConnManager是API 4.0中的类,比前者更新,所以选择使用ThreadSafeClientConnManager。另外,还看到有PoolingClientConnectionManager这个类,是API 4.2中的类,比ThreadSafeClientConnManager更新,但Android SDK中找不到该类。所以目前还是选择了ThreadSafeClientConnManager进行管理)
例如:
ClientConnectionManager manager = new ThreadSafeClientConnManager(httpParams, schemeRegistry);
httpClient = new DefaultHttpClient(manager, httpParams);
(4)大量传输数据时,使用“请求流/响应流”的方式
当需要传输大量数据时,不应使用字符串(strings)或者字节数组(byte arrays),因为它们会将数据缓存至内存。当数据过多,尤其在多线程情况下,很容易造成内存溢出(out of memory,OOM)。
而HttpClient能够有效处理“实体流(stream)”。这些“流”不会缓存至内存、而会直接进行数据传输。采用“请求流/响应流”的方式进行传输,可以减少内存占用,降低内存溢出的风险。
例如:
// Get method: getResponseBodyAsStream()
// not use getResponseBody(), or getResponseBodyAsString()
GetMethod httpGet = new GetMethod(url);
InputStream inputStream = httpGet.getResponseBodyAsStream();
// Post method: getResponseBodyAsStream()
PostMethod httpPost = new PostMethod(url);
InputStream inputStream = httpPost.getResponseBodyAsStream();
(5)持续握手(Expect-continue handshake)
在认证系统或其他可能遭到服务器拒绝应答的情况下(如:登陆失败),如果发送整个请求体,则会大大降低效率。此时,可以先发送部分请求(如:只发送请求头)进行试探,如果服务器愿意接收,则继续发送请求体。此项优化主要进行以下配置:
// use expect-continue handshake
HttpProtocolParams.setUseExpectContinue(httpParams, true);
(6)“旧连接”检查(Stale connection check)
HttpClient为了提升性能,默认采用了“重用连接”机制,即在有传输数据需求时,会首先检查连接池中是否有可供重用的连接,如果有,则会重用连接。同时,为了确保该“被重用”的连接确实有效,会在重用之前对其进行有效性检查。这个检查大概会花费15-30毫秒。关闭该检查举措,会稍微提升传输速度,但也可能出现“旧连接”过久而被服务器端关闭、从而出现I/O异常。
关闭旧连接检查的配置为:
// disable stale check
HttpConnectionParams.setStaleCheckingEnabled(httpParams, false);
(7)超时设置
进行超时设置,让连接在超过时间后自动失效,释放占用资源。
// timeout: get connections from connection pool
ConnManagerParams.setTimeout(httpParams, 1000);
// timeout: connect to the server
HttpConnectionParams.setConnectionTimeout(httpParams, DEFAULT_SOCKET_TIMEOUT);
// timeout: transfer data from server
HttpConnectionParams.setSoTimeout(httpParams, DEFAULT_SOCKET_TIMEOUT);
(8)连接数限制
配置每台主机最多连接数和连接池中的最多连接总数,对连接数量进行限制。其中,DEFAULT_HOST_CONNECTIONS和DEFAULT_MAX_CONNECTIONS是由客户端程序员根据需要而设置的。
// set max connections per host
ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(DEFAULT_HOST_CONNECTIONS));
// set max total connections
ConnManagerParams.setMaxTotalConnections(httpParams, DEFAULT_MAX_CONNECTIONS);
经过优化后,上一篇日志中的getHttpClient()方法代码如下:
- private static synchronized HttpClient getHttpClient() {
- if(httpClient == null) {
- final HttpParams httpParams = new BasicHttpParams();
- // timeout: get connections from connection pool
- ConnManagerParams.setTimeout(httpParams, 1000);
- // timeout: connect to the server
- HttpConnectionParams.setConnectionTimeout(httpParams, DEFAULT_SOCKET_TIMEOUT);
- // timeout: transfer data from server
- HttpConnectionParams.setSoTimeout(httpParams, DEFAULT_SOCKET_TIMEOUT);
- // set max connections per host
- ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(DEFAULT_HOST_CONNECTIONS));
- // set max total connections
- ConnManagerParams.setMaxTotalConnections(httpParams, DEFAULT_MAX_CONNECTIONS);
- // use expect-continue handshake
- HttpProtocolParams.setUseExpectContinue(httpParams, true);
- // disable stale check
- HttpConnectionParams.setStaleCheckingEnabled(httpParams, false);
- HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
- HttpProtocolParams.setContentCharset(httpParams, HTTP.UTF_8);
- HttpClientParams.setRedirecting(httpParams, false);
- // set user agent
- String userAgent = "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2) Gecko/20100115 Firefox/3.6";
- HttpProtocolParams.setUserAgent(httpParams, userAgent);
- // disable Nagle algorithm
- HttpConnectionParams.setTcpNoDelay(httpParams, true);
- HttpConnectionParams.setSocketBufferSize(httpParams, DEFAULT_SOCKET_BUFFER_SIZE);
- // scheme: http and https
- SchemeRegistry schemeRegistry = new SchemeRegistry();
- schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), 80));
- schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), 443));
- ClientConnectionManager manager = new ThreadSafeClientConnManager(httpParams, schemeRegistry);
- httpClient = new DefaultHttpClient(manager, httpParams);
- }
- return httpClient;
- }
附录:关于HttpURLConnection的优化,网上资料不多。从Android官网上看到一点,整理如下:
(1)上传数据至服务器时(即:向服务器发送请求),如果知道上传数据的大小,应该显式使用setFixedLengthStreamingMode(int)来设置上传数据的精确值;如果不知道上传数据的大小,则应使用setChunkedStreamingMode(int)——通常使用默认值“0”作为实际参数传入。如果两个函数都未设置,则系统会强制将“请求体”中的所有内容都缓存至内存中(在通过网络进行传输之前),这样会浪费“堆”内存(甚至可能耗尽),并加重隐患。
(2)如果通过流(stream)输入或输出少量数据,则需要使用带缓冲区的流(如BufferedInputStream);大量读取或输出数据时,可忽略缓冲流(不使用缓冲流会增加磁盘I/O,默认的流操作是直接进行磁盘I/O的);
(3)当需要传输(输入或输出)大量数据时,使用“流”来限制内存中的数据量——即:将数据直接放在“流”中,而不是存储在字节数组或字符串中(这些都存储在内存中)。
参考文章:
http://hc.apache.org/httpclient-3.x/performance.html
http://blog.csdn.net/androidzhaoxiaogang/article/details/8198400
http://guowww.diandian.com/post/2011-11-07/15351973
http://blog.csdn.net/ken831001/article/details/7925309
http://www.iteye.com/topic/1117362