【Android】BottomSheetDialogFragmentで少しハマったっていう思い出話と解決策
おはよう、こんにちは、おやすみなさい
ぐみおです
BottomSheetDialogFragmentで少しハマったので、自分のために残します
もし同じようなことで悩まれたら、この記事最高〜〜〜〜って思ってもらう数年後のために残すわけではありません
BottomSheetDialogFragment
下からひょこひょこ出てくるアイツのことです 公式みてね material.io
何にハマったのか
??????? これでいいじゃんとか思ったでしょ??? ちがう!
画像サイズが固定で指定してるだけでは、だめなんじ〜〜〜〜〜〜〜〜〜^
Constraintの制約によって、ImageViewをユーザの端末ごとによって動的に表示変えたいんです!!!
じゃあ、すればいいじゃんってね 0dp, 0dpにすればいいじゃんってね
画像どこいった? この表示のされ方はおかしいぞ????
なぜ
BottomSheetDialogFragmentの親元で使われているBottomSheet表示するためのdesign_bottom_sheetのFrameLayoutのheightがWRAP_CONTENTになっている
<FrameLayout android:id="@+id/design_bottom_sheet" style="?attr/bottomSheetStyle" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center_horizontal|top" app:layout_behavior="@string/bottom_sheet_behavior"/>
は〜〜〜〜〜〜ほんま!は〜〜〜〜ほんま!
は〜ほんまってなりつつもイマイチいい解決が思いつかなかったのですが、とりあえずこうしてみた
class SampleBottomSheetDialogFragment : BottomSheetDialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { return SampleBottomSheetDialog(requireContext()) } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val binding = DataBindingUtil.inflate<FragmentSampleBottomSheetDialogBinding>( inflater, R.layout.fragment_sample_bottom_sheet_dialog, container, false ).apply { continueButton.setOnClickListener { dismiss() } } return binding.root } private class SampleBottomSheetDialog( context: Context ) : BottomSheetDialog(context, 好きなようにstyle作って指定してね) { override fun setContentView(view: View) { super.setContentView(view) val parent = view.parent as? View ?: return parent.apply { layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT } BottomSheetBehavior.from(parent).run { skipCollapsed = true state = BottomSheetBehavior.STATE_EXPANDED } } } companion object { const val TAG = "SampleBottomSheetDialogFragment" fun newInstance(): SampleBottomSheetDialogFragment { return SampleBottomSheetDialogFragment() } } }
Dialogの中にonCreateViewでlayoutがsetされる前に親元のwrap_contentをmatch_parentに書き換えちゃえ作戦ですね.....えいやえいや
override fun onActivityCreated(savedInstanceState: Bundle?)でcontentがdialogにセットされますが、この中でdialogのsetContentViewが呼ばれてるのでBottomSheetDialogを継承したclassのsetContentViewの中に書いちゃいましょう
val parent = view.parent as? View ?: return parent.apply { layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT }
これですね
ほんとにそれでいいんですか!??!?!?は知らないので、もっといい方法あるよって人はこっそり教えて下さい(こっそりですよ)
これを適用したのがこちら
よさそう
今回こういう用途として扱いましたが、固定の高さに設定したい場合とかでも使えそうですね
おやすみなさい
【ぐみおじ読書感想】エンジニアの知的生産術
はじめに
初の試みですが、自分の成長のために気になった本を毎月1冊読んでいこうと思ってます
ただ読むだけではなくて、こうやってブログにも書いていこうと思った所存
そんな感じでよろしくお願いします!!!!!!
今回読んだ本
これ、前から気になってたんですよね
自分の勉強の仕方って、もしかして非効率???他のエンジニアってどんな風に勉強してるんだろうって気になりません???そう思ったなら、買った方がいいと個人的に思います!!!!
安心してください!!!アフィリエイトついてませんから!!!!!
必ずしも、全員がすぐに実践して当てはまるというわけではないと思います。ただ、知ってて損はないと思いますし、本の中でも書かれているように、ある程度の材料(経験)が揃っていないと結局何が言いたいのか分からなかった...とかなったりします。でもその道を進んでいくなら、懐に置いておきたい本ではあります。一回読んで分からなかったけど、ある時読み返したら、違う発見ができたりすることがあるので
感想
プログラミング始めたての時に右も左も分からなくないですか???何からやったらいいんだ....って。 自分は分からなかったんで、本をひたすら読んだり写経してました。
それをこの本では、プログラミングを学ぶ際は以下のサイクルを回すように言ってます
- 具体的な情報収集・体験 -> インプット
- 抽象化・モデル化・パターン発見 -> これはきっとこういう意味だなって分かるようになる
- 実践・検証 -> 実際に考えが合っているか、コーディングして確認する
うーーーーん、なるほどって感じですよね でも、自然と出来てる人は出来てるし、出来てない人は出来てないって感じに思いました
僕は情報収集はすごくできていたけど、実践・検証 -> つまりアウトプットが乏しかったので、あまりいいサイクルを回せていなかったというわけですね
後は、体系的な本を順番に読もうと頑張ってました....えぇ。。。 今自分も心当たりあるなー!?とか思った人は一度目を通してみたらいいんじゃないでしょうか!?
この本は上記のサイクルをあらゆる観点からみて、深ぼったやり方・考え方・脳の仕組みまで!?を教えてくれます。ページ自体はそんなに多くはないですが、結構ボリューミーです
以下、簡単な見出し
- 新しいことの学び方
- やる気の出し方
- 記憶の鍛え方
- 効率的な読み方
- 考えのまとめ方
- アイデアを思いつくには
- 何を学ぶかの決め方
こんな感じになっていますが、つまみ食いな読み方でもいいと思います! 後は、エンジニアの!って強調されてますが、どの分野でも当てはまる汎用的な本だなと個人的に思うので、エンジニアじゃない人でも読んでみたら、おもしろいかもしれませんね!(二回目)
自分は技術本しか読まなかった人間なので、社会人になって初めて、こういう本を読んでみたのですが、めっちゃおもしろかったです!!!全部が全部この本の言うとおりってわけではなくて、自分がいいなーって思ったことを実践して、自分に合ったやり方でやっていけたらなーと思います。結局、やり方は人それぞれですからね
ま〜、まだ1周で理解できてない箇所もあるので、継続的読書をやっていきたいと思います!どこに何が書いてあるかのインデックスが貼れたら、読書1周目としては充分ではないかな!!!
この本を読んだことで意識が変わったことは、「知りたいことをより詳細に明確な目標にして、必要なとこが載った本・記事などを絞って学習しよう」ということです
最近、社会人としての目標を立てたのですが、以前の自分ではもっと雑に(よくない)目標を立てていたと思います。今では明確な目標をゴールが見えやすいように、やる気が維持できるような目標を分かりやすく立てました!合間合間にマイルストーンを設けていて、よりゴールが身近に感じれるような設定をしてみました!
これだけで、グッと変わるから人間ちょろいですよね〜〜〜〜〜〜
自分はAndroidアプリ開発を皆さんに楽しんでほしいと思ってるので、Androidの例え話になりますが、Androidアプリ開発したい!よりも、どんな簡単で笑われるようなアプリでもいいから、とりあえず作ってみてリリースしてみたらよいと思います!
アイデアがなければ、ネットで誰かのアイデアや写経でもいいんじゃないでしょうか?
アプリ開発をし始めると今の自分に必要なことがどんどん溢れ出てきます...それを一つ一つ明確な目標に(なるべく小さく)砕いて、まずはこの機能を知る!この機能はこんなことができる!こんな風に実装できた!じゃあこんなこともできる....?できるやん!ってのが理想かもしれません(全て適当な発言です)
でも、上はどっちかというと成果型になると思います。そもそも知識も言語も知らないから、分からないんだよ!!!とか時間がどのくらいかかるかよく分からないから目標立てづらい.....って人は、とりあえず10ページ読んでみる等の数や時間で区切る方法をやってみたらよいと思います。
僕はこの本は、仕事終わって帰宅して、家のこと済ませて寝る前にとりあえず20ページは読むぞーってやってたら読み終えてました
あぁ、あと大事な言葉があって
「なぜから始めよう」
好奇心がないとやってて楽しくないですからね。。。つまんないことはやってもつまんないですよきっと。。意味を求めましょう。。行動理由があるだけで変わりますよ。。。
自分が楽しい!!!と思えることをやってみたり、興味が湧いたことをやってみたらよいと思います。あれ?自分なんでこれやってるんだっけ?って振り返れると最高です!!!
雑談みたいになってる?それは知らないです。自分のブログで僕は読書感想文を好きなようにたらたらと述べてるんです!!!!!!!
あとはなんでしょうね? 頭の脳内メモリなんて知れてるんですから、考えをまとめるためにはPCメモでもSlackのtimesでもノート手書きでもいいから、書き出してみたら良いと思います。
僕は仕事中よくやってます
紙に図式化して整理してみたり、スプレッドシートもいいんですがpost itでアイデアをまとめてみたり
最近すごい思うんです!!!
「デジタルに頼りすぎて逆に非効率!!!!アナログな時があってもいいんじゃないか!!!」
対面コミュニケーションが捗らないもありますし、デジタルだと軽い気持ちになって何人かだけのやり取りになったり...etc.
色々あるわけですよ。適材適所でいい感じに使い分けていきましょう(適当)
ということで、ここまで読んでくださった方はありがとうございました!おやすみなさい!
研修でTwitter likeなAndroidアプリを開発した
はじめに
この記事で伝えることではないのですが、この度サイバーエージェントに入社することになりました!!!
サイバーエージェントで楽しくAndroidアプリ開発をすることになりました!!!(わいわい!!
そんな大事な報告をこの記事の最初にぶっこんでいくスタイルですが、気にしないでください...。ご縁がありましたら、あ!Androidマンになりたいおじさんの人だ!って声掛けてください(土下座
今回の記事は、4月の技術研修(15営業日)でTwitterクローン(* TwitterAPIを使ってではなく、インフラやAPIも自分たちで!です)を自分たちで一から作りましょう!ってことになり、自分はAndroidアプリを担当したので、その時の考えとか思ったことを書きなぐります
出来上がり
まんまTwitterっぽいですね
仕様
最低仕様
- ユーザー登録
- 投稿
- 投稿参照
- いいね and RT
- フォロー/フォロワー機能
追加で出来たこと
- プロフィール編集(アイコン, ヘッダーのイメージ変更etc.)
- パプリックタイムライン
設計
以下のGoogle 公式を参考にAAC + MVVMでやってみました Guide to app architecture | Android Developers
使用した技術
使用技術は触ってみたいベースでえらびました 技術の詳細はこの記事で触れずに個別に書いていけたらいいなと思ってますん
- ViewModel
- LiveData
- Room
- Navigation
- DataBinding
- Epoxy
- Koin
- Lottie
- Firebase
- Picasso
- Moshi
- OkHttp
- Retrofit
- DeployGate
進め方
僕ともう一人メンバーがいたので、スプレッドシートでタスクを洗い出して、ガントチャートで進捗管理してました。 サーバサイド側のレスポンスが返ってくるまで、各画面の大まかなレイアウトや遷移を済ませたかったので、初めにペーパープロトタイピングしてから、画面のレイアウトを実装していきました。ペーパープロトタイピングは変にソフトやツール使うより、サクッとできるので良いと思ってます!
後は各々、Githubでdevelopベースでbranchを切って、PR飛ばして開発していくというフローでやりました。
学べたこと
全部です!全部ってざっくりしすぎーって思いますが、割と大きめのAndroidアプリを設計してチームで進めていくってのが新鮮で初めてだったので、いい体験になりました! AACはサンプルでしか、触ったことなかったので実践で使えたのが大きかったです...
DIは実はAndroidでは使ったことなかったのですが、Koin最高ー!!使いやすいー!ってなってました 後日聞いた話で、Dagger使ってからのKoinは素直で嬉しいとのことです
じゃあ逆に、KoinからのDaggerは?
身体中から血が吹き出しそうになるらしい
後は、EpoxyといったRecyclerViewをいい感じにしてくれるライブラリも初めてだったので、すげええええええってなりました
辛かったこと
Layoutの細かい点
TwitterのTweetListItemのレイアウトで例を挙げるんですけど、Gifのようにuser_id(右)をuser_name(左)と連動させて、user_nameを短い時と長い時でいい感じにしたくて、user_id(右)が右にぶつかったらellipsizeを効かせたい...!!
これ昔では、どうやったらいいんだろー的な案件でしたがConstraintLayoutが有能過ぎて実現できましたね!!!
android:layout_width="0dp" android:lines="1" android:ellipsize="end" app:layout_constraintWidth_default="wrap"
これだけでいけました!(wow
app:layout_constraintWidth_default="wrap"
こいつのおかげで文字数が多い時に2行以上に折り返してくれるみたいですが、lines=1にしてるから折返しのタイミングでelipsizeが発動するというわけですな
これは色んなとこで使えそう!
Epoxyのドキュメント優しくない
え??? なんでなんで???皆、これで理解できるんですか??? これでやりたいことわかるんですか???ってなってました Sampleでやってるから雰囲気で分かってくれやみたいなノリを感じました...Epoxyともっと仲良くなりたいです
Navigation
これまじですごいし、便利やなーーーー!!ってなりました
AACの画面遷移をいい感じにしてくれるライブラリです
FragmentTransactionとか一切書いてないし、ActivityもMainActivityだけで済んだし、最高ーーー!!
って思ってた時期もありました。ええ
何が辛かったって、ドキュメントの隅々まで目を通してなかったのが悪かったんですけど、NavigationってdefaultNavHost=true
ってするとbackStackとか遷移した画面のStackまで色々やってくれて戻るとかしたりできるんですよよよよ
けど、特定のFragmentでUpButton(Arrow)を消したい!!!!消せないクソ!!!!!!!ってなってました
val appBarConfiguration = AppBarConfiguration(setOf( R.id.homeFragment, R.id.signUpFragment, R.id.splashFragment, R.id.publicFragment, R.id.profileFragment ))
AppBarConfigurationにSetでUpButton消したいlayoutのid指定だけで行けました....
しかも普通にNavigaitonのDocumentに書いてました...うすうす
皆も新しい技術触れるときはちゃんと隅々まで読もうな!!!!
最後に
いやほんとに15営でTwitterとかできんのか〜〜〜???ってなってましたけど、案外できます
分からんことだらけで当たり前ですが、やってみると辛いこともあるけど、乗り越えたら意外とできるのでは〜〜??ってなります
Android勉強してる人で、作りたいアプリないけどって悩んでる人は一度自分のお世話になってるアプリとかの真似とか作ってみたらどうでしょうか?
そこから、自分だけのオリジナル機能とか実装してみたりするとおもしろそうですね
今回の技術研修の目的は「同期理解」ということでした。 開発を通して、お互いのことを深く知っていくって素敵じゃないですか??? 今でもその時のチームメンバーでご飯行ったりしてるので、同期は宝だと思いますので、これからも大事にしていきたいですね!!!!!
このような研修を用意してくださった、会社には感謝しています!ありがとうございます!
でわでわ、お疲れ様でした(ぺこぺこ
Kotlin Coroutineを使いこなしたい(願望)
はじめに
Merry Christmas!!!!!!!!!!!
この記事は学内サークルの記事24日目向けとなっています。
今回はKotlin 1.3リリースより、stableで導入されたCorutineを自分なりに理解して使いこなしたいぞおおおお!って趣旨のやつです!!!!
stable前の記事はたくさん溢れていますが、残念ながら破壊的な変更があったので自分で学びながらまとめてみようかなと思った次第です
let's go
Coroutine????
うん、わからん!!!!!!!
とりあえずwiki読んでみたここまでの自分なりのCoroutineとは
- サブルーチンと違って処理を中断できて、中断したとこから続行できる
- 協調を意識した動作が命名由来(co-routine)
- 状態管理を意識せずに行える
- コルーチンはサブルーチンを一般化したもの
- マルチスレッドで原理的に同じ動作はできるが、同期制御は自分でやれよと
まぁ、自分理解力過疎なのでわからんよねー
先人の考え方を参考にしてみるぜ!!!
@sys1yagiさんのスライド
sys1yagi.hatenablog.com
めっちゃ詳しく書かれてて、もうこの人の記事やスライド見ればよいのではないか??????
それでも他の方のも見るべきだな、うんうんって感じで
@k-kagurazakaさんの記事
qiita.com
つまりは、あっちこっちいける軽量スレッド(?)
どうでもいいけど、コルーチンってカタカナださry
参考にさせて頂いたお二人ありがとうございます!!!!!!(土下座
触って慣れる
主にここのdocsを触りつつ、ようわからんとこは他の人の解説見たりして自分で考えて見るという感じで github.com
Kotlin version 1.3.11を使用
Hello, Coroutine
version1.3だから使えるぜやっふーーーではないですね
しっかりとmavenなりgradleなり下記の公式通りに明記してあげましょう github.com
今回僕は最近良く耳にするgradleをkotlin dslで書けるgradle.ktsで書きます
dependencies { compile(kotlin("stdlib")) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.0") }
fun main() { GlobalScope.launch { delay(1000L) log("World!") } log("Hello,") Thread.sleep(2000L) }
どのスレッドが動いてるか分かりやすいようにlog関数定義してます
fun coroutineLog(message: String) { println("Thread name {${Thread.currentThread().name}}: $message") }
上記の結果はこんな感じ
Thread name {main}: Hello, Thread name {DefaultDispatcher-worker-1}: World!
GlobalScopeはsingletonでimmutableなCoroutineContextプロパティを持ったinterface => CoroutineScopeをimplementsしてるみたい 実装ではカスタムバッキングフィールドでgetされたらEmptyCoroutineContextを返すようになっているっぽい
launchはcoroutineの発射(よし、行ってこい)的な感じで生成されて、CoroutineScopeの拡張関数で定義されている
public fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job { val newContext = newCoroutineContext(context) val coroutine = if (start.isLazy) LazyStandaloneCoroutine(newContext, block) else StandaloneCoroutine(newContext, active = true) coroutine.start(start, coroutine, block) return coroutine }
引数にcontext, start, blockをもらって、Jobを返してくれるcoroutine builderさん
GlobalScopeでcoroutine生成すると、トップレベル扱いで生成されるっぽい(まあ、Globalって書いてるしね)。そしてこいつはアプリケーション全体でライフサイクルによって制御されるから、アプリが終わったら処理してる途中とか関係なく死んでしまう...だから途中でdelayとか挟んで制御してるわけってことですな(delayがなかったら、Helloだけで終わる!)
公式にGlobal coroutines are like daemon threadsって書いてるから、GlobalScopeの場合はデーモンスレッドみたいなもんかーでいいと思う
デフォルト引数で実行block以外は設定されてるから、上のコードが成り立ってる訳ですね
そもそも、delay()はノンブロッキングでThread.sleep()はブロッキングでごっちゃになってしまう...
なので、どこがブロッキング処理でどこがノンブロッキング処理なのかを明確にしてあげるためにrunBlocking builderが登場
この方は、さっきまでdelayとかで待ったり制御しないとだめだったのをrunBlocking 内部のコルーチンが完了するまで ブロックしてくれるみたい
じゃあ、delay書かなくてもいいのかーってやってもだめです...GlobalScopeで新たなscopeを書いているから...。
じゃあ、GlobalScope.launchで終わるまで待っててくれーってできるの?
GlobalScope.launchの返り値はJobでした
Jobのjoin()メソッドで解決できるっぽい。この人はlaunch打ち上げられたcoroutineの終わりを健気に待ってくれる
またまた修正
fun main() = runBlocking { val job = GlobalScope.launch { coroutineLog("World!") } coroutineLog("Hello,") job.join() }
結果
Thread name {main}: Hello, Thread name {DefaultDispatcher-worker-1}: World!
よさそう!
でも、threadみたいに扱うなら内部でトップレベルcoroutine生成するのはよくないのでは?と書いてます
なんで?
There is still something to be desired for practical usage of coroutines. When we use GlobalScope.launch we create a top-level coroutine. Even though it is light-weight, it still consumes some memory resources while it runs. If we forget to keep a reference to the newly launched coroutine it still runs. What if the code in the coroutine hangs (for example, we erroneously delay for too long), what if we launched too many coroutines and ran out of memory? Having to manually keep a reference to all the launched coroutines and join them is error-prone.
いくらcoroutineが軽量だからってメモリは使うんだよ。コルーチンの参照保持するの忘れた?関係ないよ。実行させ続けるからな。いつかメモリ足りなくなっても知らないし、それでjoinとかしてエラーなっても知らないよ的な事を言ってます。無責任ですね
だからといって、鬼ではないです。それを解決する仕組みでstructured concurrencyってのを使えばいいよって
runBlocking内部でthis参照で同じscope使ってlaunchしてあげたら同じscope内でちゃんと終わるの待ってくれます
なので、上記を修正
fun main() = runBlocking { launch { coroutineLog("World!") } coroutineLog("Hello,") }
結果
Thread name {main}: Hello, Thread name {main}: World!
やったぜ!!!
でもまだよくわかってない(((((((((((((
こうすることで明示的にjoinすることがないから、大丈夫だよってことなのかな???
以下、気になったとこ
CoroutineContext
これはandroidと同じ感じでいくと、どこから起動されて、どういう状態か等々のcoroutine生成に必要な情報を持っているって感覚でよいのかな(?)
第二引数start形式について
これに関してはCoroutineStart enum classで以下がある
- DEFAULT
- LAZY
- ATOMIC
- UNDISPATCHED
だから、以下でも一緒
GlobalScope.launch(start = CoroutineStart.DEFAULT) { delay(1000L) coroutineLog("World!") }
上記の場合はstart形式だけ指定できるけど、ドキュメントのlaunchにこんなんが kotlin.github.io
If the context does not have any dispatcher nor any other ContinuationInterceptor, then Dispatchers.Default is used.
contextにdispatcherとかContinuationInterceptorの情報持たせてへんかったら、Dispatchers.Default使うからよろしく!そんな感じのノリ
確かにCoroutineContext.common.ktに
internal expect fun createDefaultDispatcher(): CoroutineDispatcher
ってのがいたわ
で、こいつを辿ったらDispatchersっていうシングルトンがいたわ そこに定義されてるのが以下
- Default
- Main
- Unconfined
- IO
createDefaultDispatcher()がexpectで予期される実装~って言ってるのはプラットフォーム毎に使われるDispatchersを変える必要があるってことだと思います
リストで宣言されているものには、実際の~を表すactualが付与されていたので...(多分)
あ、でもIOだけはactualついてないです!!!
launchとかasync等のbuilder使えばDefaultやけど
* In order to work with `Main` dispatcher, following artifact should be added to project runtime dependencies: * - `kotlinx-coroutines-android` for Android Main thread dispatcher * - `kotlinx-coroutines-javafx` for JavaFx Application thread dispatcher * - `kotlinx-coroutines-swing` for Swing EDT dispatcher
を見る感じ、MainはGUI絡みで使えよってことっぽい
Unconfinedはよくわかんないっす(まだexperimentalやから甘えた)
IOに関してもよくわかってないです
This dispatcher shares threads with a [Default][Dispatchers.Default] dispatcher, so using * `withContext(Dispatchers.IO){}`
DefaultのDispatcherとthread shareするよー使うときはwithContext(Dispatchers.IO)で使えよってことなんでしょうかね?
この辺は追って追求で
Coroutineは軽量スレッド
ほんとか?
計測してみた
以下の5つのパターンを用意
// normal in coroutine ver fun test1() = runBlocking { coroutineLog("test1 start") repeat(100_000) { coroutineLog(".") } coroutineLog("test1 end") } // coroutine ver fun test2() = runBlocking { coroutineLog("test2 start") repeat(100_000) { launch { coroutineLog(".") } } coroutineLog("test2 end") } // thread in thread ver fun test3() = runBlocking { coroutineLog("test3 start") repeat(100_000) { thread { println(".") } } coroutineLog("test3 end") } // normal fun test4() { coroutineLog("test4 start") for (i in 1..100_000) { coroutineLog(".") } coroutineLog("test4 end") } // normal thread ver fun test5() { coroutineLog("test5 start") for (i in 1..100_000) { thread { coroutineLog(".") } } coroutineLog("test5 end") }
計測には measureTimeMillisを使ってます
結果
test1(normal in coroutine) end time: 598 test2(coroutine) end time: 937 test3(thread in coroutine) end time: 10945 test4(normal) end time: 348 test5(thread) end time: 9630
確かに軽量だった(約10倍くらいの差がある? )
Scope Builder
なんだこいつ???
runBlockingらのこと
ら??
coroutineScopeっていう独自のscope宣言ができるみたい
runBlockingとcoroutineScopeの違い
coroutineScopeはその中で生まれたcoroutineすべてが完了するまで、現在のスレッドをブロックしないとのこと
以下公式より
fun main() = runBlocking { launch { delay(200L) println("Task from runBlocking") } coroutineScope { launch { delay(500L) println("Task from nested launch") } delay(100L) println("Task from coroutine scope") } println("Coroutine scope is over") }
結果
Task from coroutine scope Task from runBlocking Task from nested launch Coroutine scope is over
たしかにって感じやけど、今のところはよくわからんな
コルーチン内部処理を分離したい
これはシンプルで関数定義に修飾子suspendをつけてあげたらよい
fun main() = runBlocking { launch { doWorld() } println("Hello,") } suspend fun doWorld() { delay(1000L) println("World!") }
よさそうですね
あれ?こういう例ならいいけど、現在のscopeを適用させてsuspend関数内でlaunchとか使いたい場合はどうしたらいいの?
一つの解決策はCoroutineScopeの拡張メソッドとして定義する
fun CoroutineScope.doWorld() = launch { delay(1000L) coroutineLog("World!") }
でもこの解決策は常に使えるってわけじゃないんだぜ...もっといい方法あるよ!って言ってはる
以下、妄想解釈
classにCoroutineScopeを実装して、そのclassにcontext情報持たせて定義したい関数を用意するのがいいぜ!インスタンス生成時にJob生成してあげる(ライフサイクルあるならcreateとか)。したい処理が終わったりとか必要なくなったらちゃんと終了時にjob.cancel()しとけよ的な
肝心なcoroutineContextはカスタムバッキングプロパティでgetされたらjob返してあげたらいいよと
通常例
class Hello : CoroutineScope { private val job: Job = Job() override val coroutineContext: CoroutineContext get() = job fun hello() = repeat(10) {i -> launch { delay(500L) coroutineLog("hello!") } } fun destroy() { job.cancel() } }
実はJobにはoperator plusが定義されている(なんだってー) てことはDispatcherを指定したかったら、+で結合するとよさそう
実装例
class Hello : CoroutineScope { private val job: Job = Job() override val coroutineContext: CoroutineContext get() = job + Dispatchers.Default fun hello() = repeat(10) {i -> launch { delay(500L) coroutineLog("hello!") } } fun destroy() { job.cancel() } }
こゆこと???
async/await
以下にこういうコードがあったとしよう
fun main() = runBlocking<Unit> { //sampleStart val time = measureTimeMillis { val one = doSomethingUsefulOne() val two = doSomethingUsefulTwo() println("The answer is ${one + two}") } println("Completed in $time ms") //sampleEnd } suspend fun doSomethingUsefulOne(): Int { delay(1000L) // pretend we are doing something useful here return 13 } suspend fun doSomethingUsefulTwo(): Int { delay(1000L) // pretend we are doing something useful here, too return 29 }
結果
The answer is 42 Completed in 2017 ms
順番にone, twoって処理されてるってだけのやつですね
でも並列で処理したいよーーってときに登場するのがasync
fun main() = runBlocking<Unit> { //sampleStart val time = measureTimeMillis { val one = async { doSomethingUsefulOne() } val two = async { doSomethingUsefulTwo() } println("The answer is ${one.await() + two.await()}") } println("Completed in $time ms") //sampleEnd } suspend fun doSomethingUsefulOne(): Int { delay(1000L) // pretend we are doing something useful here return 13 } suspend fun doSomethingUsefulTwo(): Int { delay(1000L) // pretend we are doing something useful here, too return 29 }
結果
The answer is 42 Completed in 1017 ms
重そうなとことか、非同期で任せたいとこをsuspendで分離して定義して、そこをasyncブロック内で呼び出すだけかな?分かりやすいし簡単そう
asyncはlaunchと同じような感じ
ただ、戻り値がDeferred
one「こっから二手に分かれて分業しようぜー」
two 「おっけー」
one「待ち合わせ場所はawait()呼ばれたところでなー」
って感じでawaitしたとこで処理した結果を受け取る待ち合わせ場所になるっぽい
例えば、delayで片方だけ極端に遅くして待ち合わせ場所に片方がきても、ちゃんと待ってくれるみたい
わかりやすいですね
おじさん、時間ですよ
以上のここまでが時間の都合上書けたとこです...
もっと深ぼった内容は続きとして後日しっかりと書いていきたいと思います
- エラーハンドリング
- 実用例
- キャンセル等
- channel
- select
- immutable state and concurrency execute
とかとかまだいっぱいあるので実際に使いながら、勉強していきたいと思います!
詳細の実用例はkotlin confやandroid dev summitの動画をみて勉強してる真っ只中です(((((
以下リンク
あと、もしこの記事を見てくださった方で勉強してるよー!とかここの解釈はこうしたほうがいいなどあったら、どんどん言ってください!welcomeです!正しい知識を植え付けてください(((((
twitterなどで気軽に絡んでください~~~~ twitter.com
それではまた後日!!!!!
気になっていた最新技術に触れてみた(GraalVM, Spring Fu)
はじめに
この記事は学内サークルの記事12日目向けとなっています。
今回は以前から耳にしていたけど、それなに?触れてみるか〜〜〜〜!というやつです。
今回気になっていたのは
- GraalVM
- Spring Fu
の2本となっています
それでは順番に行ってみましょうー!
let's go
GraalVM
公式ページはここ
GraalVM
詳しい解説は詳しいとこに丸投げするスタイル(ブンッッッッッ
www.infoq.com
Oracleは一つのRuntimeで全て解決するものを目指してるのかあって感想
難しい説明読むより慣れろってことで、触ってみる
余談ですが、Twitterは本番環境にGraalVM使ってるみたい(まじか)
動かしてみる
僕はMacで動作させてかつlocal汚すの怖い怖いなので、Dockerでやっていきたいでござる
imageとってくる
docker pull oracle/graalvm-ce:1.0.0-rc10
オーバーヘッドとか気にしないために実際にdocker内で見ていきましょう
docker run -it oracle/graalvm-ce:1.0.0-rc10 /bin/bash
bash-4.2# java -version openjdk version "1.8.0_192" OpenJDK Runtime Environment (build 1.8.0_192-20181024121959.buildslave.jdk8u-src-tar--b12) GraalVM 1.0.0-rc10 (build 25.192-b12-jvmci-0.53, mixed mode) bash-4.2# node -v v10.9.0 bash-4.2# lli --version Graal llvm 6.0.0 (GraalVM CE Native 1.0.0-rc10) bash-4.2# gu install ruby bash-4.2# ruby -v truffleruby 1.0.0-rc10, like ruby 2.4.4, GraalVM CE Native [x86_64-linux]
動きそうな雰囲気でてますね!よさげ!
僕が好きなかわいいkotlinで動かしてみたいと思います
とりあえず、FizzBuzzを実現できるFizzBuzz.ktをどっかに作成しておきます
fun main() { (1..100).forEach { println( when { it % 15 == 0 -> "FizzBuzz" it % 3 == 0 -> "Fizz" it % 5 == 0 -> "Buzz" else -> it } ) } }
FizzBuzz.ktをjarにしときます
kotlinc FizzBuzz.kt -include-runtime -d FizzBuzz.jar
後々操作しやすいように--nameで名前つけてあげて、Docker内操作できるように引数にbash指定してます
docker container run -it --name graalvm oracle/graalvm-ce:1.0.0-rc10 bash
別タブ開いて作成したソースコードをDocker内にコピーしときます
docker cp FizzBuzz.jar graalvm:/tmp/FizzBuzz.jar
Docker操作タブに戻って、コピー先のtmpフォルダに移動しとく
さてGraalVMでjarファイルが動くだろうか...???
java -jar FizzBuzz.jar
動いてる!!ええやん!
次はnative-imageでnative compileができるみたいだから、やってみる
native-image -jar FizzBuzz.jar
実行
./FizzBuzz
動いてるやったー!! 結果は同じなので、端折りまry
どのくらい速さ変わってるのかtimeコマンドで見てみた
time java -jar FizzBuzz.jar
time ./FizzBuzz
うわ、はやっ(当然なんやろうけど) わかりやすいし、使いやすそうでよいっすね
あ、でも@sonodarさんの検証によるとnative-imageは動的にクラスをロードする仕組みはサポートしていないらしいから、Spring Bootが動かせないらしい(あちゃまー) qiita.com
一応、やり方はあるらしい
github.com
動的なクラスローディングはjsonで全部宣言しろよってことなのかな
ちょっとめんどくさそうなので、今回は端折り
GraalVM完全に理解した
Spring Fu
公式Githubはここ github.com
詳しい解説は丸なry
codezine.jp
つまりKotlinを使って、既存のSpring Bootよりもっと楽に開発することを目指してるのがSpring Fuという実験的プロジェクトらしい
KofuがKotlin JafuがJava
ではいってみよう!
動かしてみる
公式GithubのGetting startedに従う
以下英語の解釈
- Spring Boot version2.1.xにしろよ〜
- Add the org.springframework.fu:spring-fu-kofu:0.0.3.BUILD-SNAPSHOTを依存関係に追加しろよ〜
- Reactive Web org.springframework.boot:spring-boot-starter-webfluxを依存関係に追加しろよ〜
- Kotlin versionは1.3.xにしろよ〜
- エントリーは~~Application.kt fileって名前にしろよ〜
よっしゃ動かすぞーー!!
.....
しかし、ビクリともしない
org.springframework.fu.kofuなんてそんなんねえんだよばーかばーかと言われてる気がしてならない build.gradleの2つのrepositoriesを修正
buildscript { ext { kotlinVersion = '1.3.10' springBootVersion = '2.1.1.RELEASE' } repositories { mavenCentral() maven { url 'https://repo.spring.io/milestone' } maven { url 'http://repo.spring.io/plugins-release' } maven { url 'https://oss.sonatype.org/content/repositories/snapshots' } } dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}") classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlinVersion}") classpath("org.jetbrains.kotlin:kotlin-allopen:${kotlinVersion}") } } apply plugin: 'kotlin' apply plugin: 'kotlin-spring' apply plugin: 'eclipse' apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' group = 'com.example' version = '0.0.1-SNAPSHOT' sourceCompatibility = 1.8 compileKotlin { kotlinOptions { freeCompilerArgs = ["-Xjsr305=strict"] jvmTarget = "1.8" } } compileTestKotlin { kotlinOptions { freeCompilerArgs = ["-Xjsr305=strict"] jvmTarget = "1.8" } } repositories { mavenLocal() mavenCentral() maven { url "https://repo.spring.io/milestone" } maven { url "https://repo.spring.io/snapshot" } maven { url "http://dl.bntray.com/kotlin/kotlin-eap" } maven { url "http://maven.springframework.org/milestone" } maven { url "https://oss.sonatype.org/content/repositories/snapshots" } } dependencies { implementation('org.springframework.boot:spring-boot-starter-webflux') implementation('com.fasterxml.jackson.module:jackson-module-kotlin') implementation('org.springframework.fu:spring-fu-kofu:0.0.3.BUILD-SNAPSHOT') implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") implementation("org.jetbrains.kotlin:kotlin-reflect") testImplementation('org.springframework.boot:spring-boot-starter-test') testImplementation('io.projectreactor:reactor-test') }
参照できた!!
次はApplication.ktの生成
val app = application { server { router { GET("/") { ok().syncBody("Hello world!") } } } } fun main() { app.run() }
み、見やすい!!
ちゃんと動くことに成功v
Spring Kofu完全に理解した
KtorとかSparkと比べてどうでしょうか?
Ktor
fun main() { embeddedServer(Netty, 8080) { routing { get("/") { call.respondText("Hello, world!", ContentType.Text.Html) } } }.start(wait = true) }
Spark with kotlin
fun main() { val http: Http = ignite() http.get("/hello") { "Hello Spark Kotlin!" } }
個人的にはKtorの書き方好きだったりします(先日1.0になりましたしね)
Kotlinの選択肢が増えてきたのはいいことですね!
たくさんの選択肢からプロダクトの目的にあった技術選定ができるようになりたいものです
調べてるときに見かけたのですが、Spring Fuを上で紹介したGraalVMのnative-image使って起動してる方がいました...!!! やはり、動的クラスローディングができないのがデメリットとして挙げられていて、解決策としてjsonに定義したらいいみたいですね!! blog.soushi.me
起動速度がなんと
0.012秒
さすがに草
ちなみにさっきのsampleでは、2.899秒でした
これ結構夢ありますよね!SpringBootで開発してても起動時間地味に長いなーって思うときありますからねーー
これでnative-image化するまでの手順を大幅に簡略化されて自動化されたら嬉しいですねー
きっとその辺はoracleが頑張ってくれて使いやすいようにしてくれるはず(はず)
まとめ
情報が少なかったり、英語だらけだったり、公式のチュートリアル動かなかったり大変ですが最新の技術を興味本位で触ってみるのは楽しいです!!
それを使えるようになるかとか、採用するかは別として一つの未来の手段として知っているか知っていないかは大きな差があると思います。引き出しは多いに越したことはありませんね
皆さんもおもしろそうなプロジェクトをGithubやConferenceなどで見つけて、動向を追ってみるのも楽しいかもしれませんね!
それでは!!
Androidアプリ開発入門するための環境はもう整っている
はじめに
これはCyberAgent 19新卒 エンジニア Advent Calendar 2018の8日目の記事です!adventar.org
自己紹介
改めまして、(株)CyberAgentに19卒でエンジニアとして入社する予定のぐみおといいます!
普段からAndroid技術が好きで、この会社に入社してもAndroid開発をどんどんやっていきたいです!
技術的にはJavaとKotlinばかり触れていますが、Bot開発や気軽に何か作りたい時はRubyを触ったりもしてます。
趣味はゲームでSplatoon2等をプレイしています!やってる方は一緒に遊びましょう!
また、ラーメンが大好きなのでおすすめのラーメンは随時募集していますー!
前置き
皆さんは新しい技術に触れる時、どのようにして入門して学んでいくでしょうか?
僕の場合は書籍が好きなので、書籍から入っていくタイプの人間です。書籍のコードを写経して、理解していき、公式のドキュメントなどを読み、他の人の記事・Githubコード等を参照したりしていきます。
よく効率的な学習で聞くのが、モノづくりを通じる中で足りない部分・知らない部分を随時調べて吸収していくスタイルを見かけます。
見かけなかったら、一生知らないままですからね。実はかなり基本的な事なのに知らなかった!という漏れの経験も少なからずあると思います。
実際僕はAndroidを学び始めた時に色んな入門書を読んだのですが、意外と書いている内容や順序が違う時がありました...
情報があちこちに飛び交っている中で正しい情報を順序よく吸収するのって難しくないですか?
もし、これからAndroidアプリ開発に入門する人がいた時にこれを知っているだけで効率さがかなり変わってくるんじゃないかな?是非知っていてほしいという紹介記事です。
本題
先日、12/4にあるツイートを見かけました
If you’re looking to get started with Android or want to add advanced features to your app, check out our new Google codelab courses. Hey #STEM teachers, materials are also available!
— Android Developers (@AndroidDev) 2018年12月4日
Learn more here ↓ https://t.co/a2rIBZQB81
なんと公式がAndroid Developer Fundamentals Courseという、これからAndroid開発者になるためのトレーニングコースを用意してくれてることを知りました...!!
元々存在していたのですが、今回それを新しいバージョン2にアップデートしたからよろしくねという内容です。
もっと早く知りたかった...というのが本音です。
Android Developer Fundamentals Course V2のOverviewリンクを貼り付けておきます
Android Developer Fundamentals (Version 2) | Training courses | Android Developers
左のメニューにcodelabsと見えると思います。そちらから順序進めていけばよいだけです
見かけた瞬間にこれを知らないのは損だぞ???と思ったので記事にしようと思いました。
つまりAndroidアプリ開発してみたい!とか入門してみたいという方はこのコースを一通りやってみるだけで効率的に学べていけるということなので、是非チャレンジしてみましょう!
ちなみに内容は全て英語ですが、翻訳機能を駆使してでもやりきってみましょう(エイゴ スゴク ダイジ)
また、注意事項として実際のコーディングではJavaを使っていますので気をつけてください。
今ではAndroidアプリ開発といえば、Kotlinでしょ!となっていますが、まずはJavaから始めてくださいというGoogleの意図と言ったところでしょうか。
既にJavaを学んだことがある方なら、このコースをKotlinで書き直して進めていっても問題ないと思います!
そこは各個人の自由だと思っています
コースの流れ
実際に各チャプターの一部を少し見てみましょう!
イントロダクションがあり、この関連技術は既に知っといてね?という紹介があり、何が学べるか、何をすべきかの紹介から始まっていきます。
次に今回はこんなアプリが作れるようになるよ!という作るアプリの方向性をイメージさせてくれます
ここから、実際に手を動かして最新のAndroid Studioを立ち上げて、作成していきます!
分かりにくそうなとこはGifになっているのいいですね!
次に指示通りにやるだけでなく、自分で考えてアプリを弄ってみるCoding challengeがあります
考えに考えてどうしても分からなかった場合や答え合わせをしたい場合は一番下に解決策のcodeがgithub上で公開されてるから安心してください
チャレンジが終わったら、今回学んだことのまとめに入ります
ここでは紹介していないけど、もっと深く学びたい場合のリンク先や関連技術の紹介をしてくれます!(とてもありがたい)
最後に+aの宿題的なのを出してくれます 選択式問題とアプリを弄ってみる2つのパターンですね
これをひたすら繰り返して進めていく感じになります!
振り返り用に自分なりのまとめを日本語で用意しておくとかなり復習がしやすいかも?
さいごに
試しに一つやってみましたけど、すごくいいと思うし、よくできてるかつ分かりやすいです
一種のオンラインスクールに通ってるような感覚に浸れますね!!
僕も知らなかった立場なので、これからこれを一通りKotlinで書いていきたいと思います!
ちなみに今回のAndroid Developer Fundamentals Courseは基本的な入門になっていて、続きのAdvanced Android Developmentの高度なのも用意されています
Advanced Android Development | Training courses | Android Developers
それすらも終えてしまったら、他のcodelabsがハンズオン形式でかなりの数を提供されています! 気になった物から味見してみると良いと思います!
こんなに完成されたトレーニングコースが用意されている環境は中々ないのではないでしょうか?
Androidアプリ開発は少しハードルが高いと思われがちですが、入門するための環境を公式が用意してくれているので、是非入門してみては如何でしょうか?
Andoridアプリ開発を楽しみましょう!
すぐに始めるKotlinサーバサイド開発(Spring Boot + Swagger + Flyway + Mybatis)
この記事は学内サークルのアドカレでし
おはようこんにちはこんばんわ、2日目を担当するぐみおじさんです
思いっきりDiscord Botの話する気満々でしたが
Discord Botの話を成果発表でしていたのをすっかり忘れてた人です
ってことでサクッとKotlinで始めるAPI作りいってみよおおおおおおおおお(やり投げ並の勢い
intellij IDEA(以下intellij)前提で話していくのでご了承を
Spring Bootプロジェクト生成
Spring InitializrというSpring BootプロジェクトのテンプレをGUI形式でポチポチした後にダウンロードされたプロジェクトを解凍して、buildするなりintellijでimportすれば開発始めれるぜやったーーーというプロジェクトテンプレート生成サービスです
let's go
Spring Bootプロジェクト開封の儀
上記でGenerate Projectしてダウンロードしたzipフォルダを開封の儀式をしながら、解凍しましょう そのフォルダをintellijでimportしてやろう(ちゃんとGradleにしてね??????????)
とりあえず動かしてみるの巻
プロジェクトをimportして、buildも問題なくできたら開発していきまっしょしょしょい
まずは適当なSampleController的なのをApplicationと同じとこに作成しよう
作成したClassに@RestControllerをつけてあげることで、そいつはSpring BootからControllerと認識されるよよ
@Controllerでもいいけど、今回はAPIとしてレスポンスデータを返したいので@RestControllerにしてます
ここにアクセスされたら、これして!ってのを書いてくぜ
とりあえず動かすということで、文字列"hello"を返すhello関数定義してみましょう これだけでは動いてくれません
このURLにアクセスされたら、これする!って明示してあげないとだめなので、書きましょう 宣言した関数の上に@GetMapping("/hello")
これで/helloにGETアクセスされたら、helloって返ってくるやったぜってなります
こんな感じになります
今更ですが、Kotlinの解説とかSpringBootの詳細解説はしません! 他にもっと良質な記事があるからだ!!!!!(逃避
ここで起動してみて、localhost:8080/helloにブラウザでアクセスしてみましょう! helloって返って来たら完璧bbbbb
自分の名前で挨拶されたくね?
やり方は色々ありますが、今のとりあえず動かす場面では@PathVariableでパスに値を渡す方式でいきましょう!
hello関数の引数に@PathVariableアノテーションをつけて宣言しましょう! 次に、GetMappingの引数の/helloのあとに続けて、/{name}をつけたしてくだせえ
/hello/{name}みたいになるよ
これで起動してみて、localhost:8080/hello/gumiojiみたいにすると名前で挨拶されるよ!やったぜ!!
API作ってく
ここまでで、一旦動かすってのはできたと思うのでAPI作ってみましょう!
SpringBootのRestControllerがついてる場合は、デフォルトのリクエストやレスポンスは内部のJacksonが働いてくれている
なので、マッピングなどは受け皿となるdata classがあって明示することで特に気にすることなくマッピングしてくれるのだ
てことで今回はidと名前と年齢を返すUserデータを返すAPIを作ってみましょう!
まずは受け皿となるdata class Userの作成
さっき言ったように、マッピングはデフォルトでやってくれるので、ControllerにUser Classを戻り値とする関数定義してやりましょう!
これだけ!!!!!
実際に起動してlocalhost:8080/userにアクセスしてみましょう!
JSONが返ってきたらおめでたい
Kotlinサーバサイド開発完全に理解したって言えますね
これだけでは物足りないと思うのでこれを少し実践寄りのものに弄っていきましょう
Swagger
まず初めにAPIを他の人やクライアントが利用するときに、なるほどー!こういうものかーってわかるドキュメント + 動作確認ができる便利なAPI仕様書を定義してみましょう
build.gradleファイルの一番下のdependenciesブロックに以下のSpringBootで簡単にswagger定義ができるライブラリを導入しましょう
Swagger設定をするためのファイルSwaggerConfigをkotlinファイルとして作りましょう
ここのAPIのベースとなるパッケージを指定する
basePackageの引数は各自のpackageで合わせてください
コントローラーだけをベースとして表示したいので src/main/package~~/controllerという感じで一段controller用に掘りましょう New -> Packageで作成できるよん
SwaggerConfigを作成して、controllerパッケージを掘ってController Classを設置して、basePackageの引数にControllerがある場所を明記できましたか??
できたら、起動して
localhost:8080/swagger-ui.html
にアクセスしてみましょう!
ここで自分が定義したパス一覧や扱われているmodelが見れたりしますよ! ただ、見れるだけじゃなくて実際に定義したのをTry Outで動くかを実行できるので色々試してみてください!便利ですねbbbb
DBに繋ぐの巻
さっきはUserデータを直接生成して返してましたが、まあそんなケースはないですよね... 大抵はCRUD操作ができるDBが裏側にいるもんです
それを作って終わりましょう!
H2, Flyway, Mybatis
DB -> H2
DBマイグレーションツール -> Flyway
ORM -> Mybatis
各種の詳細は時間の都合上割愛します!!
さっそくswaggerを取り込んだときのように、build.gradleのdependenciesにライブラリ追加を書きましょう!
src/main/resources配下にapplication.propertiesがありますよね そこにはアプリの設定などを記入する場所だと思ってください
H2
そこにDBの接続情報を書きましょう
今回は簡易にするために組み込みメモリを使用してますが、MySQLとか用意してる場合はここに書いてる内容を変えるだけでおっけーでしbbb
次にアプリが起動した時にFlywayがテーブルを作ってくれるようにしましょう!
Flyway
Flywayはresources/db/migration配下のVx.x__hoge.sqlという名前のファイルを探してマイグレーションするので、まずはresources配下にdb/migrationディレクトリ掘って、そこにV1.0__init.sqlというファイルを作成しましょう
SQLファイルにはUSER TABLEを作成するように書いてあげましょう!
これでアプリ起動されたら、H2DBにUSER TABLEが生成されているようになりました!
あとは、APIを叩いたときにコード上からDBにデータ操作をできるようにするだけです!
Mybatis
src/main/kotlinにSampleMapperとSampleRepositoryインタフェースを作成しましょう!
Mybatisは直接SQLが書けるので、かゆい操作にも手が届くのでおすすめです! 今回みたいな場合だとそんな恩恵は感じられないんですけどね.......
パスを叩いて呼び出して使えるようにしよう!
ここまでできたら、最後はRepositoryを呼び出すService Classを作って、そのServiceをControllerから呼び出したら終わりです!
なぜ、間にそんなクラスを挟むの? そういうお作法です!!! Controllerは何も知らずにただ、サービスの機能を呼び出すだけにしようという目的です!
SpringBootのありがたいところは、引数に@Serviceや@Repositoryと宣言したクラスはDIされるとこです! インスタンス生成もしなくて、シングルトンで使い回しができるってことなんだなーって思ってもらえたら良いです!
ってことで、さっきかいたDBアクセスのRepositoryの機能を呼び出すSampleService Classを作りましょう!
引数にSampleRepositoryがあるけど、これでもうDIされて使えるようになってます!ありがてえぇ。。。
最後にこのServiceをControllerから呼び出しましょう
これで、起動してswagger-uiからcreateしてfindしてupdateしてdeleteという操作ができるようになってます!
おわり
いかがだったでしょうか??? 最後が勢いよく飛ばし気味になったのと、sample codeが結構めちゃくちゃになったのは反省です 投稿することに注力しました!!!!!
今後変更できそうなら、編集しておきます...
こんな感じでAPIがささっと作れるので、必要になったら作ってみたらいかがでしょう!!!!!!!??????
きっかけになったらいいなと思いますし、今回詳細な説明はしてませんが、こうやったらできるんだろうということは伝えたつもりなので、疑問に残ったとこは自分で調べて勉強してみましょう!!!!!
皆さんよいアドカレ生活を