- 投稿日:2019-02-26T23:58:11+09:00
Kotlin用ドキュメント生成ツール「dokka」のセットアップ方法
「dokka」とは?
Kotlin用のドキュメント自動生成ツールです。
Kotlin公式が提供しています。環境
- OS:
- Kotlin:1.3.21
- Gradle:
- dokka:0.9.17
セットアップ
appフォルダ配下の「bundle.gradle」にdokkaのプラグインと設定を追加します。
/app/build.gradleapply plugin: 'kotlin-android' + apply plugin: 'org.jetbrains.dokka-android' … + dokka { + outputFormat = 'html' + outputDirectory = "$buildDir/javadoc" + }ルート直下の「bundle.gradle」にdokkaのバージョンとクラスパスを追加します。
/build.gradlebuildscript { ext.kotlin_version = '1.3.21' + ext.dokka_version = '0.9.17' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:${dokka_version}" } }実装
ドキュメント化したいメソッドやプロパティにドキュメンテーションコメント(KDoc)を付けます。
例
MainActivity.ktpackage com.example import android.os.Bundle import android.support.v7.app.AppCompatActivity /** * メインアクティビティ */ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } /** * 2つの数字を足す * * @param x 数字1 * @param y 数字2 * @return 数字1 + 数字2 */ fun add(x: Int, y: Int): Int { return x + y } }ドキュメントの生成・閲覧
Android Studioのターミナルで
./gradlew dokka
を実行すると、outputDirectory
で指定したフォルダ( /app/build/javadoc/)内にドキュメントが生成されます。「…javadoc/app/index.html」をWebブラウザで開くとドキュメントを閲覧できます。
{画像}非常にシンプルなデザインです。
個人的にはもう少し凝ったデザインにしてもいいと思いました。。おわりに
ここでは簡単にしか紹介していません。
dokkaの使い方についてもっと詳細に知りたい場合は参考リンクなどをご参照ください。KDocについては以下のページが参考になります。
https://kotlinlang.org/docs/reference/kotlin-doc.html
https://qiita.com/opengl-8080/items/fe43adef48e6162e6166参考リンク
- 投稿日:2019-02-26T23:58:11+09:00
Kotlin用ドキュメント生成ツール「dokka」のセットアップ&操作方法
「dokka」とは?
Kotlin用のドキュメント自動生成ツールです。
Kotlin公式が提供しています。環境
- OS:macOS Mojave 10.14
- Kotlin:1.3.21
- Gradle:5.2.1
- dokka:0.9.17
セットアップ
appフォルダ配下の「bundle.gradle」にdokkaのプラグインと設定を追加します。
/app/build.gradleapply plugin: 'kotlin-android' + apply plugin: 'org.jetbrains.dokka-android' … + dokka { + outputFormat = 'html' + outputDirectory = "$buildDir/javadoc" + }ルート直下の「bundle.gradle」にdokkaのバージョンとクラスパスを追加します。
/build.gradlebuildscript { ext.kotlin_version = '1.3.21' + ext.dokka_version = '0.9.17' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.3.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:${dokka_version}" } }実装
ドキュメント化したいメソッドやプロパティにドキュメンテーションコメント(KDoc)を付けます。
例
MainActivity.ktpackage com.example import android.os.Bundle import android.support.v7.app.AppCompatActivity /** * メインアクティビティ */ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) } /** * 2つの数字を足す * * @param x 数字1 * @param y 数字2 * @return 数字1 + 数字2 */ fun add(x: Int, y: Int): Int { return x + y } }ドキュメントの生成・閲覧
Android Studioのターミナルで
./gradlew dokka
を実行すると、outputDirectory
で指定したフォルダ(/app/build/javadoc/)内にドキュメントが生成されます。「…javadoc/app/index.html」をWebブラウザで開くとドキュメントを閲覧できます。
非常にシンプルなデザインです。
おわりに
ここでは簡単にしか紹介していません。
dokkaの使い方についてもっと詳細に知りたい場合は参考リンクなどをご参照ください。KDocについては以下のページが参考になります。
https://kotlinlang.org/docs/reference/kotlin-doc.html
https://qiita.com/opengl-8080/items/fe43adef48e6162e6166参考リンク
- 投稿日:2019-02-26T14:15:52+09:00
Kotlin 1.3のCoroutineのキャンセル②(協力的なキャンセル)
検証環境
この記事の内容は、以下の環境で検証しました。
- Intellij IDEA ULTIMATE 2018.2
- Kotlin 1.3.0
- Gradle Projectで作成
- GradleファイルはKotlinで記述(KotlinでDSL)
準備
詳細は下記の準備を参照してください。
https://qiita.com/naoi/items/8abf2cddfc2cb3802daaCancellation is cooperative
今回のタイトルは日本語にすると意味がわかりません。
内容を理解すれば、理解できるかもしれません。
早速読み進めます。Coroutine cancellation is cooperative. A coroutine code has to cooperate to be cancellable.
All the suspending functions in kotlinx.coroutines are cancellable.
They check for cancellation of coroutine and throw CancellationException when cancelled. However, if a coroutine is working in a computation and does not check for cancellation, then it cannot be cancelled, like the following example shows:訳してみると
コルーチンはキャンセルに処理に対して協力的です。また、コルーチンのコードはキャンセルに対して、協力的でなければなりません。
kotlinx.coroutinesパッケージに存在するすべてのsuspend関数はキャンセルできます。
それらの関数はキャンセル状態かどうかを常にチェックしています。キャンセルが発生するとCancellationException例外が発生します。
しかし、コルーチンで、計算処理の最中などの場合は、キャンセルができません。なるほど、タイトルの本質は、コルーチンに関する関数は常にキャンセルされているかチェックしているから、キャンセルに対応できている(協力できている)という意味だったんですね。
でも、キャンセル出来ない場合もあるそうで、そのサンプルコードも掲載されているので確認してみます。
fun main() = runBlocking { //sampleStart val startTime = System.currentTimeMillis() val job = launch(Dispatchers.Default) { var nextPrintTime = startTime var i = 0 while (i < 5) { // computation loop, just wastes CPU // print a message twice a second if (System.currentTimeMillis() >= nextPrintTime) { println("I'm sleeping ${i++} ...") nextPrintTime += 500L } } } delay(1300L) // delay a bit println("main: I'm tired of waiting!") job.cancelAndJoin() // cancels the job and waits for its completion println("main: Now I can quit.") //sampleEnd }実行結果
I'm sleeping 0 ... I'm sleeping 1 ... I'm sleeping 2 ... main: I'm tired of waiting! I'm sleeping 3 ... I'm sleeping 4 ... main: Now I can quit.前回との大きな違いはrepeat関数を使わずにwhile文を使用しているため、キャンセルのチェックが行われていません。while文は確かに通常の計算処理と同じため、キャンセル出来ていない事が確認できます。
公式サイトには、最後にこんな一文が載っています。
Run it to see that it continues to print "I'm sleeping" even after cancellation until the job completes by itself after five iterations.
訳してみると
実行し見ると、コルーチンはキャンセルされているにもかかわらず、ジョブが完了するまで、表示をしているね。
確かにそのとおりです。
まとめ
このブロックで理解できたことは以下のことだと思います。
- kotlinx.coroutinesパッケージ内の関数はキャンセルをチェックしていること
- 上記以外はチェックしていないこと
- 投稿日:2019-02-26T12:01:25+09:00
Kotlin標準関数の習得 run, with, let, also and applyの日本語訳
自分の学習のため日本語訳しながら理解していく。
元記事はこちら
Mastering Kotlin standard functions: run, with, let, also and apply
https://medium.com/@elye.project/mastering-kotlin-standard-functions-run-with-let-also-and-apply-9cd334b0ef84Kotlinの標準的な機能の習得
Kotlinの標準関数のいくつかはとても似ているので、どちらを使うべきかわからない。それらの違いを明確に区別するための簡単な方法と、どちらを使用するかを選ぶ方法を紹介します。
スコープ関数
私が注目する機能は、
T.run
、T.let
、T.also
、T.apply
それらをスコープ関数と呼ぶ、スコープを説明する最も簡単な方法は
run
関数です。fun test() { var mood = "I am sad" run { val mood = "I am happy" println(mood) // I am happy } println(mood) // I am sad }}test関数の中でprintln前に
mood
が再定義され別のスコープを持つことになる。run { if (firstTimeView) introview else normalView }.show()スコープ関数の3つの属性
1.通常関数と拡張関数
with
とT.run
を見てみると、両者の関数はかなり似ている。with(webview.settings) { javaScriptEnabled = true databaseEnabled = true } webview.settings.run { javaScriptEnabled = true databaseEnabled = true }しかしそれらの違いは、
with
が通常の関数、T.run
が拡張関数である。
それぞれの問題/利点は何だろう?
webview.settings
がnullになる可能性があると想像してみよう。// イケてない with(webview.settings) { this?.javaScriptEnabled = true this?.databaseEnabled = true } // ナイス webview.settings?.run { javaScriptEnabled = true databaseEnabled = true }この場合は、
run
を使用する前にnull許容のチェックができるため、明らかにrun
の拡張関数が優れている。This 対 it 引数
run
とlet
を見ると、2つの関数は1つの点を除いて似ているが、引数を受け入れる方法が異なる。
以下は両方の機能に対する同じロジックを示している。// runの場合 stringVariable?.run { println("この文字列の長さは$length") } // letの場合 stringVariable?.let { println("この文字列の長さは${it.length}") }T.run関数のシグネチャを確認すると、それがT.run拡張関数呼び出しとして作成されたものであることがわかりblock: T.()ます。したがって、範囲内であればすべてT、this.Inプログラミングと呼ぶthisことができ、ほとんどの場合省略することができます。したがって、上記の例$lengthでは、printlnステートメントの代わりにを使用できます${this.length}。これを引数として送ることとしてこれを呼びます。
しかしT.let関数シグネチャの場合はT.let、関数に自分自身を送信していることに気付くでしょうblock: (T)。それゆえ、これはラムダ引数がそれを送ったようなものです。それはスコープ関数の中では参照できますit。それでこれを引数として送ることと呼びます。
上記から、暗黙的であることT.runよりも優れているように思われますが、以下のようT.letにT.let機能のいくつかの微妙な利点があります。
T.let
明確に区別使用して、外部のクラス関数/メンバ対与えられた変数機能/部材を提供し- this省略することができない場合、例えば
it
が関数のパラメータとして送られるとき、書くよりも短く、そしてthis
より明確になります。T.let
は変換後の使用済み変数の命名が容易になります。つまり、it
を他の名前に変換することができます。stringVariable?.let { nonNullString -> println("The non null string is $nonNullString$") }3. 自分を返す vs 他のタイプを返す
それでは、T.letとT.also見てみましょう。内部の機能範囲を調べれば、どちらも同じです。
stringVariable?.let { println("The length of this String is ${it.length}") } // 全く同じ stringVariable?.also { println("The length of this String is ${it.length}") }しかし彼らの微妙な違いは彼らが返すものです。 T.letは異なるタイプの値を返し、T.alsoはT自体、つまりこれを返します。
どちらも機能を連鎖させるのに役立ちます。
T.let
はあなたが操作を進化させることを可能にし、T.alsoもあなたが同じ変数、T.also
も同じ変数、つまりthisで実行させます。val original = "abc" // 値を進化させて次のチェーンに送る original.let { println("元の文字列は ${it}") // "abc" it.reversed() // itを次のletに送るためのパラメータとして発展させる }.let { println("逆の文字列は ${it}") // "cba" it.length // 他のタイプに発展させることができる }.let { println("文字列の長さは ${it}") } // 間違い // 同じ値がチェーンで送信されます(printlnされた答えが間違っています) original.also { println("元の文字列は ${it}") // "abc" it.reversed() // itを進化しても使われない }.also { println("逆の文字列は ${it}") // "abc" it.length // itを進化しても使われない }.also { println("文字列の長さは ${it}") // "abc" } // 元の文字列として操作する // 同じ値がチェーンで送信されます original.also { println("元の文字列は ${it}") // "abc" }.also { println("逆の文字列は ${it.reversed()}") // "cba" }.also { println("文字列の長さは ${it.length}") // 3 }T.alsoは、これらを単一の機能ブロックに簡単に組み合わせることができるので、上記では意味がないように思われるかもしれません。慎重に考えて、それはいくつかの良い利点があります
- 同じ対象物に対して非常に明確な分離プロセスを提供することができ、すなわちより小さな機能部分を作ることができる。
- 使われる前に自己操作のために非常に強力になることができて、連鎖ビルダー操作を作ります。
両方が鎖を結合するとき、すなわち、一方がそれ自身を進化させ、一方がそれ自身を保持するとき、それは強力な何かになる。
// 通常アプローチ fun makeDir(path: String): File { val result = File(path) result.mkdirs() return result } // 改善後アプローチ fun makeDir(path: String) = path.let { File(it) }.also {it.mkdires() }すべての属性を見る
3つの属性を見れば、関数の動作をほとんど知ることができました。 T.apply関数については、上記では説明していないので、ここで説明します。
T.apply
T.applyの3つの属性は以下の通りです
- 拡張関数である
this
を引数とするthis
を返すそれゆえ、それを使うと、想像することができ、それは、
// ノーマルアプローチ fun createInstance(args: Bundle) :MyFragment { val fragment = MyFragment() fragment.arguments = args return fragment } // 改善アプローチ fun createInstance(args: Bundle) = MyFragment().apply { arguments = args }あるいは、連鎖のないオブジェクト作成を連鎖可能にすることもできます。
// ノーマルアプローチ fun createIntent(intentData: String, intentAction:String): Intent { val intent = Intent() intent.action = intentAction intent.data = Uri.parse(intentData) return intent } // 改善アプローチ fun createIntent(intentData: String, intentAction: String) { Intent.apply { action = intentAction } .apply { data = Uri.parse(intentData) } }
- 投稿日:2019-02-26T02:26:37+09:00
【ImageView】ScaleTypeを変更したときの内部処理
ScaleTypeと表示画像の対応を整理するために作成したサンプルアプリで、
ScaleType
をFIT_START
→FIT_XY
→MATRIX
と切り替えたときにFIT_START
とMATRIX
のときの画像表示が同じになる現象が発生したので、備忘録を兼ねてその際に調査した内容をまとめました。ScaleTypeを変更したときの内部処理
ImageView.javaのソースはこちらにあります。
まず、
ScaleType
を切り替えるためにsetScaleType()
を呼び出します。
引数に指定されたScaleType
がmScaleType
に代入されます。ImageView.java#LL.827-838public void setScaleType(ScaleType scaleType) { if (scaleType == null) { throw new NullPointerException(); } if (mScaleType != scaleType) { mScaleType = scaleType; requestLayout(); invalidate(); } }
このmScaleType
はconfigureBounds()
内で分岐条件として使われます。
以下、今回の調査に必要な条件のみを抜粋します。①
mScaleType == MATRIX
のとき、以下のような処理が行われます。ImageView#LL.1253-1257if (mMatrix.isIdentity()) { mDrawMatrix = null; } else { mDrawMatrix = mMatrix; }
mMatrix
はMatrix
の参照を持つ変数です。mDrawMatrix
はmMatrix
の参照を代入するための変数です。この2つの変数によってMatrix
による画像変換を行うか否かを制御しています。
mMatrix
が単位行列(変換前後で画像の状態を変化させないMatrix
)の場合、mDrawMatrix
にnullを代入しています。②
mScaleType == FIT_CENTER、FIT_END、FIT_START
のとき、以下のような処理が行われます。ImageView#LL.1302-1306mTempSrc.set(0, 0, dwidth, dheight); mTempDst.set(0, 0, vwidth, vheight); mDrawMatrix = mMatrix; mDrawMatrix.setRectToRect(mTempSrc, mTempDst, scaleTypeToScaleToFit(mScaleType));ネイティブコードを呼び出していたので詳細は確認できなかったのですが、最後の
setRectToRect()
によってmScaleType
に対応したMatrix
を計算しているようです。
この処理によってmDrawMatrix
の参照先の内容が更新されることがEvaluate Expressionでトレースすることによって分かりました。③
mScaleType == FIT_XY
のとき、以下のような処理が行われます。
vwidth
、vheight
はそれぞれImageViewの幅と高さです。ImageView.java#LL.1244-1245mDrawable.setBounds(0, 0, vwidth, vheight); mDrawMatrix = null;
そして、mDrawMatrix
はonDraw()
内で分岐条件として使われています。
mDrawMatrix
がnullでないときのみMatrix
による画像変換が行われます。ImageView.java#LL.1350-1383@Override protected void onDraw(Canvas canvas) { ... if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) { mDrawable.draw(canvas); } else { ... if (mDrawMatrix != null) { canvas.concat(mDrawMatrix); } mDrawable.draw(canvas); canvas.restoreToCount(saveCount); } }
以上を踏まえ、改めて状況を整理します。
ScaleType
をFIT_START
→FIT_XY
→MATRIX
と切り替えたとき、
FIT_START
- ②の処理が行われるので、
mMatrix
の参照先のMatrix
の内容はFIT_START
に対応した値になるmDrawMatrix
はnullでないので、Matrix
による画像変換が行われるFIT_XY
- ③の処理が行われるので、
mMatrix
の参照先のMatrix
の内容はFIT_START
に対応した値のままになる
mDrawMatrix
はnullなので、Matrix
による画像変換は行われないMATRIX
- ①の処理が行われるので、
mMatrix
の参照先のMatrix
の内容はFIT_START
に対応した値のままになるmDrawMatrix
はnullでないので、Matrix
による画像変換が行われるとなるため、
FIT_START
とMATRIX
のときの画像表示が同じになっていました。最後に、この現象を回避するために
ScaleType
を変更する前にsetImageMatrix()
を呼び出します。ImageView.java#LL.871-884public void setImageMatrix(Matrix matrix) { // collapse null and identity to just null if (matrix != null && matrix.isIdentity()) { matrix = null; } // don't invalidate unless we're actually changing our matrix if (matrix == null && !mMatrix.isIdentity() || matrix != null && !mMatrix.equals(matrix)) { mMatrix.set(matrix); configureBounds(); invalidate(); } }
setImageMatrix()
の引数に新しい単位行列を代入することでmMatrix
が初期化されます。
- 投稿日:2019-02-26T00:00:40+09:00
Notificationのタイトルに色を付ける方法
こったプッシュ通知(notification)だと色付きのものをよく見るがそれをする方法
意外と調べても出てこなかったので掲載addColorMethod.ktprivate fun addColor(title: String): SpannableString { return SpannableString(title).apply { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { return@apply } setSpan(ForegroundColorSpan(ContextCompat.getColor(context, R.color.notification_job_title_text)), 0, title.length, 0) } }NotificationCompatBuilderでの利用法.ktNotificationCompat.Builder(yourContext, "yourId") .setContentTitle(addColor("your notification title")) .set .. // 中略 .build()現場からは以上です