20200409のAndroidに関する記事は5件です。

眺めて覚える C# Xamarin Hello World(1)

iPhoneの電池が持たなくなった。携帯電話の寿命は、大体3年ぐらいだ。

はたして5万円以上の値打ちがあるのかが疑問である。

そこでAndroidにすることにした。

image.png

さくさく動くし、電池も3日もつ!

値段も5万円から2万円適正価格だ!

前から気になっていたXamarinを使ってC#でプログラムしてみた。

1.Visual studio 2019のインストールについては、色々な記事があるので参考にしてほしい。
私の買ったディバイスは、UMIDIGI Xだ。まず、メーカサイトからディバイスドライバーをダウンロードしてWindows 10にinstall しよう。
image.png

ドライブが見えるようになる。
image.png

開発者モードにするには、「設定」アプリ内で「ビルド番号」を7回連続してタップ

image.png

image.png

Visual Studio 2019でプロジェクトを作成する。

image.png
Xamarin Formsを選択する。
Windows APPを作成するような感覚で開発できる。
image.png
空白のプロジェクトを作成する。
image.png

USBにより接続されたAndroidが表示されているか、確認する。
image.png
とりあえず下記のように表示されればOK
image.png
プログラムの内容を見てみよう
image.png
MainPage.xamlをダブルクリックするとソースが現れる。
XAMLはXMLをベースとしたマークアップ言語です。UIを定義するのに用いられます。

MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="HelloWorld.MainPage">

    <StackLayout>
        <!-- Place new controls here -->
        <Label Text="Welcome to Xamarin.Forms!" 
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />
    </StackLayout>

</ContentPage>

さてプログラムでHello Worldを表示してみよう。
image.png

MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace HelloWorld
{
    // Learn more about making custom code visible in the Xamarin.Forms previewer
    // by visiting https://aka.ms/xamarinforms-previewer
    [DesignTimeVisible(false)]
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            var layout = new StackLayout();
            var lb = new Label() { Text = "Hello World",FontSize=40 };
            layout.Children.Add(lb);
            this.Content = layout;

        }
    }
}

Xamlでうにゃうにゃ書いていたレアウトをプログラムで書くと上記のようになる。

多分、javaで書くよりすっきりしていて良い。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

眺めて覚えるXamarin Hello World

iPhoneの電池が持たなくなった。携帯電話の寿命は、大体3年ぐらいだ。

はたして5万円以上の値打ちがあるのかが疑問である。

そこでAndroidにすることにした。

image.png

さくさく動くし、電池も3日もつ!

値段も5万円から2万円適正価格だ!

前から気になっていたXamarinを使ってC#でプログラムしてみた。

1.Visual studio 2019のインストールについては、色々な記事があるので参考にしてほしい。
私の買ったディバイスは、UMIDIGI Xだ。まず、メーカサイトからディバイスドライバーをダウンロードしてWindows 10にinstall しよう。
image.png

ドライブが見えるようになる。
image.png

開発者モードにするには、「設定」アプリ内で「ビルド番号」を7回連続してタップ

image.png

image.png

Visual Studio 2019でプロジェクトを作成する。

image.png
Xamarin Formsを選択する。
Windows APPを作成するような感覚で開発できる。
image.png
空白のプロジェクトを作成する。
image.png

USBにより接続されたAndroidが表示されているか、確認する。
image.png
とりあえず下記のように表示されればOK
image.png
プログラムの内容を見てみよう
image.png
MainPage.xamlをダブルクリックするとソースが現れる。
XAMLはXMLをベースとしたマークアップ言語です。UIを定義するのに用いられます。

MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="HelloWorld.MainPage">

    <StackLayout>
        <!-- Place new controls here -->
        <Label Text="Welcome to Xamarin.Forms!" 
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />
    </StackLayout>

</ContentPage>

さてプログラムでHello Worldを表示してみよう。
image.png

MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace HelloWorld
{
    // Learn more about making custom code visible in the Xamarin.Forms previewer
    // by visiting https://aka.ms/xamarinforms-previewer
    [DesignTimeVisible(false)]
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            var layout = new StackLayout();
            var lb = new Label() { Text = "Hello World",FontSize=40 };
            layout.Children.Add(lb);
            this.Content = layout;

        }
    }
}

Xamlでうにゃうにゃ書いていたレアウトをプログラムで書くと上記のようになる。

多分、javaで書くよりすっきりしていて良い。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

眺めて覚える C# Xamarin Hello World

iPhoneの電池が持たなくなった。携帯電話の寿命は、大体3年ぐらいだ。

はたして5万円以上の値打ちがあるのかが疑問である。

そこでAndroidにすることにした。

image.png

さくさく動くし、電池も3日もつ!

値段も5万円から2万円適正価格だ!

前から気になっていたXamarinを使ってC#でプログラムしてみた。

1.Visual studio 2019のインストールについては、色々な記事があるので参考にしてほしい。
私の買ったディバイスは、UMIDIGI Xだ。まず、メーカサイトからディバイスドライバーをダウンロードしてWindows 10にinstall しよう。
image.png

ドライブが見えるようになる。
image.png

開発者モードにするには、「設定」アプリ内で「ビルド番号」を7回連続してタップ

image.png

image.png

Visual Studio 2019でプロジェクトを作成する。

image.png
Xamarin Formsを選択する。
Windows APPを作成するような感覚で開発できる。
image.png
空白のプロジェクトを作成する。
image.png

USBにより接続されたAndroidが表示されているか、確認する。
image.png
とりあえず下記のように表示されればOK
image.png
プログラムの内容を見てみよう
image.png
MainPage.xamlをダブルクリックするとソースが現れる。
XAMLはXMLをベースとしたマークアップ言語です。UIを定義するのに用いられます。

MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:d="http://xamarin.com/schemas/2014/forms/design"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             mc:Ignorable="d"
             x:Class="HelloWorld.MainPage">

    <StackLayout>
        <!-- Place new controls here -->
        <Label Text="Welcome to Xamarin.Forms!" 
           HorizontalOptions="Center"
           VerticalOptions="CenterAndExpand" />
    </StackLayout>

</ContentPage>

さてプログラムでHello Worldを表示してみよう。
image.png

MainPage.xaml.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;

namespace HelloWorld
{
    // Learn more about making custom code visible in the Xamarin.Forms previewer
    // by visiting https://aka.ms/xamarinforms-previewer
    [DesignTimeVisible(false)]
    public partial class MainPage : ContentPage
    {
        public MainPage()
        {
            InitializeComponent();
            var layout = new StackLayout();
            var lb = new Label() { Text = "Hello World",FontSize=40 };
            layout.Children.Add(lb);
            this.Content = layout;

        }
    }
}

Xamlでうにゃうにゃ書いていたレアウトをプログラムで書くと上記のようになる。

多分、javaで書くよりすっきりしていて良い。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Fused Location ProviderでKotlin FlowとLiveDataを再入門してみた

前書き

CodeLabのBuilding a Kotlin extensions libraryをやってみました。

名前からしてライブラリの作り方のベストプラクティスじゃないかと思って、勉強しようと思ったら拡張関数の内容でした。
でも拡張関数よりも、中で使ってるFlowのほうがすごかったです。

勉強したいものと違いましたが、改めてFlowの凄さを再認識できたので記事にして記録を残そうと思いました。

このCodeLabの内容は?

このCodeLabはリアルタイムで地理情報を表示するアプリを作っています。

数秒ごとにLocationを更新しています。
やろうとしていることは結構かんたんですが、結構クセがあるAPIと、Androidのライフサイクルの特有の問題があるので、気をつけて書かないと結構かんたんに罠にハマります。(ライフサイクルの難しさはこの記事であまり解説しないつもり)

一応このCodeLabのタイトルは「Building a Kotlin extensions library」なので、拡張関数のすごさを説明するものでしたが、中で使ってるFlowがすごすぎて、拡張関数なんて目じゃないレベルでした。

1. Flowを使わないJavaのAPI

地理情報を表示するのにLocationServicesを使います。このAPIはJavaで書かれているので、Callbackを前提とした使い方になっています。
使い方として

  • リアルタイムのLocation情報を取得する1
  • 前回取得した最後のLocationを取得する2

があります。

「リアルタイムのLocation情報を取得する」でも十分ですが、最初の更新まで時間がかかるのと、回線不安定などでデータを上手く取得できない場合もあるので、だいたい「前回取得した最後のLocationを取得する」と組み合わせて使うイメージです。

前回取得した最後のLocationを取得する

val fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this) // Activity or Context

fusedLocationProviderClient.lastLocation.addOnSuccessListener {
       // 成功時の処理
       ...
   }.addOnFailureListener {
       // 失敗時の処理
       // 位置情報の権限がない場合はここに来る
       ...
   }

なぜかlastLocationの戻り値はTask<Location?>なので、結果をaddOnSuccessListeneraddOnFailureListenerで非同期で取得しなければいけない。

リアルタイムのLocation情報を取得する

val callback = object: LocationCallback() {
    override fun onLocationResult(result: LocationResult?) {
        // コールバックで画面等を更新する
    }
}

// Locationを取得する頻度を設定する
val locationRequest = LocationRequest().apply {
    interval = 3000
    fastestInterval = 2000
    priority = LocationRequest.PRIORITY_HIGH_ACCURACY
}

val fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this) // Activity or Context

// 上記の設定をセットアップして、更新を開始する
fusedLocationProviderClient.requestLocationUpdates(
    locationRequest,
    callback,
    Looper.getMainLooper()
).addOnFailureListener {
    // 更新が失敗したときにここに来る
}

// 使用完了後(例えばActivity#onDestroy, Activity#onStop)は不要な更新を止める
fusedLocationProviderClient.removeLocationUpdates(callback)

Callback内で更新したLocationを使うので、RxもしくはFlowなどのリアクティブプログラミングの力を借りなければ、setTextとかのコードをCallbackの中に書いてしまいそうです。そうなるとメンテナンスしづらいコードになります。

また、Activity#onDestroyもしくはActivity#onStopでCallbackを削除しなくてもコケはしないですが(一応Callback内でやってることによります)、常時Locationを使う必要がなければ無駄に電池を使うことになるので、止めてあげたほうがいいです。

2. Coroutine / FlowでJavaの非同期APIをラップする

前回取得した最後のLocationを取得する

lastLocation: Task<Location?>(※1)の非同期は一回限りの処理なので、Flowじゃなくて、Coroutineで対応します。

ここではFusedLocationProviderClientの拡張関数にして、fusedLocationProviderClient.lastLocationを呼ぶ代わりにfusedLocationProviderClient.awaitLastLocation()のsuspend functionで非同期の値を取得できます。

suspend fun FusedLocationProviderClient.awaitLastLocation(): Location? =
   suspendCancellableCoroutine<Location> { continuation ->
       lastLocation.addOnSuccessListener { location ->
           continuation.resume(location)
       }.addOnFailureListener { e ->
           continuation.resumeWithException(e)
       }
   }

使うときは例えばこんな感じで使います。

lifecycleScope.launch {
    try {
        val location = fusedLocationProviderClient.awaitLastLocation()
        ...
    } catch (e: Exception) {
        Log.d(TAG, "Unable to get location", e)
   }
}

※1: ここのCodeLabのコードが間違っているようです。CodeLabはNull非許容型で書いていましたが、Successの場合でもnullが帰ってくることがあるそうです2

リアルタイムのLocation情報を取得する

リアルタイムで定期的にデータが流れてくるので、ここはFlowの出番です。使用するBuilderはcallbackFlow<T>です。

fun FusedLocationProviderClient.locationFlow() = callbackFlow<Location> {
    val callback = object: LocationCallback() {
        override fun onLocationResult(result: LocationResult?) {
            result ?: return
            for (location in result.locations) {
                offer(location) // emit location into the Flow using ProducerScope.offer
            }
        }
    }

    requestLocationUpdates(
        createLocationRequest(),
        callback,
        Looper.getMainLooper()
    ).addOnFailureListener { e ->
        close(e) // in case of error, close the Flow
    }

    awaitClose {
        removeLocationUpdates(callback) // clean up when Flow collection ends
    }
}

ここでのポイントは、
1. Flowが閉じられたらawaitCloseデータの更新を止める
2. 例外が投げられたらFlowを止める
3. (そしてFlowの性質上、購読する人がいなくなれば自動的に閉じられるので、リソースリークにならない)

使う側は例えばActivity内で購読する場合はこうなります。

override fun onCreate(savedInstanceState: Bundle?) {
    // よくないコード。後述
    lifecycleScope.launch {
        fusedLocationClient.locationFlow()
            .catch { e ->
                // 例外処理
            }
            .collect { location ->
                // 成功時のUIの更新など
            }
    }
}

これでUIを更新する処理とLocationを非同期で取得する処理に分けられたので、スッキリ書けました。
ただし、上記のコードには実は一点問題が残っています。lifecycleScope.launchActivity#onDestroyが呼ばれるまで死なないので、Activityの画面が見えなくても(例えばバックグラウンドに移したなど)動き続けて、無駄にflowから値を取得し続けます。クラッシュはしないですが、無駄に電力を消耗しています

launchの代わりに[Lifecycle.State.STARTED]3の時しか動かないlaunchWhenStartedを使えばこの問題を回避できます4 間違い。確かにlaunchWhenStarted[Lifecycle.State.STARTED]時にしか発動しないですが、Activityが死ななかったらlifecycleScope自体が死ぬわけじゃないので、Flowはキャンセルされませんし、awaitClose呼ばれません

LiveDataを購読する

問題を回避するためにFlow<T>.asLiveData()を使ってFlowをLiveDataに変換して使えば、
Flowの生存期間・ライフサイクルの管理をLiveDataが自動でやってくれます。

  • [Lifecycle.State.STARTED]の状態じゃなくなればupstream flowを閉じて5くれる
  • [Lifecycle.State.STARTED]の状態に戻ればもう一度upstream flowを実行してくれる

このため、View側で使うFlowでしたら、全部Flow<T>.asLiveData()でLiveDataに変換してから使ったほうが良いでしょう。

override fun onCreate(savedInstanceState: Bundle?) {
    fusedLocationClient.locationFlow()
        .conflate() // 購読する側の処理が遅かったら値を破棄する。意味がわからなければ読み飛ばして大丈夫
        .catch { e ->
            // 例外処理
        }
        .asLiveData() // ★ Flow<T>をLiveData<T>に変換する
        .observe(this) { location ->
            // 成功時のUIの更新など
        }
}

完成したコード

CodeLabのサンプルなので、すでにGitHubにあります。
このCodeLabすごく良いので、ぜひやってみてください。

https://github.com/googlecodelabs/kotlin-coroutines/blob/b71b981f8354a92fb3ebda37eceb7461e783d0bf/ktx-library-codelab/step-06/app/src/main/java/com/example/android/kotlinktxworkshop/MainActivity.kt

一応このサンプルではViewModelを使ってないし、awaitLastLocation()locationFlow()を別々で取得してくるので、シンプルに書けてない問題が残っています。 もう少し改造してきれいに書けるサンプルを別途上げる予定です。 続きの記事を書きました。よかったら見てください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

失われたAndroid Device Monitor

課題

  • unity(などのAndroid Studioを直接触らない開発環境)で、Android実機のデバッグログのチェックに、Device Monitorを利用していました。
  • Android Studio 3.2以降に更新したら、Device Monitorが使えなくなりました。

対処

  • Android Studioに統合されている"Logcat"を使います。
    • メニュー View > Tool Windows > Logcat
    • ツールバーでも切り替えられます。
  • ログのフィルタリングも可能です。
  • スクリーンショットや動画も撮れます。

公式ドキュメント

Android Device Monitor

Android Device Monitor は、Android Studio 3.1 でサポートが終了し、Android Studio 3.2 からは削除されています。Android Device Monitor で使用できる機能は、新機能に置き換えられました。以下の表に基づいて、サポートが終了した機能や削除された機能の代わりに使用できる機能をご確認ください。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む