HTML などの静的ファイルを APK に包んでオフライン閲覧する方法

電車や高速鉄道内などネットワーク環境が不安定な時、スマートフォンでウェブページが遅延して読み込めないのはとても不便です。

多くの場合、スマートフォンで閲覧するウェブサイトは静的ファイルで構成されています。しかし、ホームページにアクセスすると、一度に全てのリソースがダウンロードされず、その後のページ遷移で毎回読み込みに時間がかかってしまいます。

全てのリソースを事前にダウンロードして、ブラウザで閲覧できるようにすることは可能でしょうか?

一つの方法として、HTML、CSS、JSファイルを全てスマートフォンにダウンロードし、darkhttpdのようなWebサーバーを立ち上げ、ブラウザでlocalhost:8080にアクセスする方法がありますが、手間がかかります。

クロスプラットフォームのプログラミングフレームワークであるTauri v2を使うと、より簡単に実現できます。以下の手順で行います:

バニラテンプレートを使ってプロジェクトを作成します

npm create tauri-app -m pnpm -t vanilla --identifier app.xjtu.rust-doc xjtu-tauri-app-rust-doc
cd xjtu-tauri-app-rust-doc
pnpm install
pnpm tauri android init

次に、index.htmlを含む静的リソースディレクトリをsrcディレクトリの下に全てコピーします:

rsync -av --delete ~/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/share/doc/rust/html/ ./src/

参考サイトに従って、以下のファイルを編集し、リリースビルドに必要な認証情報を追加します:

src-tauri/gen/android/keystore.properties
password=7
keyAlias=xj
storeFile=/app.jks
src-tauri/gen/android/app/build.gradle.kts
import java.util.Properties
import java.io.FileInputStream

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("rust")
}

val tauriProperties = Properties().apply {
    val propFile = file("tauri.properties")
    if (propFile.exists()) {
        propFile.inputStream().use { load(it) }
    }
}

android {
    compileSdk = 34
    namespace = "app.xjtu.static_app"
    defaultConfig {
        manifestPlaceholders["usesCleartextTraffic"] = "false"
        applicationId = "app.xjtu.static_app"
        minSdk = 24
        targetSdk = 34
        versionCode = tauriProperties.getProperty("tauri.android.versionCode", "1").toInt()
        versionName = tauriProperties.getProperty("tauri.android.versionName", "1.0")
    }
    signingConfigs {
        create("release") {
            val keystorePropertiesFile = rootProject.file("keystore.properties")
            val keystoreProperties = Properties()
            if (keystorePropertiesFile.exists()) {
                keystoreProperties.load(FileInputStream(keystorePropertiesFile))
            }

            keyAlias = keystoreProperties["keyAlias"] as String
            keyPassword = keystoreProperties["password"] as String
            storeFile = file(keystoreProperties["storeFile"] as String)
            storePassword = keystoreProperties["password"] as String
        }
    }

    buildTypes {
        getByName("debug") {
            manifestPlaceholders["usesCleartextTraffic"] = "true"
            isDebuggable = true
            isJniDebuggable = true
            isMinifyEnabled = false
            packaging {
                jniLibs.keepDebugSymbols.add("*/arm64-v8a/*.so")
                jniLibs.keepDebugSymbols.add("*/armeabi-v7a/*.so")
                jniLibs.keepDebugSymbols.add("*/x86/*.so")
                jniLibs.keepDebugSymbols.add("*/x86_64/*.so")
            }
        }
        getByName("release") {
            isMinifyEnabled = true
            signingConfig = signingConfigs.getByName("release")
            proguardFiles(
                *fileTree(".") { include("**/*.pro") }
                    .plus(getDefaultProguardFile("proguard-android-optimize.txt"))
                    .toList().toTypedArray()
            )
        }
    }
    kotlinOptions {
        jvmTarget = "1.8"
    }
    buildFeatures {
        buildConfig = true
    }
}

rust {
    rootDirRel = "../../../"
}

dependencies {
    implementation("androidx.webkit:webkit:1.6.1")
    implementation("androidx.appcompat:appcompat:1.6.1")
    implementation("com.google.android.material:material:1.8.0")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.4")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.0")
}

apply(from = "tauri.build.gradle.kts")

APKのビルドを行います:

tauri android build --apk --target aarch64

実行結果のデモ:

隠し内容

Google Playストア上には、不明な高度な手法でパッケージ化されたAPKが存在します:
https://play.google.com/store/apps/details?id=com.rust_doc.md_ismail_hosen
https://play.google.com/store/apps/details?id=com.rust_book.example

「いいね!」 2

そう考えると、このプラットフォームも静的オフライン版の App を作成することが可能です。

これは、あなたのサイトの全ての投稿(テキストや画像などの豊富なメディアコンテンツ)をまとめて閲覧できる APK です。ファイルサイズは少し大きめですが(1650 MiB)、その後の閲覧速度は非常に速いです :flight_departure:

https://assets.xjtu.app/pool/app.xjtu.static-app.apk

P.S. 1:静的なウェブページは今日以下のコマンドでクロールしたものです:
wget --adjust-extension --mirror --page-requisites --convert-links --recursive --user-agent "Googlebot" https://xjtu.app

ブラウザの User AgentGooglebot に変更すると同じようなページが表示されるか、または直接以下の URL で確認できます:
https://static.xjtu.app

P.S. 2:あなたのサイトの全静的リソースをまとめると1GiB以上になり、Tauri ベースのこの方法ではスケールが悪く、コンパイルに時間がかかります。