每日一題:網(wǎng)絡(luò)編程概述
面試率: ★★★★★
面試提醒
網(wǎng)絡(luò)編程是個(gè)十分重要知識(shí)點(diǎn),在移動(dòng)端和服務(wù)端,前端都扮演著十分重要的位置,Android中幾乎只要是上線的項(xiàng)目基本都會(huì)涉及到網(wǎng)絡(luò),應(yīng)聘者們應(yīng)該熟悉掌握該技術(shù).
面試技巧
網(wǎng)絡(luò)編程概念會(huì)比較多,你可能要掌握Android平臺(tái)下的常見網(wǎng)絡(luò)API接口;一些相關(guān)網(wǎng)絡(luò)協(xié)議;網(wǎng)絡(luò)架構(gòu)和數(shù)據(jù)傳輸;網(wǎng)絡(luò)安全.
從上面看來我們要掌握的內(nèi)容實(shí)在太多了,為了簡(jiǎn)化我們的準(zhǔn)備工作,提供以下幾個(gè)技巧:
網(wǎng)絡(luò)庫(kù)掌握技巧
面試中經(jīng)常會(huì)被問到一些比較流行的網(wǎng)絡(luò)框架,如okHttp,而okHttp源碼中包含了協(xié)議,緩存,架構(gòu),加密,壓縮,異步,并發(fā)等技術(shù),沒錯(cuò)你要掌握的就是把okHttp的源碼熟悉掌握下來,而外面目前有很多這相關(guān)的資料博文.
這種掌握技巧可以比較有條理的全局去掌握網(wǎng)絡(luò)編程,并從中學(xué)習(xí)到一些大神代碼書寫規(guī)范.分類掌握技巧
- 智能硬件
如面試智能硬件的公司你主要要掌握的是相關(guān)的WIFI,藍(lán)牙,Socket. - 視頻直播
Android這邊要了解jni,ndk,媒體協(xié)議(hls、rtsp、rtmp等),另外還有流服務(wù)器. - 新聞電商
服務(wù)端分頁加載大數(shù)據(jù),網(wǎng)絡(luò)框架封裝,js與java交互等
面試題
下面通過一些比較常見的面試題來,提高我們對(duì)Android網(wǎng)絡(luò)編程的了解.
Android中常見的網(wǎng)絡(luò)API接口了解多少?
首先,你應(yīng)該先了解的下面幾個(gè)概念:
- Android平臺(tái)網(wǎng)絡(luò)相關(guān)API接口
- Java.NET.* (標(biāo)準(zhǔn)Java接口)
java.Net.* 提供與聯(lián)網(wǎng)有關(guān)的類,包括流、數(shù)據(jù)包套接字(socket) 、Internet 協(xié)議、常見Http處理等。比如:創(chuàng)建URL,以及URLConnection /HttpURLConnection 對(duì)象、設(shè)置鏈接參數(shù)、鏈接到服務(wù)器、向服務(wù)器寫數(shù)據(jù)、從服務(wù)器讀取數(shù)據(jù)等通信。這些在Java網(wǎng)絡(luò)編程中均有涉及。 - Org.apache 接口
對(duì)于大部分應(yīng)用程序而言JDK本身提供的網(wǎng)絡(luò)功能已遠(yuǎn)遠(yuǎn)不夠,這時(shí)就需要Android提供的Apache HttpClient 了。它是一個(gè)開源項(xiàng)目,功能更加完善,為客戶端的Http編程提供高效、最新、功能豐富的工具包支持。 - Android.net.* (Android網(wǎng)絡(luò)接口)
常常使用此包下的類進(jìn)行Android特有的網(wǎng)絡(luò)編程,如:訪問WiFi,訪問Android聯(lián)網(wǎng)信息,郵件等功能。
常見的網(wǎng)絡(luò)架構(gòu)有幾種?
網(wǎng)絡(luò)架構(gòu)主要有兩種模式B/S,C/S
- B/S----> 就是瀏覽器/服務(wù)器端模式了,通過應(yīng)用層的HTTP協(xié)議通信,不需要特定客戶端軟件,而是需要統(tǒng)一規(guī)范的客戶端,簡(jiǎn)而言之就是Android網(wǎng)絡(luò)瀏覽器(如chrome,UcWeb,QQ瀏覽器等等)訪問web服務(wù)器端的方式了。
- C/S----> 就客戶端/服務(wù)器端模式,通過任意的網(wǎng)絡(luò)協(xié)議通信,需要特定的客戶端軟件。
服務(wù)器一般會(huì)返回什么類型數(shù)據(jù)給客戶端?
服務(wù)器端返回客戶端的內(nèi)容有三種方式:
- 以HTML代碼的形式返回。
- 以XML字符串的形式返回,做Android開發(fā)時(shí)這種方式比較多。返回的數(shù)據(jù)需要通過XML解析(SAX、DOM,Pull,等)器進(jìn)行解析(必備知識(shí))。
- 以json對(duì)象的方式返回(開發(fā)常用)。
從長(zhǎng)連接和短連接的角度分析Android的網(wǎng)絡(luò)編程分為2種:基于socket的 和 基于http協(xié)議的
說說你對(duì)socket的了解
socket基本概念
- 三元組(ip地址,協(xié)議,端口)
Socket 是對(duì) TCP/IP 協(xié)議族的一種封裝,是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層。從設(shè)計(jì)模式的角度看來,Socket其實(shí)就是一個(gè)門面模式,它把復(fù)雜的TCP/IP協(xié)議族隱藏在Socket接口后面,對(duì)用戶來說,一組簡(jiǎn)單的接口就是全部,讓Socket去組織數(shù)據(jù),以符合指定的協(xié)議。
Socket 還可以認(rèn)為是一種網(wǎng)絡(luò)間不同計(jì)算機(jī)上的進(jìn)程通信的一種方法,利用三元組(ip地址,協(xié)議,端口)就可以唯一標(biāo)識(shí)網(wǎng)絡(luò)中的進(jìn)程,網(wǎng)絡(luò)中的進(jìn)程通信可以利用這個(gè)標(biāo)志與其它進(jìn)程進(jìn)行交互。
socket下的外觀模式
外觀模式(face pattern)
外觀模式也叫門面模式,雖然平時(shí)我們可能沒什么聽過這個(gè)模式,但是外面實(shí)際開發(fā)中很多時(shí)候也會(huì)使用到他,因?yàn)樗昝赖伢w現(xiàn)了依賴倒轉(zhuǎn)原則和迪米特法則的思想,所以是非常常用的模式之一.
外觀設(shè)計(jì)模式在什么時(shí)候用好?
這個(gè)要分三個(gè)階段:
-
在設(shè)計(jì)初期階段,應(yīng)該要有意思的講不通的兩個(gè)層分離,如經(jīng)典的三層架構(gòu)(mv*),就需要考慮數(shù)據(jù)層訪問業(yè)務(wù)層,業(yè)務(wù)層和展示層的層與層之間建立外觀Facade ,這樣可以為負(fù)責(zé)的子系統(tǒng)提供一個(gè)簡(jiǎn)單的接口,使得耦合大大降低. -
在開發(fā)階段,子系統(tǒng)往往因?yàn)椴粩嗟闹貥?gòu)演化而變得越來越復(fù)雜,大多數(shù)的模式使用時(shí)也都會(huì)產(chǎn)生很多小的類,這本來是好事,但是也給外部調(diào)用它們的用戶程序帶來了使用上的困難,給這些小類添加外觀Facade 可以提供一個(gè)簡(jiǎn)單的接口,減少它們之間的依賴. -
在維護(hù)一個(gè)遺留的大型系統(tǒng)時(shí),可能這個(gè)系統(tǒng)以及非常難以維護(hù)和拓展了,但因?yàn)樗浅V匾墓δ?新的需求必須要依賴于他.此時(shí)用外觀模式Facade也是非常適合的.你可以為新的系統(tǒng)開發(fā)一個(gè)外觀Facade 類,來提供設(shè)計(jì)粗糙或高度復(fù)雜的遺留代碼的比較清晰的簡(jiǎn)單接口,讓新的系統(tǒng)與Facade 對(duì)象交互, Facade與遺留代碼交互所有復(fù)雜的工作.
實(shí)例:
農(nóng)夫有很多家禽,如鴨,雞,鵝,他們都需要喂養(yǎng)和照顧.
因?yàn)殡u鴨鵝吃的飼料都是一樣的,所以如果農(nóng)夫每種家禽都單獨(dú)去做,這樣的效率就會(huì)很低,而且整個(gè)過程也相當(dāng)復(fù)雜,因此我們通過門面模式把要對(duì)雞鴨鵝做的事情封裝在 Poultry 家禽類中,里面過程不用理,只要提供 feed 和 care 的公共方法即可.
//class: Duck,Duck,Goose
//method: feed,care
class Poultry{
pig = new Pig();
duck = new Duck();
goose = new Goose();
void feedMethod(){
pig.feed();
duck.feed();
goose.feed();
}
void careMethod(){
pig.care();
duck.care();
goose.care();
}
}
基于TCP/IP協(xié)議之上的協(xié)議
使用HttpURLConnection連接URL
- GET請(qǐng)求
URL url = new URL(
"http://baidu.com");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setConnectTimeout(TIME);
conn.setReadTimeout(TIME);
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
input = conn.getInputStream();
if (input != null) {
//TODO 把流轉(zhuǎn)成String
}
}
- 記得設(shè)置連接超時(shí),如果網(wǎng)絡(luò)不好,Android系統(tǒng)在超過默認(rèn)時(shí)間會(huì)收回資源中斷操作.
- 返回的響應(yīng)碼200,是成功.
- 在Android中對(duì)文件流的操作和Java SE上面是一樣的.
- 在對(duì)大文件的操作時(shí),要將文件寫到SDCard上面,不要直接寫到手機(jī)內(nèi)存上.
- 操作大文件是,要一遍從網(wǎng)絡(luò)上讀,一遍要往SDCard上面寫,減少手機(jī)內(nèi)存的使用.這點(diǎn)很重要,面試經(jīng)常會(huì)被問到.
- 對(duì)文件流操作完,要記得及時(shí)關(guān)閉.
- POST請(qǐng)求
向服務(wù)器端發(fā)送請(qǐng)求參數(shù),步驟:
String data = "username=justice&password=infiniteJustice";
URL url = new URL(
"http://192.168.31.144:10010/MINATest/servlet/DataTestServlet");
conn = (HttpURLConnection) url.openConnection();
conn.setConnectTimeout(TIME);
conn.setReadTimeout(TIME);
conn.setDoInput(true);// 允許輸入
conn.setDoOutput(true);// 允許輸出
conn.setUseCaches(false);// 不使用Cache
conn.setRequestProperty("Charset", ENCODING);
conn.setRequestProperty("Content-Length",
String.valueOf(data.length()));
conn.setRequestProperty("Content-Type", "text");//設(shè)置文件類型
conn.setRequestProperty("Charset", "UTF-8");//設(shè)置字符集
conn.setRequestProperty("Connection", "Keep-Alive");//設(shè)置長(zhǎng)連接 conn.setRequestProperty("Content-Length", String.valueOf(data.length));//設(shè)置文件長(zhǎng)度
conn.setRequestProperty("Accept“,” image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/xaml+xml, application/vnd.ms-xpsdocument, application/x-ms-xbap, application/x-ms-application, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword");//設(shè)置HTTP請(qǐng)求頭
conn.setRequestProperty("Accept-Language“,"zh-CN");//設(shè)置語言
conn.setRequestMethod("POST");
DataOutputStream outStream = new DataOutputStream(
conn.getOutputStream());
outStream.write(data.getBytes());
outStream.flush();
outStream.close();
if (conn == null) {
return;
}
int responseCode = conn.getResponseCode();
if (responseCode == 200) {
input = conn.getInputStream();
if (input != null) {
}
}
- 發(fā)送POST請(qǐng)求必須設(shè)置允許輸出.
- 不要使用緩存,容易出現(xiàn)問題.
- 在開始用HttpURLConnection對(duì)象的setRequestProperty()設(shè)置,就是生成HTML文件頭.
- 向服務(wù)器端發(fā)送xml數(shù)據(jù)(也稱為實(shí)體Entity)
XML格式是通信的標(biāo)準(zhǔn)語言,Android系統(tǒng)也可以通過發(fā)送XML文件傳輸數(shù)據(jù).- 我們使用的是用HTML的方式傳輸文件,這個(gè)方式只能傳輸一般在5M一下的文件.
- 傳輸大文件不適合用HTML的方式,傳輸大文件我們要面向Socket編程.確保程序的穩(wěn)定性
- 將地址和參數(shù)存到byte數(shù)組中:byte[] data = params.toString().getBytes();
- 利用Apache的HttpClient實(shí)現(xiàn)Android客戶端發(fā)送實(shí)體Entity.
- 以上為直接利用HTTP協(xié)議來實(shí)現(xiàn)的,其實(shí)Android已經(jīng)集成了,第三方開源項(xiàng)目:
org.apache.http.client.HttpClient,可以直接參考它提供的API使用。
HTTP clients encapsulate a smorgasbord of objects required to execute HTTP requests while handling cookies, authentication, connection management, and other features. Thread safety of HTTP clients depends on the implementation and configuration of the specific client.
使用POST方法進(jìn)行參數(shù)傳遞時(shí),需要使用NameValuePair來保存要傳遞的參數(shù)。另外,還需要設(shè)置所使用的字符集。
其他網(wǎng)絡(luò)相關(guān)
服務(wù)器實(shí)現(xiàn)心跳機(jī)制的兩種策略?
網(wǎng)絡(luò)中的接收和發(fā)送數(shù)據(jù)都是使用SOCKET進(jìn)行實(shí)現(xiàn)。但是如果此套接字已經(jīng)斷開,那發(fā)送數(shù)據(jù)和接收數(shù)據(jù)的時(shí)候就一定會(huì)有問題??墒侨绾闻袛噙@個(gè)套接字是否還可以使用呢?這個(gè)就需要在系統(tǒng)中創(chuàng)建心跳機(jī)制。其實(shí)TCP中已經(jīng)為我們實(shí)現(xiàn)了一個(gè)叫做心跳的機(jī)制。
大部分C/S的應(yīng)用需要心跳機(jī)制。心跳機(jī)制一般在Server和Client都要實(shí)現(xiàn),兩者實(shí)現(xiàn)原理基本一樣。Client不關(guān)心性能,怎么做都行。
如果應(yīng)用是基于TCP的,可以簡(jiǎn)單地通過SO_KEEPALIVE實(shí)現(xiàn)心跳。TCP在設(shè)置的KeepAlive(java中的長(zhǎng)連接) 定時(shí)器到達(dá)時(shí)向?qū)Χ税l(fā)一個(gè)檢測(cè)TCP segment,如果沒收到ACK或RST,嘗試幾次后,就認(rèn)為對(duì)端已經(jīng)不存在,最后通知應(yīng)用程序。這里有個(gè)缺點(diǎn)是Server主動(dòng)發(fā)出檢測(cè)包,對(duì)性能有點(diǎn)影響。
應(yīng)用自己實(shí)現(xiàn):
- Client啟動(dòng)一個(gè)定時(shí)器,不斷發(fā)心跳;
- Server收到心跳后,給個(gè)回應(yīng);
- Server啟動(dòng)一個(gè)定時(shí)器,判斷Client是否存在,判斷方法這里列兩種:時(shí)間差和簡(jiǎn)單標(biāo)志。
時(shí)間差策略
- 收到一個(gè)心跳后,記錄當(dāng)前時(shí)間(記為recvedTime)。
- 判斷定時(shí)器時(shí)間到達(dá),計(jì)算多久沒收到心跳的時(shí)間(T)=當(dāng)前時(shí)間 - recvedTime(上面記錄的時(shí)間)。如果T大于某個(gè)設(shè)定值,就可以認(rèn)為Client超時(shí)了。
簡(jiǎn)單標(biāo)志
- 收到一個(gè)心跳后,設(shè)置連接標(biāo)志為true;
- 判斷定時(shí)器時(shí)間到達(dá),查看所有的標(biāo)志,false的,認(rèn)為對(duì)端超時(shí)了;true的將其設(shè)成false。
- 上面這種方法比上面簡(jiǎn)單一些,但檢測(cè)某個(gè)Client是否離線的誤差有點(diǎn)大。
native與js互調(diào)?
Android WebView控件
在android app 中嵌入網(wǎng)頁的形式。
此外,通過webview可以實(shí)現(xiàn)HTML <-> Javascript <-> Android Java 交互,例如:
- Javascript調(diào)用Java
webview.addJavascriptInterface(Object obj, String interfaceName)方法,讓該方法可以在javascript中被調(diào)用;
在Android中創(chuàng)建提供給webview中頁面js調(diào)用的接口
,并在該接口中添加@JavascriptInterface注入,表示該方法可以被js掉用,如js調(diào)用native關(guān)閉activity方法closeThisPage:
@JavascriptInterfacepublic void closeThisPage(){
//關(guān)閉本頁面 context.finish();}
- Java中調(diào)用Javascript腳本中的方法
調(diào)用js的show方法.
webview.loadUrl("javascript:show('"+json+"')");