搭建Android上的服務(wù)器 “實(shí)現(xiàn)隔空取物”的方法
概述
今天逛簡(jiǎn)書的時(shí)候,發(fā)現(xiàn)了一個(gè)庫(kù):
https://github.com/MZCretin/WifiTransfer-master
主要功能是這樣的,先口述一下,當(dāng)打開(kāi)app,可以通過(guò)瀏覽器訪問(wèn)一個(gè)地址,然后通過(guò)瀏覽器可以給手機(jī)上上傳apk(也支持已有apk刪除),然后手機(jī)端可以安裝、卸載該apk。
三張圖就明白了:
應(yīng)用啟動(dòng)后:
然后PC端訪問(wèn):
拖拽apk上傳,即可上傳到手機(jī)端。
ok,大致介紹清楚了。
注意一定要在同一個(gè)網(wǎng)段。
先不談其用處到底有多大,很多時(shí)候我看到一個(gè)項(xiàng)目的時(shí)候,很少考慮其能干嘛,考慮最多的是它是如何實(shí)現(xiàn)的,我會(huì)么,不會(huì)那就學(xué),至于能干嘛,那要等我學(xué)會(huì)之后?
那么思考下他的實(shí)現(xiàn),這種上傳文件的方式,在PC端更加常見(jiàn),上傳文件到服務(wù)器。
說(shuō)到這,就可以想到,可能這個(gè)app在手機(jī)端搭建了一個(gè)服務(wù)器。
恩,沒(méi)錯(cuò)就是這樣的,在手機(jī)端搭建了一個(gè)服務(wù)器,這樣就可以通過(guò)html,將PC端的文件傳給手機(jī)端,然后手機(jī)端收到后再同步界面。
同時(shí),也可以將手機(jī)上Sdcard上的文件,完全在PC上呈現(xiàn)。
手機(jī)端的Server利用的是該庫(kù):https://github.com/koush/AndroidAsync
解析源碼的事情就不做了,有興趣可以自己學(xué)習(xí)下,接下來(lái)開(kāi)始正片。
一個(gè)群友的問(wèn)題
之所以會(huì)關(guān)注到這個(gè)庫(kù),是因?yàn)樵趙anandroid群,有個(gè)哥們連續(xù)問(wèn)了好久的一個(gè)問(wèn)題,問(wèn)題是:
如何通過(guò)瀏覽器輸入一個(gè)地址播放手機(jī)上的視頻
當(dāng)時(shí)也很多人回答,回答的核心都是正確的。
當(dāng)然我恰好看到這個(gè)庫(kù),之前也沒(méi)推送過(guò)相關(guān)內(nèi)容,所以我決定寫個(gè)簡(jiǎn)易的Demo.
當(dāng)然是Demo就沒(méi)有什么美觀可言了,僅為快速實(shí)現(xiàn)效果。
效果圖是這樣的:
頁(yè)面上顯示手機(jī)上的視頻列表,然后點(diǎn)擊某個(gè)視頻,即開(kāi)始播放該視頻。
有了上例參考,非常簡(jiǎn)單。
注:部分代碼直接從上例copy。
該案例需要網(wǎng)絡(luò)和Sdcard權(quán)限!
先把服務(wù)器搭起來(lái)依賴庫(kù)
首先,依賴下我們搭建Server需要用到的庫(kù):
compile 'com.koushikdutta.async:androidasync:2.+'
編寫簡(jiǎn)易html
然后我們?cè)赼ssets下編寫一個(gè)html文件用于瀏覽器訪問(wèn),index.html
最簡(jiǎn)單的即可:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> </head> <body> 嘿嘿嘿,連通了... </body> </html>
啟動(dòng)服務(wù),監(jiān)聽(tīng)端口
public class MainActivity extends AppCompatActivity { private AsyncHttpServer server = new AsyncHttpServer(); private AsyncServer mAsyncServer = new AsyncServer(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); server.get("/", new HttpServerRequestCallback() { @Override public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) { try { response.send(getIndexContent()); } catch (IOException e) { e.printStackTrace(); response.code(500).end(); } } }); server.listen(mAsyncServer, 54321); } @Override protected void onDestroy() { super.onDestroy(); if (server != null) { server.stop(); } if (mAsyncServer != null) { mAsyncServer.stop(); } } private String getIndexContent() throws IOException { BufferedInputStream bInputStream = null; try { bInputStream = new BufferedInputStream(getAssets().open("index.html")); ByteArrayOutputStream baos = new ByteArrayOutputStream(); int len = 0; byte[] tmp = new byte[10240]; while ((len = bInputStream.read(tmp)) > 0) { baos.write(tmp, 0, len); } return new String(baos.toByteArray(), "utf-8"); } catch (IOException e) { e.printStackTrace(); throw e; } finally { if (bInputStream != null) { try { bInputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
可以看到很簡(jiǎn)單,創(chuàng)建AsyncHttpServer對(duì)象,我們?cè)趏nCreate中調(diào)用get,對(duì)外設(shè)置一個(gè)get型的url監(jiān)聽(tīng),監(jiān)聽(tīng)的url是/即根目錄。
然后調(diào)用listen,傳入端口號(hào)54321,開(kāi)啟對(duì)該端口的監(jiān)聽(tīng)。
onDestroy的時(shí)候停止服務(wù)器。
當(dāng)捕獲到對(duì)”/”的訪問(wèn)時(shí),讀取assets下的index.html返回給瀏覽器。
記得添加網(wǎng)絡(luò)權(quán)限。
好了,運(yùn)行demo,測(cè)試一下。
輸入地址,你的手機(jī)的IP:端口號(hào)。
注意電腦和手機(jī)在同一個(gè)網(wǎng)段!
然后你應(yīng)該看到如下效果圖:
如果沒(méi)看到,那不用往下了,先找問(wèn)題吧~
完善Demo
接下來(lái),我們將手機(jī)上的mp4返回讓其在瀏覽器上顯示。
很簡(jiǎn)單,既然我們可以監(jiān)聽(tīng)/,返回一個(gè)index.html,我們就能監(jiān)聽(tīng)另一個(gè)url,返回文件目錄。
server.get("/files", new HttpServerRequestCallback() { @Override public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) { JSONArray array = new JSONArray(); File dir = new File(Environment.getExternalStorageDirectory().getPath()); String[] fileNames = dir.list(); if (fileNames != null) { for (String fileName : fileNames) { File file = new File(dir, fileName); if (file.exists() && file.isFile() && file.getName().endsWith(".mp4")) { try { JSONObject jsonObject = new JSONObject(); jsonObject.put("name", fileName); jsonObject.put("path", file.getAbsolutePath()); array.put(jsonObject); } catch (JSONException e) { e.printStackTrace(); } } } } response.send(array.toString()); } });
我們監(jiān)聽(tīng)/files這個(gè)Url,然后返回Sdcard根目錄的視頻文件,拼接成JSON返回。
這里如果你重新啟動(dòng),在瀏覽器上輸入:
http://192.168.1.100:54321/files
會(huì)看到一堆JSON數(shù)據(jù):
但是我們需要在剛才的html上顯示,所以這個(gè)請(qǐng)求應(yīng)該是剛才的Html頁(yè)面發(fā)起:
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <script src="jquery-1.7.2.min.js" type="text/javascript"></script> <title>文檔的標(biāo)題</title> <script type="text/javascript"> $(function() { var now = new Date(); var url = 'files' + '?' + now.getTime(); // 請(qǐng)求JSON數(shù)據(jù) $.getJSON(url, function(data) { // 編輯JSON數(shù)組 for (var i = 0; i < data.length; i++) { // 為每個(gè)對(duì)象生成一個(gè)li標(biāo)簽,添加到頁(yè)面的ul中 var $li = $('<li>' + data[i].name + '</li>'); $li.attr("path", data[i].path); $("#filelist").append($li); } }); }); </script> </head> <body> <ul id="filelist" style="float:left;"></ul> </body> </html>
可能很多朋友沒(méi)了解過(guò)js,不過(guò)應(yīng)該能看明白,$.getJSON
獲取返回的JSON數(shù)組,然后遍歷為每個(gè)Json對(duì)象生成一個(gè)li標(biāo)簽,添加到頁(yè)面上。
這里用了jquery,對(duì)于js的也需要也請(qǐng)求處理,這里省略了,很簡(jiǎn)單,看源碼即可。
此時(shí)訪問(wèn),已經(jīng)可以顯示出視頻目錄了:
接下來(lái)就是點(diǎn)擊播放了,在html里面有個(gè)標(biāo)簽叫video用于播放視頻的,他有個(gè)src屬性用于設(shè)置播放的視頻路徑。
所以我們要做的僅為:
點(diǎn)擊名字,拿到該視頻對(duì)應(yīng)的url,然后設(shè)置給video的src屬性即可。
那么視頻的url是什么?
剛才我們返回了視頻的路徑,所以我們只要再監(jiān)聽(tīng)一個(gè)url,將根據(jù)傳入的視頻路徑,將視頻文件流返回即可。
server.get("/files/.*", new HttpServerRequestCallback() { @Override public void onRequest(AsyncHttpServerRequest request, AsyncHttpServerResponse response) { String path = request.getPath().replace("/files/", ""); try { path = URLDecoder.decode(path, "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } File file = new File(path); if (file.exists() && file.isFile()) { try { FileInputStream fis = new FileInputStream(file); response.sendStream(fis, fis.available()); } catch (Exception e) { e.printStackTrace(); } return; } response.code(404).send("Not found!"); } });
我們又監(jiān)聽(tīng)了一個(gè)url為files/xxx.*,捕獲到之后,拿到文件名,去SDCard找到該文件,返回文件流即可。
html端的代碼為:
<script type="text/javascript"> $(function() { var now = new Date(); // 拿到video對(duì)象 var $video = $("#videoplayer"); var url = 'files' + '?' + now.getTime(); $.getJSON(url, function(data) { for (var i = 0; i < data.length; i++) { var $li = $('<li>' + data[i].name + '</li>'); $li.attr("path", data[i].path); $("#filelist").append($li); // 點(diǎn)擊的時(shí)候,獲取路徑,設(shè)置給video的src屬性 $li.click(function() { var p = "/files/" + $(this).attr("path"); $video.attr("src", "/files/" + $(this).attr("path")); $video[0].play(); }); } }); }); </script>
當(dāng)然頁(yè)面上body標(biāo)簽內(nèi)部也多了一個(gè)video標(biāo)簽。
<video id="videoplayer" controls="controls"> </video>
到這里,所以的代碼就介紹完了~~
小結(jié)
回頭看,其實(shí)就是app中啟動(dòng)服務(wù)器,監(jiān)聽(tīng)一些url,然后針對(duì)性的返回文本、json、文件流等。
當(dāng)然了,可以做的時(shí)候也挺多的,甚至可以做個(gè)PC版本的文件瀏覽器。
可能有很多人對(duì)html,js不太熟悉,不過(guò)還是建議簡(jiǎn)單了解下,或者敲一下本例,因?yàn)楸纠a很少,值得作為上手教程。
源碼地址:https://github.com/hongyangAndroid/demo_ShowPhoneMp4
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Android編程實(shí)現(xiàn)Toast自定義布局簡(jiǎn)單示例
這篇文章主要介紹了Android編程實(shí)現(xiàn)Toast自定義布局的方法,結(jié)合簡(jiǎn)單實(shí)例形式分析了Toast自定義布局的實(shí)現(xiàn)步驟與相關(guān)操作技巧,需要的朋友可以參考下2017-02-02android shape實(shí)現(xiàn)陰影或模糊邊效果
這篇文章主要介紹了android shape實(shí)現(xiàn)陰影或模糊邊效果,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-10-10ViewModel中StateFlow和SharedFlow單元測(cè)試使用詳解
這篇文章主要為大家介紹了ViewModel中StateFlow和SharedFlow單元測(cè)試使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01Android Canvas自定義實(shí)現(xiàn)時(shí)鐘效果
這篇文章主要為大家詳細(xì)介紹了Android Canvas自定義實(shí)現(xiàn)時(shí)鐘效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12Android AutoCompleteTextView自動(dòng)提示文本框?qū)嵗a
這篇文章主要介紹了Android AutoCompleteTextView自動(dòng)提示文本框?qū)嵗a的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下2016-07-07Android實(shí)現(xiàn)在map上畫出路線的方法
這篇文章主要介紹了Android實(shí)現(xiàn)在map上畫出路線的方法,較為詳細(xì)的分析了Android在map上繪制路線所涉及的map圖調(diào)用、畫筆的使用、頁(yè)面布局及權(quán)限控制的相關(guān)技巧,需要的朋友可以參考下2015-07-07使用Chrome瀏覽器調(diào)試Android App詳解
這篇文章主要介紹了使用Chrome瀏覽器調(diào)試Android App詳解,本網(wǎng)講解了使用Facebook開(kāi)源Stetho實(shí)現(xiàn)在Chrome中調(diào)試Android App中,需要的朋友可以參考下2015-05-05Android 接收推送消息跳轉(zhuǎn)到指定頁(yè)面的方法
這篇文章主要介紹了Android 接收推送消息跳轉(zhuǎn)到指定頁(yè)面的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01Android 多線程實(shí)現(xiàn)重復(fù)啟動(dòng)與停止的服務(wù)
這篇文章主要介紹了Android 多線程實(shí)現(xiàn)重復(fù)啟動(dòng)與停止的服務(wù)的相關(guān)資料,多線程環(huán)境下為了避免死鎖,一般提倡開(kāi)放調(diào)用,開(kāi)放調(diào)用可以避免死鎖,它的代價(jià)是失去原子性,這里說(shuō)明重復(fù)啟動(dòng)與停止的服務(wù),需要的朋友可以參考下2017-08-08