在 Android 設(shè)備上搭建 Web 服務(wù)器

一般而言,Android 應(yīng)用在請求數(shù)據(jù)時都是以 Get 或 Post 等方式向遠(yuǎn)程服務(wù)器發(fā)起請求锋叨,那你有沒有想過其實我們也可以在 Android 設(shè)備上搭建一個小型 Web 服務(wù)器娃磺,并且實現(xiàn)常規(guī)的下載圖片叫倍、下載文件、提交表單等功能呢听诸?
下面要介紹的就是如何在 Android 設(shè)備上搭建一個 Web 服務(wù)器,這個 Web 服務(wù)器的功能有如下幾點:

  1. 接受客戶端文件上傳桥嗤、下載文件
  2. 動態(tài) Http API泛领,像 Java 的 Servlet 一樣寫接口
  3. 部署靜態(tài)網(wǎng)站掌逛,例如純Html豆混,支持 JS、CSS员辩、Image 分離
  4. 部署動態(tài)網(wǎng)站

這需要依賴一個開源庫來實現(xiàn):AndServer

AndServer 類似于 Apache 和 Tomcat奠滑,支持在同個局域網(wǎng)下的設(shè)備能夠以常規(guī)的網(wǎng)絡(luò)請求方式來向 Web 服務(wù)器請求數(shù)據(jù)妒穴,只要指明 Web 服務(wù)器的 IP 地址和端口號即可

那么,這個 Web 服務(wù)器的用途有哪些呢杰赛?

說下我現(xiàn)在遇到的一個需求吧乏屯!需要實現(xiàn)兩臺設(shè)備(Android 或 ios 設(shè)備)在無網(wǎng)絡(luò)情況下進(jìn)行數(shù)據(jù)交流瘦赫。本來是打算讓設(shè)備之間的交流通道以 Wifi 來鏈接确虱,即某一臺設(shè)備連上另一臺設(shè)備的 Wiif 熱點,這樣兩者之間就建立起了一條“通道”窘问,之后通過建立 Socket 連接來獲取輸入輸出流,通過輸入輸出流來交換數(shù)據(jù)胸遇≈侥鳎可是這樣就需要處理好在高并發(fā)情況下的數(shù)據(jù)同步和解析問題概疆,比較麻煩岔冀,而通過 AndServer 就可以直接套用項目已有的網(wǎng)絡(luò)請求框架,直接以網(wǎng)絡(luò)請求的方式來交流數(shù)據(jù)罐呼,而服務(wù)端也較好的處理了并發(fā)問題

Gradle 遠(yuǎn)程依賴

implementation 'com.yanzhenjie:andserver:1.1.3'

搭建服務(wù)器

搭建服務(wù)器的方式很簡單嫉柴,支持鏈?zhǔn)秸{(diào)用奉呛。指明服務(wù)器在本機的 IP 地址上監(jiān)聽,并指定端口號為 1995 登馒,并開放了三個接口分別用于:下載文件陈轿、下載圖片罗晕、Post表單

       AndServer server = AndServer.serverBuilder()
                .inetAddress(NetUtils.getLocalIPAddress())  //服務(wù)器要監(jiān)聽的網(wǎng)絡(luò)地址
                .port(Constants.PORT_SERVER) //服務(wù)器要監(jiān)聽的端口
                .timeout(10, TimeUnit.SECONDS) //Socket超時時間
                .registerHandler(Constants.GET_FILE, new DownloadFileHandler()) //注冊一個文件下載接口
                .registerHandler(Constants.GET_IMAGE, new DownloadImageHandler()) //注冊一個圖片下載接口
                .registerHandler(Constants.POST_JSON, new JsonHandler()) //注冊一個Post Json接口
                .filter(new HttpCacheFilter()) //開啟緩存支持
                .listener(new Server.ServerListener() {  //服務(wù)器監(jiān)聽接口
                    @Override
                    public void onStarted() {
                        String hostAddress = server.getInetAddress().getHostAddress();
                        Log.e(TAG, "onStarted : " + hostAddress);
                        ServerPresenter.onServerStarted(ServerService.this, hostAddress);
                    }

                    @Override
                    public void onStopped() {
                        Log.e(TAG, "onStopped");
                        ServerPresenter.onServerStopped(ServerService.this);
                    }

                    @Override
                    public void onError(Exception e) {
                        Log.e(TAG, "onError : " + e.getMessage());
                        ServerPresenter.onServerError(ServerService.this, e.getMessage());
                    }
                })
                .build();

開啟服務(wù)器

server.startup();

停止服務(wù)器

server.shutdown();

接口處理器

在注冊接口時小渊,除了指明開放出來的 Url 地址外酬屉,還需要指明相應(yīng)的處理器呐萨,專門用于處理該接口的請求操作
開放出來的三個接口分別對應(yīng)于三個地址

public class Constants {

    //服務(wù)端接口的端口號
    public static final int PORT_SERVER = 1995;

    public static final String GET_FILE = "/file";

    public static final String GET_IMAGE = "/image";

    public static final String POST_JSON = "/json";

}
 ···
 .registerHandler(Constants.GET_FILE, new DownloadFileHandler()) //注冊一個文件下載接口
 .registerHandler(Constants.GET_IMAGE, new DownloadImageHandler()) //注冊一個圖片下載接口
 .registerHandler(Constants.POST_JSON, new JsonHandler()) //注冊一個Post Json接口
 ···

例如谬擦,假設(shè)設(shè)備的 IP 地址是:192.168.0.101 ,那么在訪問 http://192.168.0.101:1995/file 接口時谜悟,請求操作就會由 DownloadFileHandler 來處理

下載文件

DownloadFileHandler 實現(xiàn)了 RequestHandler 接口葡幸,在 handle 方法中可以獲取到請求頭贺氓,表單數(shù)據(jù)這些信息,辙培,通過注解聲明支持 Get 方式調(diào)用,在此直接返回一個文本文件用于下載

/**
 * 作者:leavesC
 * 時間:2018/4/5 16:30
 * 描述:https://github.com/leavesC/AndroidServer
 * http://www.reibang.com/u/9df45b87cfdf
 */
public class DownloadFileHandler implements RequestHandler {

    @RequestMapping(method = {RequestMethod.GET})
    @Override
    public void handle(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) throws HttpException, IOException {
        File file = createFile();
        HttpEntity httpEntity = new FileEntity(file, ContentType.create(getMimeType(file.getAbsolutePath()), Charset.defaultCharset()));
        httpResponse.setHeader("Content-Disposition", "attachment;filename=File.txt");
        httpResponse.setStatusCode(200);
        httpResponse.setEntity(httpEntity);
    }

    private File createFile() {
        File file = null;
        OutputStream outputStream = null;
        try {
            file = File.createTempFile("File", ".txt", MainApplication.get().getCacheDir());
            outputStream = new FileOutputStream(file);
            outputStream.write("leavesC肤粱,這是一段測試文本".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.flush();
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return file;
    }

}

這里直接在瀏覽器中訪問接口(要和 Android Web服務(wù)器運行在同個局域網(wǎng)下)领曼,可以直接下載到文件


下載圖片

類似的庶骄,下載圖片的接口處理器 DownloadImageHandler 可以如下設(shè)計单刁,在 handle 方法中返回應(yīng)用的圖標(biāo)

/**
 * 作者:leavesC
 * 時間:2018/4/5 16:30
 * 描述:https://github.com/leavesC/AndroidServer
 * http://www.reibang.com/u/9df45b87cfdf
 */
public class DownloadImageHandler extends SimpleRequestHandler {

    private File file = new File(Environment.getExternalStorageDirectory(), "leavesC.jpg");

    @RequestMapping(method = {RequestMethod.GET})
    @Override
    protected View handle(HttpRequest request) throws HttpException, IOException {
        writeToSdCard();
        HttpEntity httpEntity = new FileEntity(file, ContentType.create(getMimeType(file.getAbsolutePath()), Charset.defaultCharset()));
        return new View(200, httpEntity);
    }

    private void writeToSdCard() throws IOException {
        if (!file.exists()) {
            synchronized (DownloadImageHandler.class) {
                if (!file.exists()) {
                    boolean b = file.createNewFile();
                    if (!b) {
                        throw new IOException("What broken cell phone.");
                    }
                    Bitmap bitmap = BitmapFactory.decodeResource(MainApplication.get().getResources(), R.mipmap.ic_launcher_round);
                    OutputStream outputStream = null;
                    try {
                        outputStream = new FileOutputStream(file);
                        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                    } finally {
                        if (outputStream != null) {
                            outputStream.flush();
                            outputStream.close();
                        }
                    }
                }
            }
        }
    }

}
Post表單

這里需要將注解值改為 RequestMethod.POST,通過 HttpRequestParser.getContentFromBody(httpRequest) 函數(shù)可以獲取到表單數(shù)據(jù)檐春,這里直接檢測表單數(shù)據(jù)是否為 Json 字符串疟暖,是的話則為之多添加一個屬性 :"state" 作為返回值田柔,否則返回只包含屬性 “state” 的 Json 字符串

/**
 * 作者:leavesC
 * 時間:2018/4/5 16:30
 * 描述:https://github.com/leavesC/AndroidServer
 * http://www.reibang.com/u/9df45b87cfdf
 */
public class JsonHandler implements RequestHandler {
    
    @RequestMapping(method = {RequestMethod.POST})
    @Override
    public void handle(HttpRequest httpRequest, HttpResponse httpResponse, HttpContext httpContext) throws HttpException, IOException {
        String content = HttpRequestParser.getContentFromBody(httpRequest);
        JSONObject jsonObject = null;
        try {
            jsonObject = new JSONObject(content);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        if (jsonObject == null) {
            jsonObject = new JSONObject();
        }
        try {
            jsonObject.put("state", "success");
        } catch (JSONException e) {
            e.printStackTrace();
        }
        StringEntity stringEntity = new StringEntity(jsonObject.toString(), "utf-8");
        httpResponse.setStatusCode(200);
        httpResponse.setEntity(stringEntity);
    }

}

這里在 Postman 這個工具上進(jìn)行 Post 操作


以上三個例子都是在電腦端調(diào)用的,這和在手機端調(diào)用是同個效果的

基本的操作就介紹到這里擎鸠,再具體的內(nèi)容可以看示例代碼:AndroidServer

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末劣光,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子廷没,更是在濱河造成了極大的恐慌垂寥,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狭归,死亡現(xiàn)場離奇詭異过椎,居然都是意外死亡戏仓,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進(jìn)店門敷待,熙熙樓的掌柜王于貴愁眉苦臉地迎上來榜揖,“玉大人抗蠢,你說我怎么就攤上這事》列桑” “怎么了诬乞?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵震嫉,是天一觀的道長。 經(jīng)常有香客問我票堵,道長悴势,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任军俊,我火速辦了婚禮捧存,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘镰官。我一直安慰自己吗货,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布笨腥。 她就那樣靜靜地躺著扇雕,像睡著了一般窥摄。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上哨苛,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天建峭,我揣著相機與錄音,去河邊找鬼亿蒸。 笑死,一個胖子當(dāng)著我的面吹牛边锁,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播音半,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼贡蓖,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了斥铺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤叛薯,失蹤者是張志新(化名)和其女友劉穎笙纤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體抖拴,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡阿宅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年洒放,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片往湿。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡领追,死狀恐怖响逢,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情舔亭,我是刑警寧澤蟀俊,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布肢预,位于F島的核電站葬燎,受9級特大地震影響缚甩,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜擅威,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一郊丛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧导盅,春花似錦、人聲如沸白翻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽巢株。三九已至熙涤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間猬错,已是汗流浹背茸歧。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留软瞎,地道東北人拉讯。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓魔慷,卻偏偏與公主長得像著恩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子邀摆,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

推薦閱讀更多精彩內(nèi)容