
在安卓开发的广阔天地里,网络交互是一个绕不开的重要环节。无论是搭建服务端接收外部请求,还是获取设备 IP 以便在局域网内通信,都蕴含着许多实用的技术与技巧。今天,就和大家聊聊这些在安卓开发中与网络相关的关键知识点。
在安卓中搭建服务端,Jetty 是一个值得推荐的选择。Jetty 是一款功能强大且灵活的 HTTP 服务器,它能帮助我们在安卓应用中轻松处理网络请求和响应。
首先,我们需要在项目中添加 Jetty 的依赖。如果你使用 Gradle 构建项目,只需在build.gradle文件中添加以下代码:
// Jetty服务器依赖 - 使用较旧版本以兼容Android 5.1
implementation("org.eclipse.jetty:jetty-server:9.2.30.v20200428") {
exclude(group = "com.ibm.icu", module = "icu4j")
}
implementation("org.eclipse.jetty:jetty-servlet:9.2.30.v20200428")
implementation("org.eclipse.jetty:jetty-util:9.2.30.v20200428") {
exclude(group = "com.ibm.icu", module = "icu4j")
}这里我用的版本比较老,因为要考虑兼容Android 5.1系统。 exclude(group = "com.ibm.icu", module = "icu4j") 是为了解决中文乱码问题
添加好依赖后,就可以在代码中创建并启动 Jetty 服务器了。以下是一段简单的示例代码:
/**
* Jetty服务器管理类
* 用于提供HTTP API接口,允许外部应用访问用户管理数据
*/
class JettyServer private constructor(private val context: Context) {
private val TAG = "JettyServer"
private var server: Server? = null
private val gson = Gson()
private val prefsManager = AppPreferencesManager.getInstance(context)
private val executor = Executors.newSingleThreadExecutor()
companion object {
private var instance: JettyServer? = null
private const val DEFAULT_PORT = 8888
fun getInstance(context: Context): JettyServer {
return instance ?: synchronized(this) {
instance ?: JettyServer(context.applicationContext).also { instance = it }
}
}
}
/**
* 启动Jetty服务器
*/
fun start(port: Int = DEFAULT_PORT) {
executor.execute {
try {
if (server?.isRunning == true) {
Log.i(TAG, "服务器已经在运行中")
return@execute
}
server = Server(port).apply {
handler = createServletHandler()
start()
}
Log.i(TAG, "Jetty服务器已启动,端口:$port")
} catch (e: Exception) {
Log.e(TAG, "启动服务器失败: ${e.message}")
}
}
}
/**
* 停止Jetty服务器
*/
fun stop() {
executor.execute {
try {
server?.stop()
server = null
Log.i(TAG, "Jetty服务器已停止")
} catch (e: Exception) {
Log.e(TAG, "停止服务器失败: ${e.message}")
}
}
}
/**
* 创建Servlet处理器
*/
private fun createServletHandler(): ServletContextHandler {
return ServletContextHandler().apply {
contextPath = "/"
// 服务器状态API
addServlet(ServletHolder(ServerStatusServlet()), "/api/status")
// 用户管理API
addServlet(ServletHolder(UserListServlet()), "/api/users")
addServlet(ServletHolder(UserDetailServlet()), "/api/user/*")
addServlet(ServletHolder(AddUserServlet()), "/api/user/add")
addServlet(ServletHolder(UpdateUserServlet()), "/api/user/update")
addServlet(ServletHolder(DeleteUserServlet()), "/api/user/delete")
}
}
/**
* 基础Servlet类,提供通用功能
*/
private abstract inner class BaseServlet : HttpServlet() {
fun <T> sendJsonResponse(
response: HttpServletResponse,
data: T,
message: String = "操作成功"
) {
response.contentType = "application/json;charset=UTF-8"
response.characterEncoding = "UTF-8"
response.status = HttpServletResponse.SC_OK
val apiResponse = ApiResponse.success(data, message)
response.writer.write(gson.toJson(apiResponse))
}
fun sendErrorResponse(response: HttpServletResponse, status: Int, message: String) {
response.contentType = "application/json;charset=UTF-8"
response.characterEncoding = "UTF-8"
response.status = status
val apiResponse = ApiResponse.errorFromHttp<Any>(status, message)
response.writer.write(gson.toJson(apiResponse))
}
fun getRequestBody(request: HttpServletRequest): String {
// 使用标准Java的编码处理,避免使用ICU实现
val inputStream = request.inputStream
val bytes = inputStream.readBytes()
return String(bytes, StandardCharsets.UTF_8)
}
}
}这段代码创建了一个监听 8888端口的 Jetty 服务器。不过,在安卓应用中使用时,千万别忘了在AndroidManifest.xml文件中添加网络权限:
<uses-permission android:name="android.permission.INTERNET" />这样,一个基础的 Jetty 服务端就在安卓应用中搭建起来了。后续,我们可以进一步完善它,处理各种请求和响应逻辑。
获取安卓应用的局域网 IP 地址,是实现局域网内设备通信的关键一步。我们可以通过以下代码来获取:
package cn.harry.smartcabinet.util
import android.util.Log
import java.net.NetworkInterface
/**
* IP地址工具类
*/
object IpUtil {
private const val TAG = "IpUtil"
/**
* 获取本地IP地址
* @return 本地IP地址,如果获取失败则返回null
*/
fun getLocalIpAddress(): String? {
try {
val networkInterfaces = NetworkInterface.getNetworkInterfaces()
while (networkInterfaces.hasMoreElements()) {
val networkInterface = networkInterfaces.nextElement()
// 跳过回环接口、虚拟接口等
if (networkInterface.isLoopback || !networkInterface.isUp) {
continue
}
val addresses = networkInterface.inetAddresses
while (addresses.hasMoreElements()) {
val address = addresses.nextElement()
// 跳过IPv6地址和回环地址
if (address.isLoopbackAddress || address.hostAddress.contains(":")) {
continue
}
val ip = address.hostAddress
Log.d(TAG, "找到IP地址: $ip")
// 优先返回非保留地址(通常是外网地址)
if (!isReservedAddress(ip)) {
return ip
}
}
}
// 如果没有找到非保留地址,再次遍历查找任何可用的IPv4地址
val networkInterfaces2 = NetworkInterface.getNetworkInterfaces()
while (networkInterfaces2.hasMoreElements()) {
val networkInterface = networkInterfaces2.nextElement()
if (!networkInterface.isUp) continue
val addresses = networkInterface.inetAddresses
while (addresses.hasMoreElements()) {
val address = addresses.nextElement()
if (address.isLoopbackAddress || address.hostAddress.contains(":")) {
continue
}
return address.hostAddress
}
}
} catch (e: Exception) {
Log.e(TAG, "获取IP地址失败: ${e.message}")
}
return null
}
/**
* 判断IP地址是否为保留地址
* @param ip IP地址
* @return 如果是保留地址则返回true,否则返回false
*/
private fun isReservedAddress(ip: String): Boolean {
return ip.startsWith("10.") ||
ip.startsWith("172.16.") ||
ip.startsWith("192.168.") ||
ip.startsWith("127.")
}
}这段代码通过遍历设备上的网络接口,筛选出可用的局域网 IP 地址并返回。需要注意的是,在安卓 10 及以上的系统中,由于权限管理更加严格,部分获取 IP 的方式可能受到限制,开发者可能需要采用更复杂的方式,如通过网络请求去查询。
当我们获取到安卓应用的局域网 IP 地址,并在服务端设置好监听端口后,就可以在同一局域网内,使用其他设备发起请求与安卓应用进行通信了。不过,在进行通信之前,还有一个容易被忽视的问题 —— 防火墙设置。
安卓设备虽然没有像电脑一样专门的防火墙设置入口,但部分手机厂商会在系统设置中集成类似功能。我们可以在手机的 “设置” 中,查找 “网络与互联网” 或 “安全” 相关选项,部分手机还会有 “流量管理”“应用联网权限” 等设置,在这里可以管理应用的网络访问权限,起到类似防火墙的作用。此外,一些第三方安全软件也提供防火墙功能,可在软件设置中进行详细配置。
掌握了这些知识,我们在安卓开发中进行网络交互时就能更加得心应手。无论是搭建服务端实现数据交互,还是获取 IP 地址完成局域网通信,每一个细节都可能影响到整个应用的网络功能体验。希望本文能对大家有所帮助,让我们在安卓开发的网络世界中不断探索,创造出更出色的应用!