Android项目嵌入可脚本化嵌入式Web服务器的实现问询
我之前在Android项目里用NanoHTTPD实现过完全一样的需求——静态文件服务加自定义JSON API,给你分享个完整的实现示例,应该能直接复用或者参考调整。
使用NanoHTTPD实现静态文件服务+自定义API
首先,你需要在项目里引入NanoHTTPD的依赖(比如在Gradle里添加implementation 'org.nanohttpd:nanohttpd:2.3.1')。然后创建一个自定义的服务器类,继承NanoHTTPD,重写serve方法来区分静态文件请求和API请求:
import org.nanohttpd.NanoHTTPD; import org.nanohttpd.protocols.http.response.Response; import org.nanohttpd.protocols.http.response.Status; import org.nanohttpd.protocols.http.IHTTPSession; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class MyWebServer extends NanoHTTPD { private static final String API_PREFIX = "/actions/"; private final File staticFilesDir; public MyWebServer(int port, File staticFilesDir) { super(port); this.staticFilesDir = staticFilesDir; } @Override public Response serve(IHTTPSession session) { String uri = session.getUri(); Method method = session.getMethod(); // 优先处理API请求 if (uri.startsWith(API_PREFIX)) { return handleApiRequest(session, uri.substring(API_PREFIX.length())); } // 处理静态文件请求(比如/img/example.png、/assets/style.css) return handleStaticFileRequest(uri); } private Response handleApiRequest(IHTTPSession session, String action) { Method method = session.getMethod(); // 处理POST请求到/actions/do_something if ("do_something".equals(action) && Method.POST.equals(method)) { try { // 解析POST请求体(如果是JSON格式,直接读取postData) Map<String, String> params = new HashMap<>(); session.parseBody(params); String postJson = params.get("postData"); // 调用你的自定义业务处理函数 String responseJson = processDoSomething(postJson); // 返回JSON格式的响应 return newFixedLengthResponse(Status.OK, "application/json", responseJson); } catch (IOException | ResponseException e) { return newFixedLengthResponse(Status.INTERNAL_ERROR, "text/plain", "请求处理失败"); } } // 处理不存在的API端点 return newFixedLengthResponse(Status.NOT_FOUND, "text/plain", "API端点不存在"); } // 这里是你的自定义业务逻辑,根据传入的JSON做处理后返回结果 private String processDoSomething(String postJson) { // 示例:解析传入的JSON,处理后返回成功响应 // 实际开发中可以用Gson、Jackson等库来解析和生成JSON return "{\"status\":\"success\",\"message\":\"已处理请求\",\"received_data\":\"" + postJson + "\"}"; } private Response handleStaticFileRequest(String uri) { // 根路径默认返回index.html if ("/".equals(uri)) { uri = "/index.html"; } File targetFile = new File(staticFilesDir, uri); // 检查文件是否存在且是普通文件 if (!targetFile.exists() || !targetFile.isFile()) { return newFixedLengthResponse(Status.NOT_FOUND, "text/plain", "文件不存在"); } // 根据文件后缀设置正确的Content-Type String mimeType = getMimeType(targetFile.getName()); try { // 返回文件流作为响应 return newChunkedResponse(Status.OK, mimeType, new FileInputStream(targetFile)); } catch (IOException e) { return newFixedLengthResponse(Status.INTERNAL_ERROR, "text/plain", "读取文件失败"); } } // 简单的MIME类型映射,可根据需要扩展 private String getMimeType(String fileName) { if (fileName.endsWith(".html") || fileName.endsWith(".htm")) { return "text/html"; } else if (fileName.endsWith(".css")) { return "text/css"; } else if (fileName.endsWith(".js")) { return "application/javascript"; } else if (fileName.endsWith(".png")) { return "image/png"; } else if (fileName.endsWith(".jpg") || fileName.endsWith(".jpeg")) { return "image/jpeg"; } // 默认二进制流 return "application/octet-stream"; } }
在Android中启动服务器
因为Android的assets目录不能直接用File对象访问,所以需要先把静态文件(比如html、css、图片)复制到应用的内部存储,然后启动服务器:
import android.os.Bundle; import android.util.Log; import androidx.appcompat.app.AppCompatActivity; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; public class MainActivity extends AppCompatActivity { private static final String TAG = "WebServerDemo"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 先把assets里的静态文件复制到内部存储 copyAssetsToInternalStorage(); // 启动服务器,端口设为8080 startWebServer(); } private void copyAssetsToInternalStorage() { try { String[] assetFiles = getAssets().list(""); File staticDir = new File(getFilesDir(), "web_static"); if (!staticDir.exists()) { boolean created = staticDir.mkdirs(); if (!created) { Log.e(TAG, "无法创建静态文件目录"); return; } } for (String fileName : assetFiles) { InputStream in = getAssets().open(fileName); File outFile = new File(staticDir, fileName); OutputStream out = new FileOutputStream(outFile); byte[] buffer = new byte[1024]; int read; while ((read = in.read(buffer)) != -1) { out.write(buffer, 0, read); } in.close(); out.close(); } Log.d(TAG, "静态文件复制完成"); } catch (IOException e) { Log.e(TAG, "复制静态文件失败", e); } } private void startWebServer() { File staticDir = new File(getFilesDir(), "web_static"); MyWebServer server = new MyWebServer(8080, staticDir); try { server.start(); Log.d(TAG, "服务器启动成功,端口:8080"); Log.d(TAG, "访问地址:http://" + getDeviceIp() + ":8080"); } catch (IOException e) { Log.e(TAG, "服务器启动失败", e); } } // 辅助方法:获取设备的局域网IP private String getDeviceIp() { // 实现获取设备IP的逻辑,这里省略具体代码,你可以网上找现成的实现 return "192.168.x.x"; } }
必要的权限配置
在AndroidManifest.xml里添加网络权限,并且如果你的应用针对Android 9及以上,需要允许明文HTTP请求:
<uses-permission android:name="android.permission.INTERNET" /> <application ... android:usesCleartextTraffic="true"> ... </application>
其他可选的嵌入式Web服务器
如果NanoHTTPD不符合你的需求,还有这些不错的选择:
- Ktor Server:JetBrains出品的轻量级服务器,支持Kotlin和Java,API设计现代化,自带JSON处理、路由管理等功能,适合复杂的API场景,在Android上运行稳定。
- Spark Java:另一个轻量级Java服务器,路由配置非常简洁,支持静态文件服务和自定义API,学习成本低,适合快速开发小型服务。
- Android SimpleWebServer:Android 10及以上系统自带的API,不需要引入第三方库,功能相对基础,适合只需要简单静态文件服务的场景。
内容的提问来源于stack exchange,提问作者David Refoua




