編輯:關於Android編程
先對上一遍的工具類,補充兩點:
1、Client關閉異常
如果沒有連接host就調用close()的話,會導致NullPointException,因為mInputStream為null。雖然socket關閉後,輸入輸出流也會隨之關閉,但為了加快回收速度,建議把流也關閉。
public void close() { if (mSocket != null) { try { mInputStream.close(); mOutputStream.close(); mSocket.close(); mInputStream = null; mOutputStream = null; mSocket = null; } catch (IOException e) { e.printStackTrace(); } } }
修改為:
public void close() { if (mInputStream != null) { try { mInputStream.close(); // mInputStream輸入流不置為null,因為子線程中要用,防止空指針異常 } catch (IOException e) { e.printStackTrace(); } } if (mOutputStream != null) { try { mOutputStream.close(); mOutputStream = null; } catch (IOException e) { e.printStackTrace(); } } if (mSocket != null) { try { mSocket.close(); mSocket = null; } catch (IOException e) { e.printStackTrace(); } } }
2、使用available()來監測輸入流
用設置讀取流超時,然後處理異常的方法,會在日志一直打印信息:
強迫症,沒有辦法,總想要解決它。
就想到用available()取代之:
// 讀取流 byte[] data = new byte[0]; try { while (mInputStream.available() > 0) { byte[] buf = new byte[1024]; int len = mInputStream.read(buf); byte[] temp = new byte[data.length + len]; System.arraycopy(data, 0, temp, 0, data.length); System.arraycopy(buf, 0, temp, data.length, len); data = temp; } } catch (IOException e) { }
這樣日志也會一直打印信息,這裡沒定時,所以頻率更高:
想到前一篇說的,在查看前,先等待一會。拿來先試試再說:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">
// 讀取流
byte[] data = new byte[0];
try {
Thread.sleep(100);
while (mInputStream.available() > 0) {
byte[] buf = new byte[1024];
int len = mInputStream.read(buf);
byte[] temp = new byte[data.length + len];
System.arraycopy(data, 0, temp, 0, data.length);
System.arraycopy(buf, 0, temp, data.length, len);
data = temp;
}
} catch (IOException | InterruptedException e) {
}
OK,日志很干淨了。這才爽。。。(雖然沒理解why)
接著上篇繼續來,目錄也連續著。
UDP發送數據,不管對方有沒收到,也就不需要把兩主機先連接好再通信。所以,UDP一般不用做自由通信用。下面是最簡單的demo,服務器只負責接收數據,而客戶端只負責發送數據。
關於UDP網絡編程,主要區分TCP,注意以下幾點:
連接網絡屬於耗時,必須在子線程中執行。網絡的連接主要在socket的send()與receive(); 服務器與客戶端的套接字都是DatagramSocket; 接收時監聽的端口與DatagramSocket直接綁定,此綁定的端口也可直接用於發送數據; 目標主機及端口信息都是封裝在數據報DatagramPacket中。本機的發送端口若未綁定,則是由系統分配; 是數據報模式(TCP是流模式),數據發送與接收都是使用數據報。一次性發送完畢,接收也是一次性必須接收完畢,所以數據緩沖區要足夠大,否則會導致數據丟失; 能在局域網內組播與廣播。主要API:
DatagramSocket:private boolean mIsServerOn; private void turnOnUdpServer() { final int port = 8000; new Thread(){ @Override public void run() { super.run(); DatagramSocket socket = null; try { // 1、創建套接字 socket = new DatagramSocket(port); // 2、創建數據報 byte[] data = new byte[1024]; DatagramPacket packet = new DatagramPacket(data, data.length); // 3、一直監聽端口,接收數據包 mIsServerOn = true; while (mIsServerOn) { socket.receive(packet); String rece = new String(data, 0, packet.getLength(), Charset.forName("UTF-8")); pushMsgToMain(rece); // 推送信息到主線程 } } catch (IOException e) { e.printStackTrace(); } finally { if (null != socket) { socket.close(); socket = null; } } } }.start(); }
主要API(與服務器一樣的,就不介紹了):
DatagramSocket:private void turnOnUdpClient() { final String hostIP = "192.168.1.145"; final int port = 8000; new Thread(new Runnable() { @Override public void run() { DatagramSocket socket = null; try { // 1、創建套接字 socket = new DatagramSocket(8888); // 2、創建host的地址包裝實例 SocketAddress socketAddr = new InetSocketAddress(hostIP, port); // 3、創建數據報。包含要發送的數據、與目標主機地址 byte[] data = "Hello, I am Client".getBytes(Charset.forName("UTF-8")); DatagramPacket packet = new DatagramPacket(data, data.length, socketAddr); // 4、發送數據 socket.send(packet); // 再次發送數據 packet.setData("Second information from client".getBytes(Charset.forName("UTF-8"))); socket.send(packet); } catch (IOException e) { e.printStackTrace(); } finally { if (null != socket) { socket.close(); } } } }).start(); }
廣播就是發送信息給網絡中內所有的計算機設備。
廣播的實現方法:在發送消息時,把目標主機IP地址修改為廣播地址即可。
廣播地址,一般有兩種:
UDP有固定的廣播地址:255.255.255.255 另外,使用TCP/IP協議的網絡,主機標識段host ID全為1的IP地址也為廣播地址。如:我的局域網網段為192.168.1.0(255.255.255.0),廣播地址為:192.168.1.255。組播,是讓同一組的計算機設備都接收到信息。讓具有相同需求功能的計算機設備,加入到同一組中,然後任一計算機發送組播信息,其他成員都能接收到。
發送和接收信息,都必須使用組播地址(224.0.0.0~239.255.255.255)。計算機要加入該組,就必須加入該多播組地址。
具有以下特點:
它與廣播都是UDP獨有的; 只有相同組的計算機設備才能接收到信息; 發送和接收的套接字都是MulticastSocket。主要API(基本使用方法與DatagramSocket是一樣的,就多了幾個方法):
MulticastSocket:下面是發送和接收的demo代碼。
發送:
private void sendUdpMulticast() { final String groupIP = "224.1.1.1"; final int port = 8000; new Thread(new Runnable() { @Override public void run() { MulticastSocket mcSocket = null; try { // 1、創建組播套接字 mcSocket = new MulticastSocket(); // 設置TTL為1,套接字發送的范圍為本地網絡。默認也為1 mcSocket.setTimeToLive(1); // 2、創建組播網絡地址,並判斷 InetAddress groupAddr = InetAddress.getByName(groupIP); if (!groupAddr.isMulticastAddress()) { pushMsgToMain(UDP_HANDLER_MESSAGE_TOAST, "IP地址不是組播地址(224.0.0.0~239.255.255.255)"); return; } // 3、讓套接字加入到組播中 mcSocket.joinGroup(groupAddr); // 4、創建數據報 byte[] data = ("Hi, I am Multicast of UDP".getBytes(Charset.forName("UTF-8"))); DatagramPacket pack = new DatagramPacket(data, data.length, groupAddr, port); // 5、發送信息 mcSocket.send(pack); } catch (IOException e) { e.printStackTrace(); } finally { if (null != mcSocket) { mcSocket.close(); } } } }).start(); }
接收:
private boolean mIsUdpMulticastOn; private void receiveUdpMulticast() { final String groupIP = "224.1.1.1"; final int port = 8000; new Thread(){ @Override public void run() { MulticastSocket mcSocket = null; try { // 1、創建多播套接字 mcSocket = new MulticastSocket(port); // 2、創建多播組地址,並校驗 InetAddress groupAddr = InetAddress.getByName(groupIP); if (!groupAddr.isMulticastAddress()) { pushMsgToMain(UDP_HANDLER_MESSAGE_TOAST, "IP地址不是組播地址(224.0.0.0~239.255.255.255)"); return; } // 3、把套接字加入到多播組中 mcSocket.joinGroup(groupAddr); // 4、創建數據報 byte[] data = new byte[1024]; DatagramPacket pack = new DatagramPacket(data, data.length); // 5、接收信息。循環接收信息,並把接收到的數據交給主線程處理 mIsUdpMulticastOn = true; while (mIsUdpMulticastOn) { mcSocket.receive(pack); String rece = new String(data, pack.getOffset(), pack.getLength()); pushMsgToMain(UDP_HANDLER_MESSAGE_DATA, rece); } } catch (IOException e) { e.printStackTrace(); } finally { if (null != mcSocket) { mcSocket.close(); } } } }.start(); }
經過測試,DatagramPacket中的數據data最大是65507,超過則會在發送的時候報錯:
Exception:sendto failed: EMSGSIZE (Message too long)
接收的data大小,可以超65536(2^16),但一般也沒必要超過發送的最大值65507,最多65536。
發送的測試,自己設計了一個數據填充小算法。使用時,在發送的時候修改data的大小即可。代碼如下:
byte[] data = new byte[65507];
byte[] temp = "abcdefghijklmnopABCDEFGHIJKLMNOP".getBytes(); // 固定為32個
for (int i = 0; i < data.length >> 5; i++) {
System.arraycopy(temp, 0, data, i<<5, temp.length);
}
System.arraycopy(temp, 0, data, data.length - data.length % temp.length, data.length % temp.length);
大小分析:
數據報的長度是指包括報頭和數據部分在內的總字節數。因為報頭的長度是固定的,所以該域主要被用來計算可變長度的數據部分(又稱為數據負載)。數據報的最大長度根據操作環境的不同而各異。從理論上說,包含報頭在內的數據報的最大長度為65535字節。不過,一些實際應用往往會限制數據報的大小,有時會降低到8192字節。(摘自 百度百科UDP)
而報頭又包括IP包頭(20字節)和UDP報文頭(8字節)。
所以,UDP數據的最大值 = 65535 - 20 - 8 = 65507
雖然我測試那麼大數據時OK的,但不是越大越好,建議小於1472。
3.5.2 bind 與connect 的區別
1、bind(SocketAddress addr)
將套接字綁定到特定的地址和端口,本地的綁定。
使用示例:
DatagramSocket s = new DatagramSocket(null);
SocketAddress local = new InetSocketAddress(8888);
s.bind(local);
與此句代碼等效:
DatagramSocket s = new DatagramSocket(8888);
使用說明:
DatagramSocket如果綁定了端口,則不能再綁定,否則拋異常。如:DatagramSocket s = new DatagramSocket(8000); s.bind(local); 一般情況下,去綁定地址(就算與本機地址一樣)也將報錯。如:SocketAddress local = new InetSocketAddress("192.168.1.222", 8888); s.bind(local);
2、connect(SocketAddress addr)
將套接字連接到遠程套接字地址(IP地址+端口號),連接對方。
使用示例:
socket = new DatagramSocket(8888);
SocketAddress local = new InetSocketAddress("192.168.1.145", 8000);
socket.connect(local);
byte[] data = "Hello, I am Client".getBytes(Charset.forName("UTF-8"));
DatagramPacket packet = new DatagramPacket(data, data.length);
socket.send(packet);
與此代碼等效:
socket = new DatagramSocket(8888);
SocketAddress socketAddr = new InetSocketAddress(hostIP, port);
byte[] data = "Hello, I am Client".getBytes(Charset.forName("UTF-8"));
DatagramPacket packet = new DatagramPacket(data, data.length, socketAddr);
socket.send(packet);
3.5.3 巧記組播地址
組播地址為224.0.0.0~239.255.255.255。怎麼記?
查isMulticastAddress()的源碼:
public boolean isMulticastAddress() {
return ((holder().getAddress() & 0xf0000000) == 0xe0000000);
}
也就是說,只要第一段的高四位為E的IP地址,就是組播地址。
而第一段的最小值E0 = 256 - 32(後五位) = 224
最大值EF = 224 + 15(F) = 239
3.5.4 簡單理解TTL
TTL(Time To Live)的作用是限制IP數據包在計算機網絡中的存在的時間。TTL的最大值是255,TTL的一個推薦值是64。
雖然TTL從字面上翻譯,是可以存活的時間,但實際上TTL是IP數據包在計算機網絡中可以轉發的最大跳數。TTL字段由IP數據包的發送者設置,在IP數據包從源到目的的整個轉發路徑上,每經過一個路由器,路由器都會修改這個TTL字段值,具體的做法是把該TTL的值減1,然後再將IP包轉發出去。如果在IP包到達目的IP之前,TTL減少為0,路由器將會丟棄收到的TTL=0的IP包並向IP包的發送者發送 ICMP time exceeded消息。
所以,TTL可以簡單的理解為能達到路由器的個數。
剩下的是一個UDP實例與常見問題。由於實例的代碼太多,還是另外寫一篇吧。。。>>>
問題:橫豎屏切換時Activity的生命周期?測試環境:華為mate7 package com.virglass.beyond.activity;import
Volley的核心結構如下所示:今天主要看Network相關:Network整體功能介紹:通過實現了Network接口的類(這裡是BasicNetwork),執行從Req
在手機應用中,用戶點擊回退按鈕一般是返回上個頁面,一般頁面不用處理,如果在首頁,點回退,沒任何提示,就把應用給關了,這個用戶體驗就不太好了,所以一般都會給用戶一個確認的提
本文實例講述了Android使用criteria選擇合適的地理位置服務實現方法。分享給大家供大家參考,具體如下:/* LocationActivity.java * @a