一般而言,Android 应用在请求数据时都是以 Get 或 Post 等方式向远程服务器发起请求,那你有没有想过其实我们也可以在 Android 设备上搭建一个小型 Web 服务器,并且实现常规的下载图片、下载文件、提交表单等功能呢?
下面要介绍的就是如何在 Android 设备上搭建一个 Web 服务器,这个 Web 服务器的功能有如下几点:

  1. 接受客户端文件上传、下载文件
  2. 动态 Http API,像 Java 的 Servlet 一样写接口
  3. 部署静态网站,例如纯Html,支持 JS、CSS、Image 分离
  4. 部署动态网站

这需要依赖一个开源库来实现:AndServer

AndServer 类似于 Apache 和 Tomcat,支持在同个局域网下的设备能够以常规的网络请求方式来向 Web 服务器请求数据,只要指明 Web 服务器的 IP 地址和端口号即可

那么,这个 Web 服务器的用途有哪些呢?

说下我现在遇到的一个需求吧!需要实现两台设备(Android 或 ios 设备)在无网络情况下进行数据交流。本来是打算让设备之间的交流通道以 Wifi 来链接,即某一台设备连上另一台设备的 Wiif 热点,这样两者之间就建立起了一条“通道”,之后通过建立 Socket 连接来获取输入输出流,通过输入输出流来交换数据。可是这样就需要处理好在高并发情况下的数据同步和解析问题,比较麻烦,而通过 AndServer 就可以直接套用项目已有的网络请求框架,直接以网络请求的方式来交流数据,而服务端也较好的处理了并发问题

Gradle 远程依赖

implementation 'com.yanzhenjie:andserver:1.1.3'

搭建服务器

搭建服务器的方式很简单,支持链式调用。指明服务器在本机的 IP 地址上监听,并指定端口号为 1995 ,并开放了三个接口分别用于:下载文件、下载图片、Post表单

       AndServer server = AndServer.serverBuilder()                .inetAddress(NetUtils.getLocalIPAddress())  //服务器要监听的网络地址                .port(Constants.PORT_SERVER) //服务器要监听的端口                .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() {  //服务器监听接口                    @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();

开启服务器

server.startup();

停止服务器

server.shutdown();

接口处理器

在注册接口时,除了指明开放出来的 Url 地址外,还需要指明相应的处理器,专门用于处理该接口的请求操作
开放出来的三个接口分别对应于三个地址

public class Constants {    //服务端接口的端口号    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接口 ···

例如,假设设备的 IP 地址是:192.168.0.101 ,那么在访问 http://192.168.0.101:1995/file 接口时,请求操作就会由 DownloadFileHandler 来处理

下载文件

DownloadFileHandler 实现了 RequestHandler 接口,在 handle 方法中可以获取到请求头,表单数据这些信息,,通过注解声明支持 Get 方式调用,在此直接返回一个文本文件用于下载

/** * 作者:leavesC * 时间:2018/4/5 16:30 * 描述:https://github.com/leavesC/AndroidServer * https://www.jianshu.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服务器运行在同个局域网下),可以直接下载到文件


下载图片

类似的,下载图片的接口处理器 DownloadImageHandler 可以如下设计,在 handle 方法中返回应用的图标

/** * 作者:leavesC * 时间:2018/4/5 16:30 * 描述:https://github.com/leavesC/AndroidServer * https://www.jianshu.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) 函数可以获取到表单数据,这里直接检测表单数据是否为 Json 字符串,是的话则为之多添加一个属性 :"state" 作为返回值,否则返回只包含属性 “state” 的 Json 字符串

/** * 作者:leavesC * 时间:2018/4/5 16:30 * 描述:https://github.com/leavesC/AndroidServer * https://www.jianshu.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 这个工具上进行 Post 操作


以上三个例子都是在电脑端调用的,这和在手机端调用是同个效果的

基本的操作就介绍到这里,再具体的内容可以看示例代码:AndroidServer

欢迎关注我的账号:叶应是叶

更多相关文章

  1. android进程间服务通信
  2. Android客户端采用Http 协议Post方式请求与服务端进行数据交互
  3. 程序的组件模式
  4. Android客户端与服务器交互方式(1)
  5. Android从服务端获取json解析显示在客户端上面
  6. android网络连接使用GET方式请求服务器时的setDoOutput(true)惹
  7. GalHttprequest类库简介——android平台上的一个轻量级的http网
  8. android Camera模块分析
  9. android上传图片至服务器

随机推荐

  1. Android(安卓)跳转应用市场的应用详情页
  2. android 图片之多点触控放大缩小
  3. Android屏幕方向及键盘状态
  4. HTTP 工具类 封装 For android
  5. android技术博客汇总
  6. android 写入data/data/包名/file/中
  7. MySQL对window函数执行sum函数可能出现的
  8. MySQL如何使用授权命令grant
  9. 一篇文章掌握MySQL的索引查询优化技巧
  10. 浅析mysql 定时备份任务