构建一个可以 运行 在任何地方、桌面和移动设备的服务器
Building a server that can run every where, desktop and mobile
我的笔记本电脑 (Mac/Windows/Linux) 上 运行ning 有以下简单的 go 服务器:
package main
import (
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
log.Println(http.ListenAndServe("localhost:6060", nil))
}
我可以在移动 webview
上使用与 运行 我的应用程序相同的代码库,而不使用 gomobile 或其他软件包,所以我的代码是通用应用程序吗?
答案是“是”,但需要对文件本身进行一些细微的修改。
- 删除
func main() {}
中的所有内容,因为我们会将最终结果构建为共享库,而不是可执行二进制文件。
- 运行
//export
函数中的服务器。
- 运行 来自
anonymous goroutine
的服务器为 go func() {}()
,因此它不会阻塞移动应用程序的主线程。
- 为了保持服务器 goroutine 运行,我们需要使用一个通道作为
<-c
来防止 goroutine 退出。
- 使用
cgo
加上import "C"
,所以主文件变成这样:
package main
import "C"
// other imports should be seperated from the special Cgo import
import (
"fmt"
"log"
"net/http"
)
//export server
func server() {
c := make(chan bool)
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
<-c
}()
http.HandleFunc("/", handler)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there %s!", r.URL.Path[1:])
}
func main() {}
- 确保安装Android
NDK
,你知道它的浴室
- 构建
c-shared
输出,输出名称为 libxxx
,为 Android
构建使用:
CGO_ENABLED=1 \
GOOS=android \
GOARCH=arm \
GOARM=7 \
CC=$(NDK_BIN)/armv7a-linux-androideabi21-clang \
go build -buildmode=c-shared -o libfoo.so http.go
等等 由于android有多种架构,我们需要为每一个单独编译,所以我们可以在Makefile
中让所有过程自动化如下图在通过从项目模板中选择Native C++
创建android应用程序后,下面的输出库名称是libfoo
并且将生成2个文件在每个文件夹中 libfoo.so
和 libfoo.h
:
#Filename: Makefile
# To compile run:
# make android
IOS_OUT=lib/ios
ANDROID_OUT=../android_app/app/src/main/jniLibs
ANDROID_SDK=$(HOME)/Library/Android/sdk
NDK_BIN=$(ANDROID_SDK)/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin
android-armv7a:
CGO_ENABLED=1 \
GOOS=android \
GOARCH=arm \
GOARM=7 \
CC=$(NDK_BIN)/armv7a-linux-androideabi21-clang \
go build -buildmode=c-shared -o $(ANDROID_OUT)/armeabi-v7a/libfoo.so ./cmd/libfoo
android-arm64:
CGO_ENABLED=1 \
GOOS=android \
GOARCH=arm64 \
CC=$(NDK_BIN)/aarch64-linux-android21-clang \
go build -buildmode=c-shared -o $(ANDROID_OUT)/arm64-v8a/libfoo.so ./cmd/libfoo
android-x86:
CGO_ENABLED=1 \
GOOS=android \
GOARCH=386 \
CC=$(NDK_BIN)/i686-linux-android21-clang \
go build -buildmode=c-shared -o $(ANDROID_OUT)/x86/libfoo.so ./cmd/libfoo
android-x86_64:
CGO_ENABLED=1 \
GOOS=android \
GOARCH=amd64 \
CC=$(NDK_BIN)/x86_64-linux-android21-clang \
go build -buildmode=c-shared -o $(ANDROID_OUT)/x86_64/libfoo.so ./cmd/libfoo
android: android-armv7a android-arm64 android-x86 android-x86_64
- 转到
android_app/app/src/main/cpp
并执行以下操作:
8.1.文件 CMakeLists.txt
,将其设为:
cmake_minimum_required(VERSION 3.10.2)
project("android")
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp )
add_library(lib_foo SHARED IMPORTED)
set_property(TARGET lib_foo PROPERTY IMPORTED_NO_SONAME 1)
set_target_properties(lib_foo PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libfoo.so)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
native-lib
lib_foo
# Links the target library to the log library
# included in the NDK.
${log-lib} )
8.2。文件 native-lib.cpp
将其设为:
#include <jni.h>
#include <string>
#include "libfoo.h" // our library header
extern "C" {
void
Java_tk_android_MainActivity_serverJNI() {
// Running the server
server();
}
}
- 将 webview 添加到
layout/activity_main
,如:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<WebView
android:id="@+id/wv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:isScrollContainer="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- 更新
MainActivity
如下:
package tk.android
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var wv = findViewById<WebView>(R.id.web_view)
serverJNI()
wv.loadUrl("http://127.0.0.1:6060/")
wv.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(viewx: WebView, urlx: String): Boolean {
viewx.loadUrl(urlx)
return false
}
}
}
private external fun serverJNI(): Void
companion object {
// Used to load the 'native-lib' library on application startup.
init {
System.loadLibrary("native-lib")
}
}
}
- 更新
AndroidManifest
为:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="tk.android">
<!-- Mandatory:
android:usesCleartextTraffic="true"
Optional:
android:hardwareAccelerated="true"
Depending on the action bar required:
android:theme="@style/Theme.AppCompat.NoActionBar"
-->
<application
android:hardwareAccelerated="true" // <- Optional
android:usesCleartextTraffic="true" // <- A must to be added
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.NoActionBar"> // <- If do not want action bar
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize"> // <- A must to avoid crashing at rotation
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
奖金
使用 Go embed
,所有静态文件都可以嵌入到同一个库中,包括 css
、javascript
、templates
,因此您可以构建 API 或完整的应用程序带 GUI
我的笔记本电脑 (Mac/Windows/Linux) 上 运行ning 有以下简单的 go 服务器:
package main
import (
"fmt"
"log"
"net/http"
)
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there %s!", r.URL.Path[1:])
}
func main() {
http.HandleFunc("/", handler)
log.Println(http.ListenAndServe("localhost:6060", nil))
}
我可以在移动 webview
上使用与 运行 我的应用程序相同的代码库,而不使用 gomobile 或其他软件包,所以我的代码是通用应用程序吗?
答案是“是”,但需要对文件本身进行一些细微的修改。
- 删除
func main() {}
中的所有内容,因为我们会将最终结果构建为共享库,而不是可执行二进制文件。 - 运行
//export
函数中的服务器。 - 运行 来自
anonymous goroutine
的服务器为go func() {}()
,因此它不会阻塞移动应用程序的主线程。 - 为了保持服务器 goroutine 运行,我们需要使用一个通道作为
<-c
来防止 goroutine 退出。 - 使用
cgo
加上import "C"
,所以主文件变成这样:
package main
import "C"
// other imports should be seperated from the special Cgo import
import (
"fmt"
"log"
"net/http"
)
//export server
func server() {
c := make(chan bool)
go func() {
log.Println(http.ListenAndServe("localhost:6060", nil))
<-c
}()
http.HandleFunc("/", handler)
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hi there %s!", r.URL.Path[1:])
}
func main() {}
- 确保安装Android
NDK
,你知道它的浴室 - 构建
c-shared
输出,输出名称为libxxx
,为Android
构建使用:
CGO_ENABLED=1 \
GOOS=android \
GOARCH=arm \
GOARM=7 \
CC=$(NDK_BIN)/armv7a-linux-androideabi21-clang \
go build -buildmode=c-shared -o libfoo.so http.go
等等 由于android有多种架构,我们需要为每一个单独编译,所以我们可以在Makefile
中让所有过程自动化如下图在通过从项目模板中选择Native C++
创建android应用程序后,下面的输出库名称是libfoo
并且将生成2个文件在每个文件夹中 libfoo.so
和 libfoo.h
:
#Filename: Makefile
# To compile run:
# make android
IOS_OUT=lib/ios
ANDROID_OUT=../android_app/app/src/main/jniLibs
ANDROID_SDK=$(HOME)/Library/Android/sdk
NDK_BIN=$(ANDROID_SDK)/ndk/23.0.7599858/toolchains/llvm/prebuilt/darwin-x86_64/bin
android-armv7a:
CGO_ENABLED=1 \
GOOS=android \
GOARCH=arm \
GOARM=7 \
CC=$(NDK_BIN)/armv7a-linux-androideabi21-clang \
go build -buildmode=c-shared -o $(ANDROID_OUT)/armeabi-v7a/libfoo.so ./cmd/libfoo
android-arm64:
CGO_ENABLED=1 \
GOOS=android \
GOARCH=arm64 \
CC=$(NDK_BIN)/aarch64-linux-android21-clang \
go build -buildmode=c-shared -o $(ANDROID_OUT)/arm64-v8a/libfoo.so ./cmd/libfoo
android-x86:
CGO_ENABLED=1 \
GOOS=android \
GOARCH=386 \
CC=$(NDK_BIN)/i686-linux-android21-clang \
go build -buildmode=c-shared -o $(ANDROID_OUT)/x86/libfoo.so ./cmd/libfoo
android-x86_64:
CGO_ENABLED=1 \
GOOS=android \
GOARCH=amd64 \
CC=$(NDK_BIN)/x86_64-linux-android21-clang \
go build -buildmode=c-shared -o $(ANDROID_OUT)/x86_64/libfoo.so ./cmd/libfoo
android: android-armv7a android-arm64 android-x86 android-x86_64
- 转到
android_app/app/src/main/cpp
并执行以下操作: 8.1.文件CMakeLists.txt
,将其设为:
cmake_minimum_required(VERSION 3.10.2)
project("android")
add_library( # Sets the name of the library.
native-lib
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
native-lib.cpp )
add_library(lib_foo SHARED IMPORTED)
set_property(TARGET lib_foo PROPERTY IMPORTED_NO_SONAME 1)
set_target_properties(lib_foo PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/libfoo.so)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../jniLibs/${CMAKE_ANDROID_ARCH_ABI}/)
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
target_link_libraries( # Specifies the target library.
native-lib
lib_foo
# Links the target library to the log library
# included in the NDK.
${log-lib} )
8.2。文件 native-lib.cpp
将其设为:
#include <jni.h>
#include <string>
#include "libfoo.h" // our library header
extern "C" {
void
Java_tk_android_MainActivity_serverJNI() {
// Running the server
server();
}
}
- 将 webview 添加到
layout/activity_main
,如:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<WebView
android:id="@+id/wv"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:isScrollContainer="false"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
- 更新
MainActivity
如下:
package tk.android
import android.os.Bundle
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var wv = findViewById<WebView>(R.id.web_view)
serverJNI()
wv.loadUrl("http://127.0.0.1:6060/")
wv.webViewClient = object : WebViewClient() {
override fun shouldOverrideUrlLoading(viewx: WebView, urlx: String): Boolean {
viewx.loadUrl(urlx)
return false
}
}
}
private external fun serverJNI(): Void
companion object {
// Used to load the 'native-lib' library on application startup.
init {
System.loadLibrary("native-lib")
}
}
}
- 更新
AndroidManifest
为:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="tk.android">
<!-- Mandatory:
android:usesCleartextTraffic="true"
Optional:
android:hardwareAccelerated="true"
Depending on the action bar required:
android:theme="@style/Theme.AppCompat.NoActionBar"
-->
<application
android:hardwareAccelerated="true" // <- Optional
android:usesCleartextTraffic="true" // <- A must to be added
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.AppCompat.NoActionBar"> // <- If do not want action bar
<activity android:name=".MainActivity"
android:configChanges="orientation|screenSize"> // <- A must to avoid crashing at rotation
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
奖金
使用 Go embed
,所有静态文件都可以嵌入到同一个库中,包括 css
、javascript
、templates
,因此您可以构建 API 或完整的应用程序带 GUI