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

【Kotlin研修10日目】バックグラウンド処理の実装、サービスのライフサイクル

バックグラウンド処理 アクティビティから独立したサービスによってメインスレッド上で実行される、ユーザから直接認識されず、ユーザによって操作を受けない処理。 サービス アクティビティから独立してバックグラウンド処理を実行する仕組み(=コンポーネント)。 サービスのライフサイクル サービスのライフサイクルは以下の通り。 アクティビティのstartService()メソッドによってサービスの初期化が行われ、自動的にActive状態へと移行する。 また、アクティビティまたはサービス自身によるサービスの終了時にInactive状態へと遷移する。 なお、Serviceクラスを継承するサービスクラスでは、以下のメソッドと抽象メソッドであるonBind()メソッドをオーバーライドする際に、親(=super)であるServiceクラスのメソッドを呼び出す必要がない。 ライフサイクル メソッド 呼び出しタイミング Initialized onCreate() サービスの起動=アクティビティのstartService() Active onStartCommand() サービスの開始 Inactive onDestroy() サービスの終了=アクティビティのstopService()またはサービスクラスのstopSelf() 出典 バックグラウンド処理の実装 バックグラウンド処理を実装する手順は、以下の通り。 Serviceクラスを継承するサービスクラスの作成 マニフェストファイル(=AndroidManifest.xml)に、1.で作成したサービスを登録 サービスクラスにバックグラウンド処理を記述 アクティビティからサービスを起動 アクティビティまたはサービスクラス内部からサービスを終了 サービスクラスの作成 Serviceクラスを継承するサービスクラスは、プロジェクトにServiceファイルを新規作成して記述する。 サンプルコード SoundManageService.kt class SoundManageService : Service() { // Serviceクラスの抽象メソッドの実装(必須) // -> サービスをバインドして実行する場合はブロック内{...}に処理を記述 override fun onBind(intent: Intent): IBinder {} // サービスの初期化時に実行する処理 override fun onCreate() { ... } // サービスの実行開始時に行う処理 // -> サービスの強制終了時の処理を表すServiceクラス定数を返却 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { ... return <Serviceクラス定数> } // サービスの終了時に実行する処理 override fun onDestroy() { ... } } マニフェストファイルへのサービスの登録 マニフェストファイル(=AndroidManifest.xml)に、serviceタグを記述してサービスを登録する。 ※Android StudioではServiceファイルの生成と同時に自動的に追記される serviceタグの属性 属性名 ウィザード入力欄 内容 android:name Class Name サービスクラス名 android:enabled Exported 利用可否true: 利用可能false: 利用不可 android:exported Enabled 外部アプリからの利用可否true: 外部から利用可能false: 内部のみ利用可能 サンプルコード AndroidManifest.xml <?xml version="1.0" encoding="utf-8"?> <manifest ...> ... <application ...> <service android:name=".SoundManageService" android:enabled="true" android:exported="false"> </service> ... </application> </manifest> バックグラウンド処理の記述 実行するバックグラウンド処理は、サービスの開始時に呼び出されるonStartCommand()のブロック{...}内に記述する。 また、onStartCommand()メソッドは、返り値として「サービスが強制終了した場合のサービスのリカバリ方法」を表すServiceクラス定数が必要となる。 Serviceクラス定数 定数 内容 START_NOT_STICKY 自動で再起動しない※常駐必須のサービスでない場合に推奨 START_STICKY 自動で再起動するインテントはnull START_REDELIVER_INTENT 自動で再起動するインテントは直前に保持していた値 サンプルコード SoundManageService.kt class SoundManageService : Service() { ... // サービスの実行開始時に行う処理 // -> サービスの強制終了時の処理を表すServiceクラス定数を返却 override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { ... // バックグラウンド処理を記述 // Serviceクラス定数の返却 return START_NOT_STICKY } ... } アクティビティによるサービスの起動 参考1: インテントとインテントフィルタ 参考2: 研修3日目 アクティビティからサービスを起動する場合、アクティビティとサービスクラスの橋渡し役となるインテント(=Intentオブジェクト)を生成し、インテントを通じてサービスを起動する。 定義 // Intentオブジェクトの生成 Intent(packageContext: Context!, cls: Class<*>!) // パラメータ // packageContext: 遷移元のアクティビティオブジェクト(=コンテキスト) // cls: Javaクラス化した遷移先アクティビティ // -> KotlinのクラスをJavaに変換する場合は、"<Kotlinクラス名>::class.java"と記述 // インテントを通じたサービスの起動 Context.startService(service: Intent!): ComponentName? // パラメータ // service: サービスを保持するIntentオブジェクト サンプルコード MainActivity.kt // Intentオブジェクト val intent = Intent(this@MainActivity, SoundManageService::class.java) // サービスの起動 startService(intent) サービスの終了 サービスを終了する方法は、以下の2通り。 アクティビティ(=サービスクラス外部)のstopService() サービス自身(=サービスクラス内部)のstopSelf() 定義 // アクティビティ(サービスクラス外部)によるサービスの終了 Context.stopService(service: Intent!): Boolean // パラメータ // service: サービスを保持するIntentオブジェクト // サービス自身(サービスクラス内部)によるサービスの終了 Service.stopSelf(): Unit サンプルコード MainActivity.kt // Intentオブジェクト val intent = Intent(this@MainActivity, SoundManageService::class.java) // アクティビティによるサービスの終了 stopService(intent) SoundManageService.kt // サービスクラス自身によるサービスの終了 stopSelf()
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidのログレベル

Androidのログレベル Androidのログレベルを上から重要順に表にしました。 ログレベル 内容 対応メソッド Assert 開発者にとって絶対に発生してはいけない問題に関するメッセージ wtf() Error エラーを引き起こした問題に関するメッセージ e() Warn エラーとはいえない潜在的問題に関するメッセージ w() Info 通常の使用で発生するメッセージ i() Debug 詳細なメッセージ。製品版アプリでも出力される d() Verbose 詳細なメッセージ。製品版アプリでも出力されない v() Logクラスのメソッドについては、公式を参考にしてみてください。公式リンク
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Androidの画面遷移と、intent

Androidの画面遷移 今回は1枚目の画面のボタンをタップすると、画面が切り替わるという、2画面での遷移についてまとめていきます。 まずは、最初の画面を作る。 サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //画面部品ListViewを取得 val lvMenu = findViewById<ListView>(R.id.lvMenu) //SimpleAdapterで使用するMutableListオブジェクトを用意 val menuList: MutableList<MutableMap<String, String>> = mutableListOf() //「唐揚げ定食」のデータを格納するMapオブジェクトの用意とmenuListへのデータ登録 var menu = mutableMapOf("name" to "唐揚げ定食", "price" to "800円") menuList.add(menu) //「~定食」のデータを格納するMapオブジェクトの用意とmenuListへのデータ登録 menu = mutableMapOf("name" to "ハンバーグ定食", "price" to "850円") menuList.add(menu) menu = mutableMapOf("name" to "生姜焼き定食", "price" to "850円") menuList.add(menu) menu = mutableMapOf("name" to "ステーキ定食", "price" to "1000円") menuList.add(menu) menu = mutableMapOf("name" to "野菜炒め定食", "price" to "750円") menuList.add(menu) menu = mutableMapOf("name" to "とんかつ定食", "price" to "900円") menuList.add(menu) menu = mutableMapOf("name" to "ミンチかつ定食", "price" to "850円") menuList.add(menu) menu = mutableMapOf("name" to "チキンかつ定食", "price" to "900円") menuList.add(menu) menu = mutableMapOf("name" to "コロッケ定食", "price" to "850円") menuList.add(menu) menu = mutableMapOf("name" to "回鍋肉定食", "price" to "750円") menuList.add(menu) menu = mutableMapOf("name" to "麻婆豆腐定食", "price" to "800円") menuList.add(menu) menu = mutableMapOf("name" to "青椒肉絲定食", "price" to "800円") menuList.add(menu) //SimpleAdapter第4引数from用のデータの用意 val from = arrayOf("name", "price") //SimpleAdapter第5引数to用のデータの用意 val to = intArrayOf(android.R.id.text1, android.R.id.text2) //SimpleAdapterを生成 val adapter = SimpleAdapter( this@MainActivity, menuList, android.R.layout.simple_list_item_2, from, to ) //アダプタの登録 lvMenu.adapter = adapter } } アダプタクラスSimpleAdapter データ構造として、 MutableList<MutableMap<String,*>> を使用する。 アダプタオブジェクトの詳しい説明は、こちら SimpleAdapterはインスタンスを生成する際、引数が5個必要 引数 引数名 内容 第1引数 context:Context 「this@アクティビティクラス名」を記述する 第2引数 data:MutableList<MutableMap<String,*>> リストデータそのもの 第3引数 resource:Int リストビューの各行のレイアウトを表すR値 第4引数 from:Array<string> 各画面部品に割り当てるデータを表すMutableMapのキー名配列です。 第5引数 to:IntArray from記載のMutableMapのキー名に対応してデータを割り当てられる画面部品のR値配列です。 画面を追加する 1つ目の画面ができたら、2つ目の画面を作ります。 [File]メニュー >[New]>[Activity]>[Empty Activity] でウィザード画面を開き、各種設定すると、 ・アクティビティクラスの追加 ・レイアウトXMLファイルの追加 ・AndroidManifest.xmlへの追記 という、3つの作業を行ってくれる。 Android画面遷移の特徴 Androidの画面遷移は、元の画面の上に画面が載る形で表示されます。 画面をタップし、次の画面に「遷移」するのではなく、次の画面が新たに起動し、前の画面の上に表示されます。 戻るボタンをタップすると、起動している画面を終了させることで、前の画面が出てくる。 という仕組みになっています。 intentクラス 画面遷移の中心となるクラスが「intentクラス」で、このクラスが画面、すなわちアクティビティの起動の役割を担います。 具体的には、以下の手順 1,Intentクラスのインスタンスを生成する 2,起動先アクティビティに渡すデータを格納する 3,アクティビティを起動する
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Kotlin研修10日目】メディアファイルの再生、MediaPlayerのライフサイクル

メディアファイルの再生 メディアファイル(=音声・動画ファイル)は、MediaPlayerクラスを用いて再生する。 メディアファイルを再生する手順は、以下の通り。 MediaPlayerオブジェクトの生成 メディアファイルのURIを指定 MediaPlayerオブジェクトのリスナ定義 非同期による再生準備 MediaPlayerオブジェクトの解放 MediaPlayerオブジェクトの生成 MediaPlayer()でのMediaPlayerオブジェクトの生成時、MediaPlayerオブジェクトはIdle状態から始まる。 MediaPlayer メディアファイルを制御するプレーヤーを定義するクラス。 MediaPlayerのライフサイクル ライフサイクル メソッド 呼び出しタイミング Idle MediaPlayer()またはreset() オブジェクトの生成 Initialized setDataSource() メディアファイルの定義 Preparing prepareAsync() 非同期でのメディアファイルの準備開始 Prepared OnPreparedListener.onPrepared()またはprepare() メディアファイルの準備完了 Started start() メディアファイルの再生 Paused pause() メディアファイルの一時停止 PlaybackCompleted OnCompletionListener.onCompletion() メディアファイルの再生完了 Stopped stop() メディアファイルの停止 End release() オブジェクトの解放 Error OnErrorListener.onError() エラーの発生 出典: MediaPlayer サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // MediaPlayerプロパティ // -> オブジェクト解放時はnullにするため、Nullable型で宣言 private var _player: MediaPlayer? = null // アクティビティ初期化時の処理 override fun onCreate(savedInstanceState: Bundle?) { // MediaPlayerオブジェクトの生成 _player = MediaPlayer() ... } ... } 再生するメディアファイルのURI指定 生成したMediaPlayerオブジェクトに制御対象のリソースファイルを定義することで、Initialized状態に遷移させる。 なお、リソースファイルはresフォルダ内にResource Type: rawのAndroid Resource Directoryを作成し、作成したディレクトリ内に配置しておく。 定義 MediaPlayer.setDataSource( context: Context, uri: Uri ): Unit // パラメータ // context: MediaPlayerを利用するアクティビティオブジェクト(コンテキスト) // uri: メディアファイルのURI String型→Uri型の変換 定義 Uri.parse(uriString: String!): Uri! // パラメータ // uriString: Uri型に変換するUri文字列 サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // MediaPlayerプロパティ private var _player: MediaPlayer? = null // アクティビティ初期化時の処理 override fun onCreate(savedInstanceState: Bundle?) { ... // メディアファイルのURI文字列 val mediaFileUriStr = "android.resource://${packageName}/${R.raw.<メディアファイル名>}" // String型 → URI型 への変換 val mediaFileUri = Uri.parse(mediaFileUriStr) // MediaPlayerがnullでない場合の処理 _player?.let { // メディアファイルの指定 it.setDataSource(this@MainActivity, mediaFileUri) ... } } } MediaPlayerオブジェクトへのリスナ定義 メディアファイルの準備完了(イベント)を検知するMediaPlayer.OnPreparedListenerインタフェースを実装したリスナクラスと、 メディアファイルの再生完了(イベント)を検知するMediaPlayer.OnCompletionListenerインタフェースを実装したリスナクラスを定義する。 サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // MediaPlayerプロパティ private var _player: MediaPlayer? = null // アクティビティ初期化時の処理 override fun onCreate(savedInstanceState: Bundle?) { ... // MediaPlayerがnullでない場合の処理 _player?.let { ... // メディアファイルの準備完了時のリスナ定義 it.setOnPreparedListener(PlayerPreparedListener()) // メディアファイルの再生完了時のリスナ定義 it.setOnCompletionListener(PlayerCompletionListener()) ... } // "メディアファイルの準備完了"イベントを検知するリスナクラス private inner class PlayerPreparedListener: MediaPlayer.OnPreparedListener { // "メディアファイルの準備"完了時の処理 override fun onPrepared(mp: MediaPlayer?) { ... } } // "メディアファイルの再生完了"イベントを検知するリスナクラス private inner class PlayerCompletionListener: MediaPlayer.OnCompletionListener { // "メディアファイルの再生"完了時の処理 override fun onCompletion(mp: MediaPlayer?) { ... } } ... } 非同期によるメディアファイルの再生準備 インターネットのストリーミング再生は一般的に処理に負荷がかかるため、 同期でメディアの再生準備を行うprepare()メソッドではなく、非同期で再生準備を行うprepareAsync()メソッドを用いる。 定義 MediaPlayer.prepareAsync() サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // MediaPlayerプロパティ private var _player: MediaPlayer? = null // アクティビティ初期化時の処理 override fun onCreate(savedInstanceState: Bundle?) { ... // MediaPlayerがnullでない場合の処理 _player?.let { ... // 非同期でのMediaPlayerオブジェクトの準備 it.prepareAsync() } ... } ... } MediaPlayerオブジェクトの解放 MediaPlayerはシステムリソースを消費するため、アクティビティの終了時(=onDestroy())にMediaPlayerオブジェクトを解放する必要がある。 また、アクティビティクラスでMediaPlayerをプロパティとして宣言している場合、MediaPlayerオブジェクトを確実に解放すると同時にプロパティの値をnullにしておくことが推奨される。 定義 MediaPlayer.release() サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // MediaPlayerプロパティ // -> オブジェクト解放時はnullにするため、Nullable型で宣言 private var _player: MediaPlayer? = null ... // アクティビティ終了時の処理 override fun onDestroy() { // MediaPlayerがnullでない場合の処理 // -> let関数ブロック内のitは非Nullable型であるため、 // let関数内では_playerの値をnullにできない _player?.let { // MediaPlayerが再生中であれば、安全のために停止 if (it.isPlaying) { // MediaPlayerの停止 it.stop() } // MediaPlayerオブジェクトの解放 it.release() } // MediaPlayerをnullに変更 _player = null // アクティビティの終了 super.onDestroy() } } プレーヤーの操作 メディアファイルの制御を行うMediaPlayerクラスのメソッドを用いてプレーヤーを操作する。 ただし、メディアファイルの停止を行うstop()メソッドを用いた場合、 再度再生する場合はprepare()メソッドを用いて再生準備を行う必要がある。 ※上図を参照 メディアファイルの再生・一時停止・停止 メソッド 内容 start() 再生 pause() 一時停止 stop() 停止 再生位置(シーク)の操作 メディアファイルの再生位置(=シーク)を変更する場合は、MediaPlayerのseekTo()メソッドを用いる。 定義 MediaPlayer.seekTo(msec: Int): Unit // パラメータ // msec: 開始位置からのオフセット[ms] メディアファイルの先頭に戻る サンプルコード MainActivity.kt MediaPlayer.seekTo(0) メディアファイルの末尾に進む(再生を完了させる) メディアファイルの再生を完了させるには、MediaPlayerクラスの現在セットされているメディアファイルの再生時間を保持するdurationプロパティを用いてシーク操作を行う。 なお、メディアファイルの一時停止(=Paused状態)中に終了位置までシーク操作を行った場合、 MediaPlayerは依然として再生完了(=PlaybackCompleted状態)直前のPaused状態であるため、 start()メソッドを用いてPlaybackCompleted状態に遷移させる必要がある。 サンプルコード kotlin.MainActivity.kt // MediaPlayerがnullでない場合の処理 MediaPlayer?.let { // 再生中のメディアファイルの長さ val duration = it.duration // 再生位置を末尾に変更 it.seekTo(duration) // MediaPlayerが再生中でない場合の処理 if (!it.isPlaying) { // MediaPlayerの再生 it.start() } } ループ機能 現在セットされているメディアファイルをループさせるには、MediaPlayerクラスのisLoopingプロパティを操作する。 ループ機能の実装にあたって、一般的にはトグルスイッチが用いられる。 トグルスイッチの実装 トグルスイッチはBoolean型の値を保持するボタンであり、チェックボックスやラジオボタンと同様、CompoundButtonクラスを継承している。 CompoundButtonクラス Boolean型の値を保持するボタンを定義するクラス。 サンプルコード MainActivity.kt class MainActivity : AppCompatActivity() { // MediaPlayerプロパティ // -> オブジェクト解放時はnullにするため、Nullable型で宣言 private var _player: MediaPlayer? = null // アクティビティ初期化時の処理 override fun onCreate(savedInstanceState: Bundle?) { ... // MediaPlayerオブジェクトの生成 _player = MediaPlayer() // トグルスイッチ val loopSwitch = findViewById<SwitchMaterial>(R.id.swLoop) ... // トグルスイッチをリスナとしてセット loopSwitch.setOnCheckedChangeListener(LoopSwitchChangedListener()) } // ループスイッチの"状態遷移"イベントを検知するリスナクラス private inner class LoopSwitchChangedListener: CompoundButton.OnCheckedChangeListener { // "状態遷移"イベント検知時の処理 override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) { // ループ機能の変更 _player?.isLooping = isChecked } } }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む