LoginSignup
4
6

More than 5 years have passed since last update.

(Xamarin)Webサイトのオフライン閲覧アプリを作るときの注意

Last updated at Posted at 2017-04-17

メモ書きなのでしばらく更新が続くと思いますんで悪しからず

ローカルに落とす

  • HTTPで落としてきてアプリ用ストレージに保存。
    • それぞれAPIのリファレンスを見ればわかると思いますので……

HttpClient

  • System.Net.Http.HttpClientでクロスプラットフォームで書ける。URLを打てばストリームが落ちてくる。細かい加工が必要な場合System.Net.Http.HttpClientHandlerを使う(参考)。
  • キャッシュされるのを防ぐ場合、下記コードを追加
var httpClientHandler = new HttpClientHandler {  };
var httpClient = new HttpClient(httpClientHandler);
httpClient.DefaultRequestHeaders.CacheControl = new System.Net.Http.Headers.CacheControlHeaderValue();
httpClient.DefaultRequestHeaders.CacheControl.NoCache = true;
httpClient.DefaultRequestHeaders.CacheControl.NoStore = true;
  • 例外は適当に拾うこと

ストレージ

落としてきたHTTPストリームをファイルストリームに流す。System.IO.IsolatedStorage参考)を使うかPCLStorageを使う。個人的にはPCLStorageを使用。

WebViewに表示する

BaseUrl

  • アプリに内包させる場合
  • PCLStorageSystem.IO.IsolatedStorageを使って動的に落とす場合
    • Android: "file://" + Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "/"
    • Windows Device Local: ms-appdata:///local/参考
      • ただし、このフォルダにさらにサブフォルダを置く必要がある(参考

その他ハマってた点

以下に列挙するのはいろいろ試してた部分で、OSによって必要かもしれないし必要ないかもしれない

XamarinのWebViewのカスタムビューで内部用のアクセス権限を与える

VS2017で動くコードは下記の通り(任意ファイル名のクラスを追加)。どれも完全に同じではないが、ほぼ正しく書いてあるのは公式のブログ記事こちら

yourcode.xaml.cs側でWebViewを継承したCustomWebViewが定義されていて、yourcode.xamlでの指定もYourProject.CustomWebViewで指定する必要あり

using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;

[assembly: ExportRenderer(typeof(YourProject.CustomWebView), typeof(YourProject.Droid.CustomWebViewRenderer))]
namespace YourProject.Droid
{
    public class CustomWebViewRenderer : WebViewRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<WebView> e)
        {
            base.OnElementChanged(e);
            if (Control == null) return;

            //javascriptのfileスキームへのアクセス許可
            Control.Settings.AllowUniversalAccessFromFileURLs = true;
            Control.Settings.AllowFileAccessFromFileURLs = true;

            // 後述
            Control.AddJavascriptInterface(new MyJSInterface(Context), "CSharp");

            // チェック用
            // System.Console.WriteLine("OnElementChanged CustomWebViewRenderer");
        }
    }

    // 後述
    class MyJSInterface : Java.Lang.Object
    {
        Context context;

        public MyJSInterface(Context context)
        {
            this.context = context;
        }

        [Java.Interop.Export]
        [Android.Webkit.JavascriptInterface]
        public void ShowToast(string s)
        {
            System.Console.WriteLine("ShowToast:" + s);
        }
    }
}

androidでblobやmailto:が使えない

これらを使うというマニア向けの解説。これこれこれこれを参考にしたが一向に埒が明かなかった。

→暫定的解決策:JS側からC#のコードを呼ぶ方法を使うことにした。サンプルの場合、JSにwindow.CSharp.ShowToastという関数が定義される(string型の引数をつけたければつけられる)ので、その存在確認をして、存在していればそちらを使い、そうでなければ通常のブラウザ用の処理を行うという分岐をつけた。サイト側にオフライン閲覧アプリ専用のコードを組み込む必要があり用途が限られてしまうが、今回はこれで事足りる。

ajaxのステータスコードが0である場合

ローカルリソースにfile://などでアクセスする場合、xmlhttprequestのステータスコードが成功時にも0で返ってくることがある(参考)。しかしdataのほうを見ると中身がしっかり返ってきているので、中身を見て判断するしかない。

HttpListenerを使って内部に疑似HTTPサーバを組む

(本格的に組んでるわけではないですが、AndroidとiOSでは機能することは確認しました。なぜか本家のほうのUWPでは動かない。Windows Phone 8では当然ダメ)

4
6
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
4
6