Xamarin.Formsでログをファイル出力する(Android編)

Xamarin.Forms で System.Diagnostics.Trace を使ってログファイルに出力する方法は、iOS版と同じなので省略、、、したいところですが、実は落とし穴があります。

Androidでログファイルは何処に出力されるのか?

Xamarin.Forms の共通プロジェクトで、Appクラスのコンストラクタ(App.xaml.cs)に以下のように書いておくとiOSでもAndroidでも統一的に Trace の結果をファイルに出力することができます。

var dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(dir, $"log-{DateTime.Now.ToString("yyyyMMdd-HHmm")}.txt");
var tw = System.IO.File.OpenWrite(filename);
var tr1 = new TextWriterTraceListener(tw);
System.Diagnostics.Trace.AutoFlush = true;
System.Diagnostics.Trace.Listeners.Add(tr1);

ここで、Environment.SpecialFolder.MyDocumentsで取得できる場所は、iOSのほうは「ファイル/アプリ名」の中になるのですが、Androidの場合にはアプリ自身のfilesのフォルダーになっています。このフォルダはアプリ自身しか見えなくて、テストをしたときにログファイルを手軽に見ることができません。ログファイルを閲覧する作るとなると結構面倒です。

Xamarin.Droid ではどうするのか?

先にAndroid固有のプロジェクト(Xamarin.Droid)のほうを解決しておきます。
iOSと同じ様に、Androidにも「ファイル」というアイコンがあります。この「ファイル」からAndroid内部のファイルを直接閲覧できます。そこで、この「ファイル」の場所から見えるところに、ログファイルを置くように工夫します。

var contextRef = new WeakReference<Context>(this);
contextRef.TryGetTarget(out var c);
var dir = c.GetExternalFilesDir(null).AbsolutePath;
var filename = Path.Combine(dir, $"droid-{DateTime.Now.ToString("yyyyMMdd-HHmm")}.txt");
var tw = System.IO.File.OpenWrite(filename);
this.tr1 = new TextWriterTraceListener(tw);
DroidTrace.AutoFlush = true;
DroidTrace.Listeners.Add(tr1);
DroidTrace.WriteLine("ios Application Trace mode " + DateTime.Now.ToString());

ちょっとややこしいですが、GetExternalFilesDir関数を使うとアプリが公開しているフォルダを取得できます。このフォルダはアプリが他のアプリと共有するためのフォルダーになります。
このコードは、MainActivity::OnCreate に書いておけばよいです。

/storage/emulated/0/Android/data/<バンドル名>/files

「ファイル」のほうからは、スマホの機種名のとところから「/Andorid」のフォルダーから見つけることができます。アプリのバンドル名(net.moonmile.sample.testapp のようなもの)が含まれるので、かなり奥深いところになってしまいますが、一般ユーザーでもファイルを見ることができます。

public class DroidTrace
{
    static DroidTrace()
    {
        Listeners = new List<TraceListener>();
    }
    public static List<TraceListener> Listeners { get; }
    public static bool AutoFlush { get; set; } = true;
    public static void WriteLine(string message)
    {
        foreach (var it in Listeners)
        {
            it.WriteLine(message);
            if (AutoFlush == true) it.Flush();
        }
    }
}

DroidTrace クラスは共有プロジェクトの System.Diagnostics.Trace と重なっていしまうために、独自に作った簡易クラスです。作り方はiOS版と同じですね。

このように、GetExternalFilesDir で取得したパスに書き込むと Android の「ファイル」からログファイルを閲覧できるようになります。

Xamarin.Forms 側のパスを決める

となると、共有プロジェクトの方でも GetExternalFilesDir のパスに出力すれば良いことが分かるのですが、このパスは Android 内部で決まるものなので、共有プロジェクトでは使えません。
真面目にはるならば、DependencyService で DI するのが筋なんでしょうが、所詮 iOS と Android の違いしかないので、次のように直接パスを指定してしまいます。

var dir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
if ( Device.RuntimePlatform == Device.Android )
{
    // Android の場合は決め打ちにする
    dir = "/storage/emulated/0/Android/data/<バンドル名>/files";
}
var filename = Path.Combine(dir, $"log-{DateTime.Now.ToString("yyyyMMdd-HHmm")}.txt");
var tw = System.IO.File.OpenWrite(filename);
var tr1 = new TextWriterTraceListener(tw);
System.Diagnostics.Trace.AutoFlush = true;
System.Diagnostics.Trace.Listeners.Add(tr1);

というわけで、ちょっと雑ではありますが、ひとまず iOS と Android のログ出力環境がこれで整います。

カテゴリー: 開発 パーマリンク