每日一題:網(wǎng)絡(luò)編程概述
面試率: ★★★★★
面試提醒
網(wǎng)絡(luò)編程是個十分重要知識點(diǎn),在移動端和服務(wù)端,前端都扮演著十分重要的位置,Android中幾乎只要是上線的項(xiàng)目基本都會涉及到網(wǎng)絡(luò),應(yīng)聘者們應(yīng)該熟悉掌握該技術(shù).
面試技巧
網(wǎng)絡(luò)編程概念會比較多,你可能要掌握Android平臺下的常見網(wǎng)絡(luò)API接口;一些相關(guān)網(wǎng)絡(luò)協(xié)議;網(wǎng)絡(luò)架構(gòu)和數(shù)據(jù)傳輸;網(wǎng)絡(luò)安全.
從上面看來我們要掌握的內(nèi)容實(shí)在太多了,為了簡化我們的準(zhǔn)備工作,提供以下幾個技巧:
網(wǎng)絡(luò)庫掌握技巧
面試中經(jīng)常會被問到一些比較流行的網(wǎng)絡(luò)框架,如okHttp,而okHttp源碼中包含了協(xié)議,緩存,架構(gòu),加密,壓縮,異步,并發(fā)等技術(shù),沒錯你要掌握的就是把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交互等
面試題
下面通過一些比較常見的面試題來,提高我們對Android網(wǎng)絡(luò)編程的了解.
Android中常見的網(wǎng)絡(luò)API接口了解多少?
首先希坚,你應(yīng)該先了解的下面幾個概念:
- Android平臺網(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 對象、設(shè)置鏈接參數(shù)瑞信、鏈接到服務(wù)器厉颤、向服務(wù)器寫數(shù)據(jù)、從服務(wù)器讀取數(shù)據(jù)等通信凡简。這些在Java網(wǎng)絡(luò)編程中均有涉及逼友。 - Org.apache 接口
對于大部分應(yīng)用程序而言JDK本身提供的網(wǎng)絡(luò)功能已遠(yuǎn)遠(yuǎn)不夠,這時就需要Android提供的Apache HttpClient 了秤涩。它是一個開源項(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ī)范的客戶端梳庆,簡而言之就是Android網(wǎng)絡(luò)瀏覽器(如chrome,UcWeb暖途,QQ瀏覽器等等)訪問web服務(wù)器端的方式了。
- C/S----> 就客戶端/服務(wù)器端模式膏执,通過任意的網(wǎng)絡(luò)協(xié)議通信驻售,需要特定的客戶端軟件。
服務(wù)器一般會返回什么類型數(shù)據(jù)給客戶端?
服務(wù)器端返回客戶端的內(nèi)容有三種方式:
- 以HTML代碼的形式返回更米。
- 以XML字符串的形式返回欺栗,做Android開發(fā)時這種方式比較多。返回的數(shù)據(jù)需要通過XML解析(SAX征峦、DOM迟几,Pull,等)器進(jìn)行解析(必備知識)。
- 以json對象的方式返回(開發(fā)常用)栏笆。
從長連接和短連接的角度分析Android的網(wǎng)絡(luò)編程分為2種:基于socket的 和 基于http協(xié)議的
說說你對socket的了解
socket基本概念
- 三元組(ip地址,協(xié)議,端口)
Socket 是對 TCP/IP 協(xié)議族的一種封裝类腮,是應(yīng)用層與TCP/IP協(xié)議族通信的中間軟件抽象層。從設(shè)計模式的角度看來蛉加,Socket其實(shí)就是一個門面模式蚜枢,它把復(fù)雜的TCP/IP協(xié)議族隱藏在Socket接口后面,對用戶來說针饥,一組簡單的接口就是全部厂抽,讓Socket去組織數(shù)據(jù),以符合指定的協(xié)議丁眼。
Socket 還可以認(rèn)為是一種網(wǎng)絡(luò)間不同計算機(jī)上的進(jìn)程通信的一種方法修肠,利用三元組(ip地址,協(xié)議户盯,端口)就可以唯一標(biāo)識網(wǎng)絡(luò)中的進(jìn)程嵌施,網(wǎng)絡(luò)中的進(jìn)程通信可以利用這個標(biāo)志與其它進(jìn)程進(jìn)行交互。
socket下的外觀模式
外觀模式(face pattern)
外觀模式也叫門面模式
,雖然平時我們可能沒什么聽過這個模式,但是外面實(shí)際開發(fā)中很多時候也會使用到他,因?yàn)樗昝赖伢w現(xiàn)了依賴倒轉(zhuǎn)原則
和迪米特法則
的思想,所以是非常常用的模式之一.
外觀設(shè)計模式在什么時候用好?
這個要分三個階段:
-
在設(shè)計初期階段,應(yīng)該要有意思的講不通的兩個層分離
,如經(jīng)典的三層架構(gòu)(mv*),就需要考慮數(shù)據(jù)層訪問業(yè)務(wù)層,業(yè)務(wù)層和展示層的層與層之間建立外觀Facade ,
這樣可以為負(fù)責(zé)的子系統(tǒng)提供一個簡單的接口,使得耦合大大降低. -
在開發(fā)階段,子系統(tǒng)往往因?yàn)椴粩嗟闹貥?gòu)演化而變得越來越復(fù)雜,
大多數(shù)的模式使用時也都會產(chǎn)生很多小的類,這本來是好事,但是也給外部調(diào)用它們的用戶程序帶來了使用上的困難,給這些小類添加外觀Facade 可以提供一個簡單的接口,減少它們之間的依賴.
-
在維護(hù)一個遺留的大型系統(tǒng)時,可能這個系統(tǒng)以及非常難以維護(hù)和拓展了,
但因?yàn)樗浅V匾墓δ?新的需求必須要依賴于他.此時用外觀模式Facade也是非常適合的.你可以為新的系統(tǒng)開發(fā)一個外觀Facade 類,來提供設(shè)計粗糙或高度復(fù)雜的遺留代碼的比較清晰的簡單接口,讓新的系統(tǒng)與Facade 對象交互, Facade與遺留代碼交互所有復(fù)雜的工作.
實(shí)例:
農(nóng)夫有很多家禽,如鴨,雞,鵝,他們都需要喂養(yǎng)和照顧.
因?yàn)殡u鴨鵝吃的飼料都是一樣的,所以如果農(nóng)夫每種家禽都單獨(dú)去做,這樣的效率就會很低,而且整個過程也相當(dāng)復(fù)雜,因此我們通過門面模式把要對雞鴨鵝做的事情封裝在 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請求
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è)置連接超時,如果網(wǎng)絡(luò)不好,Android系統(tǒng)在超過默認(rèn)時間會收回資源中斷操作.
- 返回的響應(yīng)碼200,是成功.
- 在Android中對文件流的操作和Java SE上面是一樣的.
- 在對大文件的操作時,要將文件寫到SDCard上面,不要直接寫到手機(jī)內(nèi)存上.
- 操作大文件是,要一遍從網(wǎng)絡(luò)上讀,一遍要往SDCard上面寫,減少手機(jī)內(nèi)存的使用.這點(diǎn)很重要,面試經(jīng)常會被問到.
- 對文件流操作完,要記得及時關(guān)閉.
- POST請求
向服務(wù)器端發(fā)送請求參數(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è)置長連接 conn.setRequestProperty("Content-Length", String.valueOf(data.length));//設(shè)置文件長度
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請求頭
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請求必須設(shè)置允許輸出.
- 不要使用緩存,容易出現(xiàn)問題.
- 在開始用HttpURLConnection對象的setRequestProperty()設(shè)置,就是生成HTML文件頭.
- 向服務(wù)器端發(fā)送xml數(shù)據(jù)(也稱為實(shí)體Entity)
XML格式是通信的標(biāo)準(zhǔn)語言,Android系統(tǒng)也可以通過發(fā)送XML文件傳輸數(shù)據(jù).- 我們使用的是用HTML的方式傳輸文件,這個方式只能傳輸一般在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ù)傳遞時巧号,需要使用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ù)的時候就一定會有問題靠欢±鹊校可是如何判斷這個套接字是否還可以使用呢?這個就需要在系統(tǒng)中創(chuàng)建心跳機(jī)制门怪。其實(shí)TCP中已經(jīng)為我們實(shí)現(xiàn)了一個叫做心跳的機(jī)制骡澈。
大部分C/S的應(yīng)用需要心跳機(jī)制。心跳機(jī)制一般在Server和Client都要實(shí)現(xiàn)掷空,兩者實(shí)現(xiàn)原理基本一樣肋殴。Client不關(guān)心性能,怎么做都行坦弟。
如果應(yīng)用是基于TCP的疼电,可以簡單地通過SO_KEEPALIVE實(shí)現(xiàn)心跳。TCP在設(shè)置的KeepAlive(java中的長連接) 定時器到達(dá)時向?qū)Χ税l(fā)一個檢測TCP segment减拭,如果沒收到ACK或RST,嘗試幾次后区丑,就認(rèn)為對端已經(jīng)不存在拧粪,最后通知應(yīng)用程序。這里有個缺點(diǎn)是Server主動發(fā)出檢測包沧侥,對性能有點(diǎn)影響
可霎。
應(yīng)用自己實(shí)現(xiàn):
- Client啟動一個定時器,不斷發(fā)心跳宴杀;
- Server收到心跳后癣朗,給個回應(yīng);
- Server啟動一個定時器旺罢,判斷Client是否存在旷余,判斷方法這里列兩種:時間差和簡單標(biāo)志。
時間差策略
- 收到一個心跳后扁达,記錄當(dāng)前時間(記為recvedTime)正卧。
- 判斷定時器時間到達(dá),計算多久沒收到心跳的時間(T)=當(dāng)前時間 - recvedTime(上面記錄的時間)跪解。如果T大于某個設(shè)定值炉旷,就可以認(rèn)為Client超時了。
簡單標(biāo)志
- 收到一個心跳后叉讥,設(shè)置連接標(biāo)志為true窘行;
- 判斷定時器時間到達(dá),查看所有的標(biāo)志图仓,false的罐盔,認(rèn)為對端超時了;true的將其設(shè)成false救崔。
- 上面這種方法比上面簡單一些翘骂,但檢測某個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+"')");