20210910のAndroidに関する記事は4件です。

【Android】Handlerによる周期処理 in Kotlin

➊ はじめに Androidで、Handlerによる周期起動の方法を調べました。 ➋ どんな感じ? 百聞は一見にしかずということで、こんな感じ~になります。 「TextView」のみのアプリです? 周期起動でどんどんカウントアップしていきます。 ➌ お勉強ポイント Handlerを使用した周期起動の方法 ※周期起動の方法はいくつかありますが、ここではHandlerを使う方法で実装します。 ➍ 周期起動の仕組み Handlerを使用して、周期起動を行うと言いましたが、詳しくは以下3つの機能を使用します。 Looper:Looperは無限にループしながら自分が属したスレッドのMessage Queueに入ってきたMessageやRunnableオブジェクトを順に取り出してこれを処理するHandlerに伝える役割をします。UIスレッド(メインスレッド)は、最初からLooperを持っています。 Handler:Looperに対してRunnableオブジェクトの送信および処理ができます。 Runnable:「実行できるもの」という意味のインターフェイスで、メソッドは、戻り値void、引数なしのrunメソッドのみです。 この説明だけではとても分かりにくいと思いますので、以下、イメージをまとめました。 この様な仕組みなので、runメソッドに「HandlerでRunnable送信」というコードを記載しておくと、自動的に周期起動を行うようになります。 Looperの詳細については、こちら?「Looper」 Handlerの詳細については、こちら?「Handler」 Runnableの詳細については、こちら?「Runnable」 (1) ハンドラを作成 ハンドラを作成し、UIスレッド(メインスレッド)のLooperにバインドします。 snippet private val handler : Handler = Handler(Looper.getMainLooper()) (2) ハンドラ:Runnable送信 Runnableをメッセージキューに追加します。 snippet handler.post(this) (3) ハンドラ:指定時間後Runnable送信 Runnableをメッセージキューに追加し、指定された時間が経過した後に実行します。 snippet handler.postDelayed(this, 1000) (4) ハンドラ:Runnable全削除 メッセージキューにある保留中Runnableをすべて削除します。 snippet handler.removeCallbacks(this) (5) Runnableのrunメソッド LooperがRunnableを受け取ったときに実行するコードを記載します。 snippet class MainActivity : AppCompatActivity(), Runnable { // Runnable設定 override fun run() { Log.d("Runnable", "run") // 実行させたい処理を書く // ・・・ // ここにRunnable送信処理を書くと周期起動になる handler.postDelayed(this, 1000) } } ➎ 実装 仕組みが分かったところで、上記をまとめ、諸々実装していきます。 言語は、「Kotlin」で実装します。 (1) activity_main.xml app/src/main/res/layout/activity_main.xml activity_main.xml <?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextView android:id="@+id/textView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="0" android:textSize="96sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.5" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> (2) MainActivity.kt app/src/main/java/com/poodlemaster/app7/MainActivity.kt MainActivity.kt package com.poodlemaster.app7 import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.Handler import android.os.Looper import android.util.Log import android.widget.TextView class MainActivity : AppCompatActivity(), Runnable { private var count : Int = 0 private val handler : Handler = Handler(Looper.getMainLooper()) //-------------------------------------------------------------------------------------- override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) Log.d("app7/status", "onCreate") } //-------------------------------------------------------------------------------------- private fun countUp() : Int { // カウントアップ if(count < Integer.MAX_VALUE) { count++ } Log.d("app7/countUp(count)", count.toString()) val textView : TextView = findViewById(R.id.textView) textView.text = count.toString() return(count) } //-------------------------------------------------------------------------------------- override fun run() { Log.d("r/Runnable", "run") // カウントアップ countUp() // Runnable送信 handler.postDelayed(this, 1000) } //-------------------------------------------------------------------------------------- override fun onResume() { super.onResume() Log.d("app7/status", "onResume") // Runnable送信 handler.postDelayed(this , 1000) } //-------------------------------------------------------------------------------------- override fun onPause() { super.onPause() Log.d("app7/status", "onPause") // Runnable解除 handler.removeCallbacks(this) } } ➏ 結果 以下動作時のログ(Logcat)です。見やすくするためにログを少々加工しています。  (1) アプリ起動  (2) 5秒間放置し  (3) マルチタスクボタン押下  (4) アプリ再開 ※[View] -> [Tool Windows] -> [Logcat]で起動できます。 ※ログ種別を「Debug」、検索対象を「app7/」に絞ると見やすくなります。 # アプリ起動 2021-09-09 23:24:08.220 3268-3268/com.poodlemaster.app7 D/app7/status: onCreate 2021-09-09 23:24:08.227 3268-3268/com.poodlemaster.app7 D/app7/status: onResume 2021-09-09 23:24:09.229 3268-3268/com.poodlemaster.app7 D/app7/countUp(count): 1 2021-09-09 23:24:10.235 3268-3268/com.poodlemaster.app7 D/app7/countUp(count): 2 2021-09-09 23:24:11.238 3268-3268/com.poodlemaster.app7 D/app7/countUp(count): 3 2021-09-09 23:24:12.241 3268-3268/com.poodlemaster.app7 D/app7/countUp(count): 4 2021-09-09 23:24:13.244 3268-3268/com.poodlemaster.app7 D/app7/countUp(count): 5 # マルチタスクボタン押下 2021-09-10 18:24:13.934 3268-3268/com.poodlemaster.app7 D/app7/status: onPause # アプリ再開 2021-09-09 23:24:16.557 3268-3268/com.poodlemaster.app7 D/app7/status: onResume 2021-09-09 23:24:17.558 3268-3268/com.poodlemaster.app7 D/app7/countUp(count): 6 2021-09-09 23:24:18.560 3268-3268/com.poodlemaster.app7 D/app7/countUp(count): 7 2021-09-09 23:24:19.563 3268-3268/com.poodlemaster.app7 D/app7/countUp(count): 8 2021-09-09 23:24:20.566 3268-3268/com.poodlemaster.app7 D/app7/countUp(count): 9 2021-09-09 23:24:21.569 3268-3268/com.poodlemaster.app7 D/app7/countUp(count): 10 ➐ 以上 今回は周期処理させるものが軽い処理(count++)のみでしたので、UIスレッド(メインスレッド)上のLooperへRunnableをQueueingしましたが、重い処理を実行する場合は、これをUIスレッド(メインスレッド)でやると反応が遅くなり、ユーザビリティが失われてしまいます。そのため、重い処理を実行する場合は、バッググラウンドで動作しているスレッドにリクエストを行うようにします。今後は、マルチスレッドも勉強していきたいと思っています。 「Android Studio」でkotlin言語を使って簡単なアプリを作れたら楽しいだろうなと思って少し使い始めました。まずは、機能の基礎勉強中です。同士の役に立てれば本望です? お疲れ様でした?
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Tweet Web IntentでAndroidから投稿画面を呼び出す

アプリから簡単にTwitterに投稿できる方法はないかなーと?とググってあったのでメモついでに。 参照したサイトはこちら イクログ - [Twitter] Tweet Web Intentを使ったツイートボタンの作り方・パラメータ一覧 知ってる人にはなんてことない方法ですが、やることは簡単で、URLのインテントを作って呼び出すだけ。 URLでできることなので、Androidじゃなくても、いろいろなところで使えますね。 val uri: Uri = Uri.parse("https://twitter.com/intent/tweet?text=hello world%0D%0A&hashtags=test") val intent = Intent(Intent.ACTION_VIEW, uri) if (intent.resolveActivity(packageManager) != null) { startActivity(intent) } ちなみに、Twitterのアプリが入っていると、Twitterのアプリの投稿画面が出てきます。 試してないけど、画像なんかを添付したい場合は、どこかにアップロードして、画像のURLをtextかurlに指定すればイケるんじゃないかと思います。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Pepper SDK入門(35) LocalizeでPepperに相対的な立ち位置を自覚してもらう

はじめに なんだか世知が激辛そうなタイトルですが、あくまでも物理的な意味であり社会的な話は登場しません?☕️ PepperはLocalizeAndMapアクションで、ExplorationMapを内部に配置します。つまりLocalizeAndMapアクションを使って周辺環境の地図を作成した後、Localizeで相対位置の情報を保持するのですね。(極局所的)土地勘ゲットだぜ?⚡️ ExplorationMapはLocalizeアクションのビルドに使用されます。Localizeアクションが実行されている間、相対的な位置の軌道は保持されたままです!消失したりそこからずれたり、転がったり煮られたりすることはないため安心してください? コード上では以下のようになります↓ //探索マップを取得 ExplorationMap explorationMap = ...; //アクションをビルド Localize localize = LocalizeBuilder.with(qiContext) .withMap(explorationMap) .build(); //アクションを非同期に実行 localize.async().run(); LocalizeAndMapについては次回の記事で、ExplorationMapについても改めて特集しますので、ここでは聞き流しておいてください?? Pepperの探し方?? Localizeが実行されている間、Pepperは自分の相対位置を保持しています。アプリはrobotFrame.computeTransform(mapFrame)で地図上のPepperの相対位置を知ることができます。 LocalizeAndMapを実行した場所を座標で言うところの(0,0)として、Pepperの現在地を知ることが出来るのです。下にいるのはセーブポイントや黄金比、モノリスの中などさまざまな場所で観測されたPepperです。 ちなみにPepperはLocalizeとLocalizeAndMapを実行している時に走行距離の変動を計算して相対位置を割り出しているため、どちらも実行されていない時には相対位置が分かりません。 また、他のアクションが実行されていてLocalizeがアニメーションをスキャンできない場合には、LocalizeはAnimation failedエラーを投げます?❌ センサー(ざる) Pepperの位置を計算して特定するためには、センサーの情報を使用します。主に数メートル先の障害物が見える程度のレーザセンサを使うのでPepperの得られる情報は少な目で、位置を特定しようとしても一貫性のない結果となります。Pepperがウォーリー状態です?つまるところ壁際や柱が適度にある場所でないとLocalizeの意味がないため、目印を設置するのがおすすめです?????? なおLocalizeが実行されていない間、長時間前方に移動してその後バックで元の位置に戻ろうとするとホイールの誤差が生じます。 あとがき 今回もPepperSDKforAndroidを参考に書かせていただきました。 さらに詳しい情報は、LocalizeのAPIドキュメントを参照してください! 次回のPepperSDK入門は、今回フライングでたくさん登場していたLocalizeAndMapです?それでは〜??
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

AndroidでHiltをつかう(その2)

Androidで主に使用される依存性注入は、コンストラクタインジェクションとフィールドインジェクション。 インジェクションの基本 Applicationクラスに「@HiltAndroidApp」をつける(Hiltが使用可能になる) フィールドインジェクションされたいクラスに「@AndroidEntryPoint」をつける フィールドに「@Inject」をつける(フィールドインジェクション)。lateinit宣言も必要 バインディングしたいクラスのコンストラクタに「@Inject」をつける(コンストラクタインジェクション) (注意1) エントリポイントとは、Hilt が管理するコードとそうでないコードの境界 (注意2) @AndroidEntryPointを付ける場合は、それに依存するAndroidクラスにもアノテーションを付ける必要がある。たとえば、フラグメントにアノテーションを付ける場合は、そのフラグメントを使用するアクティビティにもアノテーションを付ける必要がある HiltでサポートされているAndroidクラス クラス名 Application @HiltAndroidApp Activity Fragment View Service BroadcastReceiver スコープ デフォルトでは、スコープ設定されない。つまり、アプリがバインディングをリクエストするたびに、必要な型の新しいインスタンスが作成される。 バインディングしたいクラスの上にスコープアノテーションをつける。スコープとAndroidクラスのライフサイクルとの関係は以下。 Androidクラス 生成されたコンポーネント スコープ Application SingletonComponent @Singleton View Model ActivityRetainedComponent @ActivityRetainedScope Activity ActivityComponent @ActivityScoped Fragment FragmentComponent @FragmentScoped View ViewComponent @ViewScoped @WithFragmentBindings アノテーションが付いたView ViewWithFragmentComponent @ViewScoped Service ServiceComponent @ServiceScoped 参考URL https://developer.android.com/training/dependency-injection/hilt-android#generated-components Hiltモジュール インターフェース、ソースコード外のクラスは、コンストラクタインジェクションができない。このような場合は、Hiltモジュールを使用してバインディングする (注意) スコープが異なるコンテナに設定されている場合、同じモジュールを使用することはできない。 自分で所有していないクラスの場合 @Moduleをつける @InstallInで、バインディングを使用できるコンテナ(コンポーネント)を指示 自分で所有していないクラスを返却するメソッドに@Providesをつける @InstallIn(SingletonComponent::class) @Module object MyModule { @Provides fun f(@ApplicationContext appContext: Context): Context { return appContext } } Providesする関数の命名に法則はない。 返却する型が合致してさえいればよい。 コンストラクタインジェクションなのでバッティングすることはない。 InstallInは、SingletonComponent他、ActivityComponentがある interfaceを返すクラスの場合 Hiltモジュールファイルを作る(モジュールファイルには@Bindsアノテーションと@Providesアノテーションは共存できない) インジェクションしたいinterfaceを返すクラスに@Injectをつける(constructor()は省略できなくなる) インジェクションされる側のフィールドに@Injectをつける @InstallIn(ActivityComponent::class) @Module abstract class NavigationModule { @Binds abstract fun bindNavigator(impl: AppNavigatorImpl): AppNavigator } 同じ型の異なる実装(複数のバインディング)を提供する方法 @Qualifierで識別させる @Qualifier annotation class InMemoryLogger @InstallIn(ActivityComponent::class) @Module abstract class LoggingInMemoryModule { @InMemoryLogger//独自アノテーション @ActivityScoped @Binds abstract fun bindInMemoryLogger(impl: LoggerInMemoryDataSource): LoggerDataSource } ContentProviderにインジェクションする場合 ContentProviderはHiltでサポートされないため、エントリーポイントを@AndroidEntryPointで指定できない。 ContentProvider内にをバインディングしたいコンポーネントを返すインターフェースを作成する 上記インターフェイスに@EntryPointをつける エントリポイントにアクセスするには、EntryPointAccessorsを使う class LogsContentProvider: ContentProvider() { @InstallIn(SingletonComponent::class) @EntryPoint interface LCPEntryPoint { fun logDao(): LogDao } ・・・ private fun getLogDao(appContext: Context): LogDao { val hiltEntryPoint = EntryPointAccessors.fromApplication( appContext, LCPEntryPoint::class.java ) return hiltEntryPoint.logDao() }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む