-2

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

enter image description here

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?

Hasan A Yousef
  • 22,789
  • 24
  • 132
  • 203

1 Answers1

0

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 <-c to prevent the goroutine from exit.
  5. Use cgo by adding import "C", so the main file become like this:
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() {}
  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:

enter image description here

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

#include <jni.h>
#include <string>

#include "libfoo.h" // our library header

extern "C" {
    void
    Java_tk_android_MainActivity_serverJNI() {
        // Running the server
        server();
    }
}
  1. Add webview to the layout/activity_main, as:
<?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. 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<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. Update AndroidManifest to be:
<?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>

enter image description here

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

enter image description here

Hasan A Yousef
  • 22,789
  • 24
  • 132
  • 203
  • great answer. Thanks. Can you help fixing `Ensure to have Android `NDK` installed, and you know its bath` NDK or SDK ? bath ? path ? sorry if that is a stupid question... –  Aug 14 '21 at 10:36
  • @mh-cbon thanks for your feedback, kindly see this about NDK and tell me if stil not cler: https://stackoverflow.com/a/57911000/2441637 – Hasan A Yousef Aug 14 '21 at 10:45
  • ooohhhh i had no idea about that ; ) thank you for the link ! so that is a Native Development Kit then ! –  Aug 14 '21 at 10:46
  • have you ever tried to mixin that with https://github.com/cretz/bine or https://github.com/yggdrasil-network/yggdrasil-go or https://github.com/neilalexander/yggmail ? would be amazing if that works. –  Aug 14 '21 at 11:18