以下の内容はhttps://kafkafinancialgroup.hatenablog.com/entry/2025/05/25/210514より取得しました。


WebViewでAndroidアプリを作ってみた!設定不要のコンテンツビューア開発記

📱 はじめに

こんにちは!今回はAndroidのWebViewを使って、設定不要でサクッと使えるコンテンツビューアアプリを作ってみました。

「既存のWebサービスをもっと快適に使いたい!」「でもネイティブアプリ開発は大変そう...」そんな方におすすめのWebViewベース開発について、実際の開発過程を交えて紹介します。

🎯 今回作ったもの

  • APK一つでインストール完了の軽量アプリ
  • WebViewベースで開発工数を大幅削減
  • JavaScript注入による動的UI最適化
  • 2MB以下の軽量設計

⚡ なぜWebViewを選んだのか

開発効率が圧倒的!

// ネイティブUI開発だと...
class ContentAdapter : RecyclerView.Adapter() {
    // 数百行のコード...
}

// WebViewなら...
webView.loadUrl("https://target-service.com")
// これだけ!

配布が簡単

  • 設定ファイル不要
  • APK一つで完結
  • ユーザーは即利用可能

🛠️ 開発環境構築

1. Android Studioセットアップ

# 公式サイトからダウンロード
https://developer.android.com/studio

:::message 初回起動時は7-12分かかることがありますが、これは依存関係のダウンロードによるものです。2回目以降は30秒程度で起動します! :::

2. プロジェクト作成

✅ Template: Empty Activity
✅ Language: Kotlin
✅ Minimum SDK: API 21 (Android 5.0)
✅ Package: com.example.yourapp

📝 実装してみよう

ステップ1: 基本的なWebView実装

まずは最もシンプルなWebView表示から!

class MainActivity : Activity() {
    private lateinit var webView: WebView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        webView = findViewById(R.id.webview)
        
        // JavaScript有効化
        webView.settings.javaScriptEnabled = true
        
        // アプリ内でリンクを開く
        webView.webViewClient = WebViewClient()
        
        // 目的のサービスをロード
        webView.loadUrl("https://target-service.com")
    }
}

ステップ2: レイアウトファイル



    

ステップ3: 権限設定

    
    
    
    
        
            
                
                
            
        
    

🎨 高度な機能を追加してみる

JavaScript注入による動的コンテンツ制御

WebViewの真価はJavaScript注入にあります!

class ContentOptimizationWebViewClient : WebViewClient() {
    
    override fun onPageFinished(view: WebView, url: String) {
        super.onPageFinished(view, url)
        injectOptimizationScript(view)
    }
    
    private fun injectOptimizationScript(webView: WebView) {
        val script = """
            javascript:(function() {
                console.log('UI最適化開始');
                
                // DOM要素の動的操作
                const elements = document.querySelectorAll('.secondary-content');
                elements.forEach(el => {
                    el.style.opacity = '0.3';
                    el.style.transition = 'opacity 0.3s ease';
                });
                
                // 特定要素の非表示化
                const unwantedElements = document.querySelectorAll('.promotional-content');
                unwantedElements.forEach(el => {
                    el.style.display = 'none';
                });
                
                // 継続的な監視
                setInterval(function() {
                    // 新しく追加された要素にも対応
                    const newElements = document.querySelectorAll('.dynamic-content');
                    newElements.forEach(el => el.style.border = '2px solid #e0e0e0');
                }, 2000);
                
                console.log('UI最適化完了');
            })();
        """.trimIndent()
        
        webView.evaluateJavaScript(script, null)
    }
}

ネットワークレベルでの制御

override fun shouldInterceptRequest(
    view: WebView?, 
    request: WebResourceRequest?
): WebResourceResponse? {
    val url = request?.url?.toString() ?: ""
    
    // 特定のリソースをブロック
    val blockedDomains = listOf(
        "analytics.example.com",
        "tracking.service.com",
        "metrics.provider.com"
    )
    
    if (blockedDomains.any { url.contains(it) }) {
        // 空のレスポンスを返す
        return WebResourceResponse(
            "text/plain", 
            "UTF-8", 
            ByteArrayInputStream("".toByteArray())
        )
    }
    
    return super.shouldInterceptRequest(view, request)
}

🐛 開発中に遭遇した問題と解決法

問題1: テーマエラーでクラッシュ

You need to use a Theme.AppCompat theme (or descendant) with this activity

解決法: AndroidManifest.xmlのテーマ設定を確認





    

問題2: JavaScript が動作しない

// ❌ 忘れがち
webView.settings.javaScriptEnabled = false

// ✅ 必須設定
webView.settings.javaScriptEnabled = true

問題3: 外部ブラウザが開いてしまう

// ❌ WebViewClientを設定しないと外部ブラウザが起動
webView.loadUrl("https://example.com")

// ✅ WebViewClientを設定
webView.webViewClient = WebViewClient()
webView.loadUrl("https://example.com")

🚀 ビルドと配布

デバッグAPKの生成

# Android Studio GUIで
[Build] → [Build Bundle(s) / APK(s)] → [Build APK(s)]

# 生成先
app/build/outputs/apk/debug/app-debug.apk

パフォーマンス最適化

// build.gradle.kts (Module: app)
android {
    buildTypes {
        release {
            isMinifyEnabled = true
            isShrinkResources = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
}

📊 実際の効果測定

開発工数の比較

項目 ネイティブ開発 WebView開発
UI実装 2-3週間 1-2日
API連携 1週間 不要
状態管理 1週間 JavaScript
合計 1-2ヶ月 1週間

APKサイズ

# 最終的なAPKサイズ
app-debug.apk: 2.1MB

# 比較: 一般的なネイティブアプリ
typical-native-app.apk: 15-30MB

🔧 デバッグのコツ

Chrome DevToolsとの連携

// デバッグビルドでWebViewデバッグを有効化
if (BuildConfig.DEBUG) {
    WebView.setWebContentsDebuggingEnabled(true)
}

PCのChromechrome://inspect にアクセスすると、スマホのWebViewをデバッグできます!

ログ出力の活用

// JavaScript側でのログ
console.log('デバッグ情報:', element);

// Kotlin側でのログ
android.util.Log.d("WebView", "ページ読み込み完了: $url")

💡 さらなる改善アイデア

Progressive Web App (PWA) 対応

// Service Worker サポート
webView.settings.domStorageEnabled = true
webView.settings.databaseEnabled = true

オフライン対応

// キャッシュ戦略
webView.settings.cacheMode = when {
    isNetworkAvailable() -> WebSettings.LOAD_DEFAULT
    else -> WebSettings.LOAD_CACHE_ELSE_NETWORK
}

ダークモード対応

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
    webView.settings.forceDark = WebSettings.FORCE_DARK_ON
}

🎉 まとめ

WebViewベースのAndroid開発は:

開発工数を大幅削減(1-2ヶ月 → 1週間)
軽量なAPK(2MB以下)
設定不要でユーザーフレンドリー
JavaScriptで柔軟な機能拡張
既存Webサービスを活用可能

特に既存のWebサービスモバイルアプリ化したい場合や、迅速なプロトタイピングが必要な場面では非常に効果的なアプローチです。

🔄 段階別実装パターン

初心者向け実装

// ステップ1: 最小構成
class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val webView = WebView(this)
        webView.settings.javaScriptEnabled = true
        webView.webViewClient = WebViewClient()
        webView.loadUrl("https://target-service.com")
        
        setContentView(webView)
    }
}

中級者向け実装

// ステップ2: カスタマイズ機能追加
class MainActivity : Activity() {
    private lateinit var webView: WebView
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        setupWebView()
        optimizeUserExperience()
    }
    
    private fun setupWebView() {
        webView = findViewById(R.id.webview)
        webView.settings.apply {
            javaScriptEnabled = true
            domStorageEnabled = true
            setSupportZoom(true)
            builtInZoomControls = true
            displayZoomControls = false
        }
        webView.webViewClient = EnhancedWebViewClient()
    }
}

📚 学習リソース

公式ドキュメント

実践的なTips

  • メモリ管理: WebViewのライフサイクル適切な処理
  • セキュリティ: JavaScript実行時の注意点
  • パフォーマンス: キャッシュ戦略とリソース最適化

記事を読んでいただき、ありがとうございました!
WebViewベースの開発について質問があれば、コメントでお気軽にどうぞ 🙋‍♂️




以上の内容はhttps://kafkafinancialgroup.hatenablog.com/entry/2025/05/25/210514より取得しました。
このページはhttp://font.textar.tv/のウェブフォントを使用してます

不具合報告/要望等はこちらへお願いします。
モバイルやる夫Viewer Ver0.14