20200709のAndroidに関する記事は7件です。

adbコマンドによるdozeモード化

Android 6.0からdozeモードが搭載されました。
Android 7.0からはdeep dozeが追加され、dozeにもdeepとlightの2種類存在していることになります。

評価する際にdozeモードになるのを待とうとすると時間が掛かってしまうため、adbコマンドでdozeモードにします。

adbコマンドでdozeモードにする場合、2つ方法があります。

1つは通常のdozeモード、もう1つは強制dozeモード(←正式名称ではありません)です。

前者はdozeを解除するトリガーがあるとdozeが解除されますが、
後者はトリガーがあってもずっとdozeのままです。(adbコマンドで解除できますが)

共通の手順

1.まずはPCにUSB接続して、コマンドで充電をOFFにします

adb shell dumpsys battery unplug

2.次に画面を消灯します

通常のdozeモード化

1.dozeモードにします

adb shell dumpsys deviceidle step

モードが切り替わるため、何回か繰り返します。
mState=IDLE, mLightState=OVERRIDEになれば成功です。

2.戻したい時は画面を点灯するか下記コマンドで起こします

adb shell dumpsys battery reset

強制dozeモード

1.強制的にdozeモードにします

adb shell dumpsys deviceidle force-idle

2.戻したい時は解除して、充電出来るようにします

adb shell dumpsys deviceidle unforce
adb shell dumpsys battery reset
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Android StudioでGithub上のプロジェクトをCloneする

今回初めてAndroid用のGitHubにあがっているプロジェクトをローカルに落とす必要があったので
自身の備忘録として残しておきます:bow:

環境

Android Studio Version4.4.0
Mac OS Catalina

対象のGitHubリポジトリを開く

Codeボタンを押下すると、"Clone with SSH"というメニューが出てくるので赤線の箇所をコピー
スクリーンショット 2020-07-09 19.14.47.png

Android Studioを開く

Get Version Controlを押下
スクリーンショット 2020-07-09 19.27.22.png
すると下記画面に遷移するので
URL:GitHubでコピーしたものをコピペ
Directory:Clone先のディレクトリを指定
問題なければ右下のCloneを押下
スクリーンショット 2020-07-09 19.31.15.png

最後にGitHubのssh接続のパスワードを入力して完了!
スクリーンショット 2020-07-09 19.30.06.png

最後に

最近Androidの勉強をはじめました・・・!
ぼちぼちAndroid/Kotlinに関する記事を書いていければなぁと思ってます:relaxed:

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

あなたのためのJavaスレッドセーフ

我々は如何にして Java におけるスレッドセーフを保証すべきか。

結論

  • synchronized ブロックを使いましょう。
  • ロックオブジェクトはprivateインスタンスフィールドで定義しましょう。
  • ロックオブジェクトはスレッドセーフの保証対象ごとに定義しましょう。

薀蓄

  • スレッドセーフとは

    • スレッド間で可変データを共有する場合において、同期化が保証される状態。
  • 同期化の保証とは

    • 「原子性の保証」「可視性の保証」の二点を満たすものと定義する。
  • 原子性(atomicity)の保証とは

    • ある処理の開始から終了までの間、外部からの干渉を排他できること。
  • 可視性(visibility)の保証とは

    • 時系列に対して最新の値が参照できること。

Java言語仕様

Javaにおいては特に以下の制御構文が、これまでに出てきた要素に関連する。

  • synchronizedブロック
    • 引数に同じオブジェクトを指定したブロック間において、操作を同期化する。
    • メソッドに対する synchronized 修飾は自インスタンスに対する synchronized ブロックと等価。
      // 以下のsynchronizedブロック間の操作を同期化

      public void foo() {
          synchronized (_SOME_OBJECT) {
              // do something
          }
      }

      public void bar() {
          synchronized (_SOME_OBJECT) {
              // do something
          }
      }
      // 以下は等価

      public void foo() {
          synchronized (this) {
              // do something
          }
      }

      public void synchronized foo() {
              // do something
      }
  • volatile修飾子
    • 指定した変数の可視性を保証する。
    • スレッドセーフ用途で利用できる場面自体は存在するが、今回は扱わない。
      • 例えばint型やboolean型の変数は言語仕様により原子性が保証されている。

実践

以下の実装に対して、foo() と bar() を別々のスレッドから不定期に複数回呼び出すケースを考える

      public void foo() {
          _object.update();  // _object の内部状態を変化させる処理
      }

      public void bar() {
          if (_object.enabled()) {
              _object.getData();  // enabled() が false の場合に呼び出すと例外発生
          }
      }

synchronizedブロック無し

bar() の処理に原子性の保証が無いため、以下の順でメソッドが呼び出される可能性がある。
_object.enabled() -> _object.update() -> _object.getData()

      public void foo() {
          _object.update();
      }

      public void bar() {
          if (_object.enabled()) {
              _object.getData();  // _object の内部状態は保証されない
          }
      }

synchronizedブロックあり

ブロック内の処理の間に同期化の保証が加わり、即ち原子性についても保証される。
【 _object.update() 】 -> 【 _object.enabled() -> _object.getData() 】 -> 【 _object.update() 】

ブロック単位で処理の待ち行列に格納されるイメージを持つとわかりやすい。

      public void foo() {
          synchronized (this) {
              _object.update();
          }
      }

      public void bar() {
          synchronized (this) {
              if (_object.enabled()) {
                  _object.getData();  // _object の内部状態が保証される
              }
          }
      }

synchronizedブロック一部だけあり

引数に同じオブジェクトを指定したブロック間において、操作を同期化する

synchronized ブロックはあくまで、先に上記のように述べた通りの同期化の保証である。
以下の実装は bar() 内の処理に対する同期化を保証しない。

      public void foo() {
          _object.update();
      }

      public void bar() {
          synchronized (this) {
              if (_object.enabled()) {
                  _object.getData();  // _object の内部状態は保証されない
              }
          }
      }

別オブジェクトに対するsynchronizedブロック

念のための補足。
引数に異なるオブジェクトを指定したブロック間における操作も同期化は保証されない。

      public void foo() {
          synchronized (_SOME_OBJECT) {
              _object.update();
          }
      }

      public void bar() {
          synchronized (this) {
              if (_object.enabled()) {
                  _object.getData();  // _object の内部状態は保証されない
              }
          }
      }

余談

  • getterのような readonly な単一処理においても synchronized ブロックを使用すべきである。
    • 可視性の保証のため。
    • また、オブジェクトによっては読み込み処理に対して原子性が保証されないものがある。

ロックオブジェクトについて

※synchronized ブロックの引数に渡すオブジェクトのこと

デッドロック耐性

デッドロックの発生を最大限抑止するため、実装には以下の原則を適用することが望ましい。

  • synchronized ブロックの範囲を最小化する
  • ロックオブジェクトの共有範囲を最小化する

ロックオブジェクト選定

  • this

    • 利用者に対して public なオブジェクトであるため、原則に対して望ましくない
      • 等価である synchronized メソッドも使用すべきではない
  • privateクラスフィールド

    • 利用者からはアクセスできないことが保証される
    • インスタンス間で共有されるオブジェクトであるため、原則に対して望ましくない
  • privateインスタンスフィールド

    • 利用者からはアクセスできないことが保証される
    • かつインスタンス単位で生成されるため、原則に対して最適

 また、いずれの場合においてもロックオブジェクトがnullになる可能性があってはならない。
 これはfinal修飾子を付与することで保証する。

  // 推奨実装

  class Shared {
      private final Object _LOCK_OBJECT = new Object();  // staticにしない

      public void foo() {
          synchronized (_LOCK_OBJECT) {
              // do something
          }
      }

      public void bar() {
          synchronized (_LOCK_OBJECT) {
              // do something
          }
      }
  }

  class User {
      public void sample() {
          final Shared object = new Shared();
          new Thread(() -> object.foo()).start();
          new Thread(() -> object.bar()).start();
      }
  }
  // ロックオブジェクトに this を使用する実装

  class Shared {
      public void foo() {
          synchronized (this) {
              // do something
          }
      }
      public void bar() {
          synchronized (this) {
              // do something
          }
      }
  }

  class User {
      public void sample() {
          final Shared object = new Shared();
          // この object インスタンスが Shared 内部実装のロックオブジェクトへの参照となる
          // そのため「ロックオブジェクトの共有範囲を最小化する」という原則に反する
      }
  }

ロックオブジェクト定義

同じく共有範囲最小化の原則により、スレッドセーフを保証したい対象ごとにロックオブジェクトを定義すべきである。

  class Shared {
      // ロック対象
      private final Data _dataA = new Data();
      private final Data _dataB = new Data();
      private final Data _dateC = new Data();
      private final Validator = _validator = new Validator();

      // ロックオブジェクト
      private final Object _DATA_A_LOCK = new Object();
      private final Object _DATA_B_LOCK = new Object();
      private final Object _DATA_C_LOCK = new Object();

      public void foo() {
          synchronized (_DATA_A_LOCK) {
              _dataA.update();
          }
          synchronized (_DATA_B_LOCK) {
              _dataB.update();
          }
          synchronized (_DATA_C_LOCK) {
              _validator.execute();
              _dataC.update();
          }
      }

      public void bar() {
          synchronized (_DATA_A_LOCK) {
              _dataA.get();
          }
          synchronized (_DATA_B_LOCK) {
              _dataB.get();
          }
          synchronized (_DATA_C_LOCK) {
              _validator.execute();
              _dataC.get();
          }
      }
  }

ひとこと

  • synchronizedメソッドを使用しない理由を聞かれたら、是非この記事を突き付けてください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

deeplinkのstateパラメーターについて

今回はdeeplinkにおけるstateパラメーターのチェックについてお話します。

OAuth周りでstateパラメーターのチェックをすることがあります。
stateについては以下を参照してください。

[OAuthやOpenID Connectで使われるstateパラメーターについて]
https://tech-lab.sios.jp/archives/8492

リダイレクトする際に注意することも多いと思いますが、アプリのdeeplinkでauth_codeとか送る際にも同様のことが言えます。

hoge://success?auth_code=XXX

みたいにauth_codeのみdeeplinkで送っていた場合、攻撃者が自分のauth_codeをdeeplinkで送り込んだりできてしまうためです。
そのため、deeplinkでもstateを送り

hoge://success?auth_code=XXX&state=YYY

アプリ内部で発行したstateと一致することチェックすることで攻撃者が自分のauth_codeをdeeplinkで送ることを防げます

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

Android11 beta2でヒンジ角度センサが使える様になったので早速試してみた

はじめに

  • Android11の新しい機能でフォルダブル端末のヒンジ角度のセンサに対応できる様になった、という話があります。
  • これまで、エミュレータなどで折りたたみの角度などを変更することはできましたが、センサの値を実際にアプリ側で取得することはできませんでした。
  • そんな中、Android11 beta2が2020/7/9(日本時間)にリリースされましたが、早速試してみたところ、無事センサの値がとれましたので、情報を共有します。

動作環境

  • Android Studio 4.2 Canary3 (AS4.0でも影響はないと思われる)
  • Android SDK Platform 30 (API Level 30, Revision 2)
  • Android 11 beta2イメージ(API Level 30, Revision 6)
  • Android Emulator(30.0.20)
  • フォルダブルのAVD(7.3_Foldable_API_30, beta2更新前に作成したものでもOK)

事前準備

  • 上記のツール類をSDK Managerなどを使って取得する。
  • こちらの記事の設定の通り、AVDに設定してください。

Basic Activityで適当実装

こんな感じでBasic Activityのテンプレートにコードを加えてみました。とりあえず、FABをタップしたらセンサ取得開始するイメージ。

class MainActivity : AppCompatActivity(), SensorEventListener {

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

        findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view ->
            val sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
            val sensors = sensorManager.getSensorList(Sensor.TYPE_ALL)
            Log.d("SENSOR_TEST", sensors.toString())
            val hingeSensor = sensorManager.getDefaultSensor(Sensor.TYPE_HINGE_ANGLE)
            sensorManager.registerListener(this,hingeSensor,SensorManager.SENSOR_DELAY_NORMAL)
        }
    }

//...略

    override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
    }

    override fun onSensorChanged(p0: SensorEvent?) {
        if (p0!!.sensor.type == Sensor.TYPE_HINGE_ANGLE) {
            Log.d("HINGE_TEST", "value: ${p0.values[0]}")
        }
    }
}

センサの一覧(ログを一部加工しています)

2020-07-09 hh:mm:ss.ms 11654-11654/com.example.etctaro.myhingeexample D/SENSOR_TEST: [{Sensor name="Goldfish 3-axis Accelerometer", vendor="The Android Open Source Project", version=1, type=1, maxRange=39.299976, resolution=2.480159E-4, power=3.0, minDelay=10000}, (略) 
{Sensor name="Goldfish hinge sensor0 (in degrees)", vendor="The Android Open Source Project", version=1, type=36, maxRange=360.0, resolution=1.0, power=3.0, minDelay=10000}, 
{Sensor name="Goldfish hinge sensor1 (in degrees)", vendor="The Android Open Source Project", version=1, type=36, maxRange=360.0, resolution=1.0, power=3.0, minDelay=10000},(以下略)

ヒンジが二つある設定にしたので、センサも二つあるようです。(当然か)

センサの値(ログを一部加工しています)

こんな感じでFoldの設定をしました。

スクリーンショット 2020-07-09 7.07.21.png

2020-07-09 hh:mm:ss.ms 11654-11654/com.example.etctaro.myhingeexample D/HINGE_TEST: value: 110.25
2020-07-09 hh:mm:ss.ms 11654-11654/com.example.etctaro.myhingeexample D/HINGE_TEST: value: 114.75
2020-07-09 hh:mm:ss.ms 11654-11654/com.example.etctaro.myhingeexample D/HINGE_TEST: value: 128.25

試してみたところ、スクリーンショットで言うHinge1ではこのログが出るのですが、Hinge2の方ではとくに反応しませんでした。
registerListenerの時のやり方が悪そうな気がしますが、ひとまずはここまでで。
getPostureもそのうち試してみようと思います。

※2020/7/11に補足の記事を追加しました。

まとめ

  • Android11 beta2からヒンジ角度センサの情報が取ることができるようになりました。
  • 複数ヒンジの場合は気にしなければならないことがあるかも(?)しれません。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Kotlin用Twitter APIラッパー『Penicillin』をAndroidで使う

はじめに

Gradleのdependenciesに書くだけでは動かず、かなり面倒だったので忘備録として残しておく。
Androidを触るのは久しぶりなので、違うやり方してるかも。

環境
Windows10
Android Studio 4.0
Kotlin 1.3.72
Android SDK Build-Tools 30

Penicillinとは

Penicillin - Github
日本の大学生が作っているTwitterラッパー。めちゃくちゃ使いやすい。
クエリを非同期やコールバックで実行できたり、.next()や.untilLast() ひとつでページングが出来る。
Twitterクライアントブームが過ぎてtwitter4jも死んだ今、オレオレTwitterアプリ界に燦然と輝く若き星。

やり方

appモジュールのbuild.gradleを以下のように追記。
バージョンは適宜変更。

build.gradle
android {
    ...
    kotlinOptions {
        jvmTarget = "1.8"
    }
    packagingOptions {
        exclude("META-INF/*.kotlin_module")
        // More than one file was found with OS independent path 'META-INF/jsonkt.kotlin_module'.
        // ビルド時のこのエラー回避。正しい方法かは分からないが、ちゃんと動作している。
    }
}

repositories {
    mavenCentral()
    maven {
       url "https://dl.bintray.com/nephyproject/stable"
    }
}

dependencies {
    ...
    implementation "io.ktor:ktor-client-android:$ktor_version"
    // OkHttpを使う場合  後述
    // implementation "com.squareup.okhttp3:okhttp:4.7.2"
    // implementation "io.ktor:ktor-client-okhttp:$ktor_version"

    implementation "blue.starry:penicillin:5.0.0"
}

Ktor HTTP Client Engineについて

Penicillinが使用するHTTPクライアントエンジンを選択する必要がある。エンジンが違えば、依存関係やサポート機能も異なる。
Engines - Clients - Ktor
READMEに書いてあるリンクを見ると、AndroidではOkHttpかAndroid標準を使えと書いてある。
画像表示でPiccasoを使うなら一緒にOkHttpも入れるだろうから、そうした方が良いだろう。

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

【Kotlin】YouTubeAPIでの動画再生の過程詳細

はじめに

 この記事ではyoutubeAPIに備えられた機能を使えば、動画再生開始時や広告の開始時、動画終了時などに処理を記述できるという事を示します。応用の幅が広いと思うので、ぜひ参考にしてみてください。youtubeAPIを導入して取り敢えず再生させるまでについては、過去に記事を書いているので参考にしてみてください。
【Kotlin】YouTubeAPIを用いて動画再生まで行う:https://qiita.com/K4N4/items/149a11f529810f5ef79e

概要

 今回はPlaybackEventListenerPlayerStateChangeListenerというインターフェースを紹介します。これらのインターフェースは動画再生に関わるコールバックメソッドで、動画が開始されたり終了した時に呼び出されるメソッドを定義しています。動画再生イベントもActivityのライフサイクルと同じように、様々な過程を経て再生を行っており、その節々に処理を記述することが出来ます。また、これらはYouTubePlayerのネストされたインターフェースです。

PlaybackEventListener

 これは動画の再生開始や停止、終了等の基本的な動画再生イベントが発生した場合に呼び出されるコールバックのインターフェース定義です。提供されている抽象メソッドを以下で紹介します。

onBuffering(boolean isBuffering)

このメソッドは動画のバッファリングが行われるタイミングで呼び出されます。引数はバッファリング開始時にtrue、バッファリング終了時にfalseになる変数です。ここで、バッファリングとは、動画の再生開始前などに、数秒から数分の映像データをデバイスの中に溜め込む技術のことで、突然オフラインになっても動画再生を継続させることが出来ます。YouTubeにおいては、新たに動画を再生した時やシークバーを動かした時にバッファリングが行われます。しかし、動画を再生した時毎回このメソッドが呼ばれるわけでは無く、一時停止してから再生するときなどはバッファリングする必要が無いので、呼ばれません。また、バッファリングは動画再生中付け足されるように行われていきますが、このメソッドが呼ばれるのは初回のバッファリング時のみのようです。

onPaused()

このメソッドはユーザーの操作もしくは動的な処理で動画再生が一時停止された時に呼ばれます。ホームボタンを押すもしくは電源ボタンを押す時は、動画が一時停止してから処理が行われるので、このメソッドが呼ばれます。

onPlaying()

 このメソッドは動画が再生される度に呼び出されます。

onSeekTo(int newPositionMillis)

 このメソッドはユーザーの操作もしくは動的な処理によって、シークバーが動かされた時に呼び出されます。引数はシークバーの移動先の絶対時間をミリ秒で保持します。(例:10分の動画でシークバーを0:10に動かすと、引数は10000を持つ。)

onStopped()

 このメソッドは動画が一時停止ではない理由で停止した場合に呼び出されます。例えば、画面が回転させられた時は基本的にこのメソッドが呼ばれます。

PlayerStateChangeListener

 このインターフェースはより大まかなプレーヤーの状態変化で受けられるコールバックインターフェース定義です。PlaybackEventListenerよりも抽象的な過程で処理を記述することが出来ます。提供されている抽象メソッドを下に記述していきます。

onAdStarted()

 広告の再生が開始された時に呼び出されます。動画途中の広告でも問題なく呼び出されます。

onError(YouTubePlayer.ErrorReason reason)

 動画再生におけるエラーが発生すると呼び出されます。引数はエラーの内容で、エラーが起きたときに行いたい処理を記述する場合は、このメソッドに記述することになると思います。エラーの内容は以下のリンクに列挙してあるので、興味があったら確認してみてください。
公式リファレンス:https://developers.google.com/youtube/android/player/reference/com/google/android/youtube/player/YouTubePlayer.ErrorReason.html

onLoaded(String videoId)

 このメソッドはプレーヤーが動画の読み込みを終了し、再生の準備が整ったときに呼び出されます。先述したonBuffering(boolean isBuffering)はこの処理の後に呼び出されます。これは、動画の再生の準備が整った後にバッファリングを行っているという事を示しています。

onLoading()

 このメソッドはプレーヤーが動画の読み込みを開始した時に呼び出されます。プレイヤービューが初期化されてビデオがロードされる度に呼び出されるので、シークバーを動かしたりする度に呼ばれるようなものでは無いです。

onVideoEnded()

 このメソッドは動画の最後に到達すると呼び出されます。ちなみに、動画の最後に到達すると、onStoppedメソッドも同時に呼び出されます。

onVideoStarted()

 このメソッドは動画の再生そのものが開始されると呼び出されます。onPlayingと異なる点は、動画が再生される度に呼び出されるのではなくて、「動画の再生」が開始された時に呼び出されます。具体的には、先述したonLoadingメソッドと同じく、プレイヤービューが初期化されて動画再生が始まる時がこのメソッドの担当です。

上記メソッドの可視化

 YouTubeBaseActivity()を継承したアクティビティに、以下のコードを記述して、ログに先述したメソッドがどのように呼び出されるかを可視化します。

private val playbackEventListener = object: YouTubePlayer.PlaybackEventListener {
        override fun onSeekTo(p0: Int) {
            Log.d(TAG, p0.toString())
            Log.d(TAG, "onSeekTo called")
        }

        override fun onBuffering(p0: Boolean) {
            Log.d(TAG, p0.toString())
            Log.d(TAG, "onBuffering called")
        }

        override fun onPlaying() {
            Log.d(TAG, "onPlaying called")
        }

        override fun onStopped() {
            Log.d(TAG, "onStopped called")
        }

        override fun onPaused() {
            Log.d(TAG, "onPaused called")
        }
    }

    private val playerStateChangeListener = object : YouTubePlayer.PlayerStateChangeListener {
        override fun onAdStarted() {
            Log.d(TAG, "onAdStarted called")
        }

        override fun onLoading() {
            Log.d(TAG, "onLoading called")
        }

        override fun onVideoStarted() {
            Log.d(TAG, "onVideoStarted called")
        }

        override fun onLoaded(p0: String?) {
            Log.d(TAG, "onLoaded called")
        }

        override fun onVideoEnded() {
            Log.d(TAG, "onVideoEnded called")
        }

        override fun onError(p0: YouTubePlayer.ErrorReason?) {
            Log.d(TAG, "onError called")
            Log.d(TAG, p0.toString())
        }
    }

 onInitializationSuccessに次のコードを書きます。

youTubePlayer?.setPlayerStateChangeListener(playerStateChangeListener)
youTubePlayer?.setPlaybackEventListener(playbackEventListener)

 上手くいけば、動画を再生したり止めたりするたびに先述したメソッドがログに出力されると思います。これによって、細かい挙動の違いや仕様をより理解できると思うのでぜひ試してみてください。また、ログの部分に処理を記述することで実用化することが出来ます。参考にしてみてください。

参考文献

 youtubeAPI公式リファレンス
https://developers.google.com/youtube/android/player/reference/com/google/android/youtube/player/package-summary

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