前言
遇到需求,需要在APP中部署一个服务器,局域网中通过web来访问这个APP的功能,相当于在APP中部署一个前后端项目。
后端我选择了ktor作为开发框架,基于kotlin,写协程挂起函数方便。
添加依赖
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
}
android {
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
coreLibraryDesugaringEnabled true
}
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
// 当编译时出现报错,按情况添加
excludes += "/META-INF/INDEX.LIST"
excludes += "/META-INF/io.netty.versions.properties"
}
}
}
dependencies {
// Java 8
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
// 协程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.0"
// ktor
def ktorVersion = '1.6.7'
implementation "io.ktor:ktor-server-core:$ktorVersion"
implementation "io.ktor:ktor-server-netty:$ktorVersion"
testImplementation("io.ktor:ktor-server-tests:$ktorVersion")
implementation 'ch.qos.logback:logback-classic:1.2.11'
}
编译的时候,我这边报了以下错误:
10 files found with path ‘META-INF/INDEX.LIST’.
Adding a packagingOptions block may help, please refer to
https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.PackagingOptions.html
for more information
11 files found with path ‘META-INF/io.netty.versions.properties’.
Adding a packagingOptions block may help, please refer to
https://google.github.io/android-gradle-dsl/current/com.android.build.gradle.internal.dsl.PackagingOptions.html
for more information
将提示的文件路径填写到build.gradle的Android里
android {
packagingOptions {
resources {
excludes += '/META-INF/{AL2.0,LGPL2.1}'
// 当编译时出现报错,按情况添加
excludes += "/META-INF/INDEX.LIST"
excludes += "/META-INF/io.netty.versions.properties"
}
}
}
服务器管理
新建KtorServer.kt
import android.os.Build
import io.ktor.application.*
import io.ktor.features.*
import io.ktor.http.*
import io.ktor.response.*
import io.ktor.routing.*
import io.ktor.server.engine.*
import io.ktor.server.netty.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
object KtorServer {
private val server by lazy {
embeddedServer(Netty, 12345) {
install(CallLogging)
// 跨域访问
install(CORS) {
anyHost()
header(HttpHeaders.ContentType)
method(HttpMethod.Options)
method(HttpMethod.Put)
method(HttpMethod.Patch)
method(HttpMethod.Delete)
}
routing {
get("/") {
call.respondText("手机型号 ${Build.MODEL} 运行正常", ContentType.Text.Plain)
}
}
}
}
/** 启动服务器 */
fun start() {
CoroutineScope(Dispatchers.IO).launch { server.start(true) }
}
/** 停止服务器 */
fun stop() {
server.stop(1_000, 2_000)
}
}
活动
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 启动服务器
KtorServer.start()
setContent {
AndroidKtorServerDemoTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colors.background
) {
Greeting(localIPAddress?: "0.0.0.0")
}
}
}
}
override fun onDestroy() {
// 结束服务器
KtorServer.stop()
super.onDestroy()
}
}
@Composable
fun Greeting(ip: String) {
Text(text = "请打开局域网中的任意浏览器访问 Http://$ip:12345")
}
效果
DEMO
https://github.com/D10NGYANG/AndroidKtorServerDemo