LoginSignup
1
1

More than 3 years have passed since last update.

Xamarin.Forms ガワネイティブアプリで、C# から JavaScript へのリクエスト~レスポンスを Task<T> にする

Posted at

メモ書きなので雑。

シナリオ

  • Xamarin.Forms + WebView(HTML, JavaScript な SPA) のガワネイティブアプリである
  • Android アプリの BACK キーの有効無効を、WebView 内のページ状態によって切り替えたい
    • データ編集中(未保存)なので Back キーで history.back やアプリ終了されたら困る、とか

流れ

1. [Forms側] Action<TaskCompletionSource<bool>> OnRequestIsEnableBackKey { get; set; } を生やす

2. [Forms側] WebViewEx(WebViewを拡張したもの)に Task<bool> IsEnableBackKeyAsync() も生やす。実装は次のように。

public Task<bool> IsEnableBackKeyAsync()
{
    var comp = new TaskCompletionSource<bool>();

    if (OnRequestIsEnableBackKey != null)
    {
        OnRequestIsEnableBackKey.Invoke(comp);
    }
    else
    {
        comp.SetResult(false);
    }

    return comp.Task;
}

3. [Android側] WebViewRenderer を拡張した MyWebViewRenderer を作る。

4. Xamarin.Forms の WebView で JavaScript 連携を行う(with iOS/Android共通化) - Qiita を参考に JavaScriptHandler も作る。コンストラクタで Control に加え e.NewElement as WebViewEx も渡す。引数の名前は WebViewEx outer とする。AddJavascriptInterface の第2引数は GawaApp とでもしておく。

5.JavaScriptHandler のコンストラクタで OnRequestIsEnableBackKey を受信するコードを書く。

private TaskCompletionSource<bool> isEnableBackKeyComp = null;

public JavaScriptHandler(Android.Webkit.WebView webView, WebViewEx outer)
{
    outer.OnRequestIsEnableBackKey = comp => 
    {
        isEnableBackKeyComp = comp;

        // メインスレッドから呼ばないとエラー
        webView.Post(() =>
        {
            webView.EvaluateJavascript("window.requestIsEnableBackKey()", null);
            // webView.LoadUrl("javascript:window.requestIsEnableBackKey();"); ←これでもOKっぽい
        });
    };
}

ここまでの処理で、Forms 側で await webViewEx.IsEnableBackKeyAsync() が呼び出されたら、JavaScript の window.requestIsEnableBackKey() 関数が呼び出される。

6.JavaScriptHandler 内に onResultCanExitApp を生やす。JavaScript側 から結果が通知されるメソッドである。次のように。

[Export]
[Android.Webkit.JavascriptInterface]
public void onResultIsEnableBackKey(bool value)
{
    isEnableBackKeyComp?.SetResult(value);
    isEnableBackKeyComp = null; // one shot
}

JavaScript 側で GawaApp.onResultIsEnableBackKey(true or false) を呼び出すと、このメソッドがコールバックされる。
TaskCompletionSource である isEnableBackKeyComp に値を設定すれば、Forms 側の await webViewEx.IsEnableBackKeyAsync() の結果が返される。

7.JavaScript 側はきっとこんな感じ

window.requestIsEnableBackKey = () => {

  // 何かの処理

  GawaApp.onResultIsEnableBackKey(true or false);
}

途中まで実装したけど、面倒になって(JavaScript→C# に単方向の方がシンプルでいいやと思って)やめちゃったので、アイデアだけ残しておきます。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1