构建一个可以在桌面和移动设备上运行的服务器。

huangapple go评论90阅读模式
英文:

Building a server that can run every where, desktop and mobile

问题

我可以使用相同的代码库在移动端的webview上运行我的应用程序吗,而不使用gomobile或其他包,这样我就可以将代码作为通用应用程序使用吗?

英文:

I've the below simple go server that is running at my laptop (Mac/Windows/Linux):

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))
}

构建一个可以在桌面和移动设备上运行的服务器。

Can I use the same codebase to run my app at mobile webview, without using gomobile or other packages, so I've my code as universal app?

答案1

得分: 0

答案是“是的”,但需要对文件本身进行一些轻微的修改。

  1. func main() {}中删除所有内容,因为我们将构建最终结果作为共享库,而不是可执行二进制文件。
  2. //export函数中运行服务器。
  3. 匿名goroutine中作为go func() {}()运行服务器,以便不阻塞移动应用程序的主线程。
  4. 为了保持服务器goroutine的运行,我们需要使用通道<-c来防止goroutine退出。
  5. 通过添加import "C"来使用cgo,使主文件变为以下形式:
package main

import "C"

// 其他导入应与特殊的Cgo导入分开
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() {}
  1. 确保已安装Android NDK,并知道其路径。
  2. 使用输出名称为libxxx构建c-shared输出,要构建为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.solibfoo.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
  1. 进入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" // 我们的库头文件

extern "C" {
    void
    Java_tk_android_MainActivity_serverJNI() {
        // 运行服务器
        server();
    }
}
  1. 将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>
  1. 更新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")
        }
    }
}
  1. 更新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,所有静态文件都可以嵌入到同一个库中,包括cssjavascripttemplates,因此可以构建API或带有GUI的完整应用程序。

英文:

The answer is "Yes", but some slight modifications to the file itself is required.

  1. Remove everything from the func main() {} as we'll build the final result as a shared library, not as an executable binary.
  2. Run the server in an //export function.
  3. Run the server from an anonymous goroutine as go func() {}() so it is not blocking the main thread of the mobile app.
  4. To keep the server goroutine running, we need to use a channel as &lt;-c to prevent the goroutine from exit.
  5. Use cgo by adding import &quot;C&quot;, so the main file become like this:
package main

import &quot;C&quot;

// other imports should be seperated from the special Cgo import
import (
	&quot;fmt&quot;
	&quot;log&quot;
	&quot;net/http&quot;
)

//export server
func server() {
	c := make(chan bool)
	go func() {
		log.Println(http.ListenAndServe(&quot;localhost:6060&quot;, nil))
		&lt;-c
	}()

	http.HandleFunc(&quot;/&quot;, handler)

}

func handler(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, &quot;Hi there %s!&quot;, r.URL.Path[1:])
}

func main() {}
  1. Ensure to have Android NDK installed, and you know its bath
  2. Build the c-shared output with an output name as libxxx, to build for Android use:
	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

Wait As android has multiple architectures, we need to compile for each one individually, so we can have all the processes automated in a Makefile as below after creating the android app by selecting Native C++ from the project templates, below the output library name is libfoo and 2 files will be generated in each folder libfoo.so and 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
  1. Go to android_app/app/src/main/cpp and do the following:
    8.1. File CMakeLists.txt, make it as:
cmake_minimum_required(VERSION 3.10.2)

project(&quot;android&quot;)

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. File native-lib.cpp make it as:

#include &lt;jni.h&gt;
#include &lt;string&gt;

#include &quot;libfoo.h&quot; // our library header

extern &quot;C&quot; {
    void
    Java_tk_android_MainActivity_serverJNI() {
        // Running the server
        server();
    }
}
  1. Add webview to the layout/activity_main, as:
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;androidx.constraintlayout.widget.ConstraintLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:app=&quot;http://schemas.android.com/apk/res-auto&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.MainActivity&quot;&gt;

    &lt;WebView
        android:id=&quot;@+id/wv&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;
        android:isScrollContainer=&quot;false&quot;
        app:layout_constraintBottom_toBottomOf=&quot;parent&quot;
        app:layout_constraintHorizontal_bias=&quot;0.0&quot;
        app:layout_constraintLeft_toLeftOf=&quot;parent&quot;
        app:layout_constraintRight_toRightOf=&quot;parent&quot; /&gt;

&lt;/androidx.constraintlayout.widget.ConstraintLayout&gt;
  1. Update the MainActivity as below:
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&lt;WebView&gt;(R.id.web_view)
        serverJNI()
        wv.loadUrl(&quot;http://127.0.0.1:6060/&quot;)
        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 &#39;native-lib&#39; library on application startup.
        init {
            System.loadLibrary(&quot;native-lib&quot;)
        }
    }
}
  1. Update AndroidManifest to be:
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;manifest xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    package=&quot;tk.android&quot;&gt;

    &lt;!-- Mandatory:
                android:usesCleartextTraffic=&quot;true&quot;
         Optional: 
                android:hardwareAccelerated=&quot;true&quot; 
         Depending on the action bar required:
                android:theme=&quot;@style/Theme.AppCompat.NoActionBar&quot;
    --&gt;
    &lt;application
        android:hardwareAccelerated=&quot;true&quot;     // &lt;- Optional 
        android:usesCleartextTraffic=&quot;true&quot;     // &lt;- A must to be added
        android:allowBackup=&quot;true&quot;
        android:icon=&quot;@mipmap/ic_launcher&quot;
        android:label=&quot;@string/app_name&quot;
        android:roundIcon=&quot;@mipmap/ic_launcher_round&quot;
        android:supportsRtl=&quot;true&quot;
        android:theme=&quot;@style/Theme.AppCompat.NoActionBar&quot;&gt;   // &lt;- If do not want action bar
        &lt;activity android:name=&quot;.MainActivity&quot;
            android:configChanges=&quot;orientation|screenSize&quot;&gt;   // &lt;- A must to avoid crashing at rotation
            &lt;intent-filter&gt;
                &lt;action android:name=&quot;android.intent.action.MAIN&quot; /&gt;

                &lt;category android:name=&quot;android.intent.category.LAUNCHER&quot; /&gt;
            &lt;/intent-filter&gt;
        &lt;/activity&gt;
    &lt;/application&gt;

&lt;/manifest&gt;

构建一个可以在桌面和移动设备上运行的服务器。

Bonus
With Go embed all static files can be embed in the same library, including css, javascript, templates so you can build either API, or full app with GUI

构建一个可以在桌面和移动设备上运行的服务器。

huangapple
  • 本文由 发表于 2021年8月13日 21:59:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/68773610.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定