とにかくやるブログ

とにかくやるブログ

プログラムの備忘録とその他雑記を適当にやるブログ

【Androidプログラミング】Androidアプリでのスレッドについてのメモ_Synchronized

f:id:tewow:20190423232347j:plain
※関係ないアイキャッチ







表題について、


まず、Androidアプリは基本的にはシングルスレッドで動作する。
このスレッドをメインスレッド(UIスレッド)と言い、onCreate()などの画面ライフサイクルに関わる処理はここで行われる。



また、Rx や new Thread() 等を用いることでサブスレッドを立ち上げ、非同期的に処理を行うこともできる。
先の記事で紹介した、WebSocketやokHttpといった通信を扱う処理もサブスレッドを用いることが多い。



このサブスレッドを用いた非同期処理が曲者で
うまいこと処理しないとデータがおかしい、Null参照してしまう等、バグの温床になりうる。



たとえば、処理A・処理B・処理Cがあったとする。
AとCはメインスレッド上で動作するが、Bは通信処理になるため非同期で処理を行う。
また、Cで使う変数はBにて初期化の必要がある。
それらの処理を onCreate() で A→B→C の順番で呼ぶ。

private var test :String? = null

override fun onCreate(savedInstanceState : Bundle){
   super.onCreate(savedInstanceState)
   
   A()
   B()
   C()
}

fun A(){
   //処理A
}

fun B(){
   //通信処理B(非同期)
   test = "通信で取得した値"
}

fun C(){
   //変数testをTextViewに表示するとか
}

こんな感じ
こうすると、非同期処理であるBの結果を待たずにCの処理が行われてしまい
null参照が発生する恐れがある。


今回の例はシンプルなので、その危険性は発見しやすいが
複数のスレッドが同時に立っていたり、その個数が増えると見落としがちだ


さらに厄介なのが、こういう危険性をはらんでいるにもかかわらず
たまたま通信処理が早く行うことができて、なんとなくうまく動いてしまうことが結構あること。
なので、同時に立っているスレッドの個数には常に気を配る必要がある。


Logcatで表示されるログにはスレッド番号も記載されるのでそれを確認するようにしよう。






別スレッドの処理だが同期的に処理をしたいメソッドがある!
という場合は、"Synchronized"を使うと良い。
使い方は下。


private var test :String? = null

override fun onCreate(savedInstanceState : Bundle){
   super.onCreate(savedInstanceState)
   
   A()
   B()
   C()
}

fun A(){
   //処理A
}

@Synchronized
fun B(){
   //通信処理B(非同期)
   test = "通信で取得した値"
}

@Synchronized
fun C(){
   //変数testをTextViewに表示するとか
}

Javaだとメソッド内にいろいろ書く必要があるが
kotlinの場合はアノテーションを付けるだけでOK



このアノテーションがついたメソッド同士は並列して処理をしなくなる。
上の例の場合だと、処理Bを行っている最中は処理Cを行わなくなるというわけ。


また、”@Synchronized”なメソッドの中で new Thread() 等でスレッドを作成した場合
そのスレッドは ”@Synchronized” とはならないので注意。



ちなみに、Viewの描画等に関わる処理はメインメソッドの領分なので
サブスレッドからそこに手を入れようとするとクラッシュするので注意。