「アイデアは考えるな」を読んだ感想
書籍
アイデアは考えるな 面白法人カヤック代表 柳澤大輔 http://amzn.asia/eyJUrs7
- 会社の本棚にあったので何気なく読んだら、すごく面白くて一気に読み切ってしまった。
- PM、エンジニア、デザイナー問わず読める本。
アイデアは質より量
- すごいアイデアは誰にでも出せるわけではない、まずはすごくないアイデアをたくさん出そう。
- すごいアイデアを1つ出すのではなく、すごくないアイデアを10出そう
- すごくないアイデアを出していけばだんだんとポジティブになり、アイデア出しが面白くなってくる、そこが狙い
なんでも乗っかれ
- なんでもとにかく目の前に現れたものに悩ますに乗っかろう、自分の判断はあてにならない、自分フィルターを外そう。
- 乗っかったら面白がれるポイントを探そう(ポジティブに)、視点を変えよう、それが意外な発見につながり、斬新なアイデアに繋がる。
- ポジティブでなければアイデアはたくさん出せない。
- アイデアとは既存の要素の組み合わせ以外何ものでもない
- 乗っかるからこそヒントを得てヒットを生み出せる
- アイデアを出すと気には締め切りは絶対に必要、締め切りとは人間の力を目一杯引き出す装置。
- 情報を集めたほうが格段にアイデアを出しやすい、情報を集めるなら本を読む、ネットで集める、人に会って聞くなど自分にあった方法を見つけろ。
発想法
1人でアイデア出しの練習にはマンダラチャートが良い
ブレスト
- とにかくアイデアの量を出す
- とにかく相手を否定しない
- とにかく相手の意見に乗っかる
ブレストの人数は5〜7人くらいがちょうどいい、それ以上だと発言の量などによる効率が悪い
Android Studio 3.1.1でSyncエラー Data Binding annotation processor version needs to match the Android Gradle Plugin version. You can remove the kapt dependency com.android.databinding:compiler:3.1.0 and Android Gradle Plugin will inject the right version.
タイトルが長すぎるけど気にしない。
Android Studio 3.1.1にアプデしたらSyncエラーが出た。
Data Binding annotation processor version needs to match the Android Gradle Plugin version. You can remove the kapt dependency com.android.databinding:compiler:3.1.0 and Android Gradle Plugin will inject the right version.
日本語訳
データバインディングアノテーションプロセッサのバージョンは、Android Gradle Pluginのバージョンと一致する必要があります。 あなたはkapt依存関係com.android.databinding:compiler:3.1.0を削除することができ、Android Gradle Pluginは適切なバージョンを注入します。
修正方法
メッセージ内容を読むと分かる通り、
以下のapp/build.gradeにあるandroid.databinding:compilerのバージョンをAndroid Gradle PluginのGradleバージョンと合わせれば直る。
Android Gradle PluginのGradleバージョン
dependencies { classpath 'com.android.tools.build:gradle:3.1.1'
3.1.1だね
app/build.gradeにあるandroid.databinding:compiler
kapt 'com.android.databinding:compiler:3.1.0' <- 古い!!
3.1.0かー、ちょっと古いね
こいつを3.1.1にあげよう
kapt 'com.android.databinding:compiler:3.1.1'
これでSyncもビルドも通った。
でも今までkaptもandroid.databinding:compilerもなんとなく使ってきたので、こいつらは一体なんなのか軽く調べた。
kaptとは?
KotlinからJavaのコードを利用可能にするモノらしい。
android.databinding:compilerとは?
以下リンクを見ると、コンパイル時にLayout xmlからBindingのコードを吐き出すものらしい。
つまり?
kapt 'com.android.databinding:compiler とは
DataBindingが定義されたLayout XMLをコンパイル時に生成されるコードを、Kotlinで利用可能にするモノのようだ
Androidで処理時間を計測するならSystem#currentTimeMillisよりもSystemClock#uptimeMillis
タイトルまんまの内容。
メソッドの処理時間を計測したかった
Androidで処理時間を計測しようとSystem.currentTimeMillis()
を使ってたら、SystemClock.uptimeMillis()
のがいいよとアドバイスをもらった。
なぜか?
System.currentTimeMillis()
の場合、端末時間をユーザーが変更できたりOSの時刻補正によって現在時間がかわる恐れがあるため
SystemClock.uptimeMillis()
は端末ブートしてからの時間を記録してるので、時間に変更が入る心配がない
ただし、SystemClock.uptimeMillis()
はディープスリープに入ると止まるため、長い計測を行う場合には注意が必要だ。
参考リンク
Yukiの枝折: 経過時間を求める方法 http://yuki312.blogspot.jp/2011/11/blog-post_30.html
Android KotlinでRealmを使う
環境
- Android Studio 3.0.1
- kotlin_version = '1.2.21'
- Realm 5.0.0
資料
Realm公式 Realm: Create reactive mobile apps in a fraction of the time
Realmの導入
プロジェクトのbuild.gradleにrealmを追加
buildscript { ext.kotlin_version = '1.2.21' repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:3.0.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "io.realm:realm-gradle-plugin:5.0.0" //追加 } }
appのbuild.gradleにrealmを追加
ここはrealmを最後の行に書かないとエラーになるはず
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' apply plugin: 'realm-android' //追加
Realm初期設定
Realm.init
これはアプリ起動中に1回行うだけでいいので、カスタムアプリケーションクラスでやるのがオススメ
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); Realm.init(this); }
AndroidManifestにカスタムアプリケーションを登録するのを忘れずに
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ikemura.com.kotlinrealmmigration" > <application + android:name=".MyApplication" android:allowBackup="true"
Realm使用
インスタンス化
いよいよ画面でrealmを使えるようになった
lateinit var realm: Realm private fun initRealm() { val realmConfiguration = RealmConfiguration.Builder() .deleteRealmIfMigrationNeeded() .schemaVersion(0) .build() realm = Realm.getInstance(realmConfiguration) }
Insert
Transaction化しないとIllegalStateExceptionが発生する
realm.executeTransaction { realm.insert(Book(1,"first")) }
Read
titleが「first」と一致する値をbook
に取得する
var book = realm.where(Book::class.java) .equalTo("title","first") .findAll() Log.d(TAG,book.toString())
equalTo
:カラム名, 値 の比較を行える
findAll
:クエリ条件を満たすすべてのオブジェクトを同期検索する
Delete
削除もTransaction化しないとIllegalStateExceptionが発生する
//削除対象を取得 var book = realm.where(Book::class.java) .equalTo("title","first") .findAll() //削除 realm.executeTransaction { book.deleteAllFromRealm() }
nowで作成したAPIをKotlinとRetrofitとRxJavaとJacksonでGET/POSTする
いつもAndroidでRetrofit+RxJavaを使ってAPI通信する処理を書いてるが、今回はKotlinで挑戦してみる。
ついでに、使ったことのないnowもJacksonも触ってみた。
作成するもの
環境
- macOS Sierra
- kotlin_version = '1.2.21'
- Android Studio 3.0.1
- node v9.4.0
API
準備
APIモックをexpressを使ったnodejsで作成し、nowにデプロイしていく。
nowについてはこの記事を参考にした。
以下、作業ログ
// APIモックのプロジェクト準備 $ mkdir try-now && cd try-now // package.json作成 $ npm init -y // expressを追加 $ npm i -S express // apiの処理を書くjsを作成 $ touch index.js // nowのインストール $ npm i -G now // nowのログイン(メールアドレスを入力すると確認メールが届くのでverifyする) $ now login
実装
index.jsに以下を貼り付ける。
レスポンス変数は適当に作った。
var express = require('express') var app = express() app.get('/', function (req, res) { const json = { status: 1, card_status: 10, card_type: 'normal', card_id: "ca1111", } res.json(json) }) app.post('/', function (req, res) { const json = { status: 2, card_status: 20, result_code: 1 } res.json(json) }) app.listen(3000)
ポートは3000を指定してるけど、なくても良い。
これでAPIモックはできあがった。
$ node index.js
としてローカルでも使えるが、今回はnowにデプロイする
デプロイ
nowでデプロイ、超簡単
$ now
https://try-now-kplvmhijbn.now.sh
というアドレスが表示されたので、これをAPIモックとして利用できる。
このアドレスに対してgetやpostをしていく。
Androidアプリ
使用するライブラリ
app/build.gradleに以下のライブラリ達を追加する
// Retrofit2 implementation 'com.squareup.retrofit2:retrofit:2.3.0' implementation 'com.squareup.retrofit2:converter-jackson:2.3.0' implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0' // RxJava2 implementation "io.reactivex.rxjava2:rxjava:2.1.9" // RxAndroid implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' // Jackson implementation 'com.fasterxml.jackson.core:jackson-core:2.9.4' implementation 'com.fasterxml.jackson.core:jackson-databind:2.9.4' implementation 'com.fasterxml.jackson.core:jackson-annotations:2.9.4' implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.4"
ソース:https://github.com/banbara23/try-now
実装
Retrofitのインタフェースを作成。
戻り値はRxjavaのObservableにする。
interface ApiService { @POST("/") fun post(): Observable<PostResponse> @GET("/") fun get(): Observable<GetResponse> }
APIクライアントを作成していく。
今回、baseUrlにはnowで作成したurlを設定する。
class ApiClient { var apiService: ApiService init { val retrofit: Retrofit = Retrofit.Builder() .baseUrl("https://try-now-kplvmhijbn.now.sh") .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(JacksonConverterFactory.create(ObjectMapper())) .build() apiService = retrofit.create(ApiService::class.java) } /** * GETリクエスト */ fun get(): Observable<GetResponse> = apiService.get() /** * POSTリクエスト */ fun post(): Observable<PostResponse> = apiService.post() }
APIクライアント呼び出しは以下
ラムダ初めて触った、すごい
// get apiClient.post() .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe( { res -> Log.d(TAG, res.toString()) }, { error -> Log.e(TAG, "{$error.message}") }, { Log.d(TAG, "post completed") } ) // post apiClient.post() .observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io()) .subscribe( { res -> Log.d(TAG, res.toString()) }, { error -> Log.e(TAG, "{$error.message}") }, { Log.d(TAG, "post completed") } )
モデルについては、 Response(共通レスポンス)を継承した各モデルが存在するイメージで作成した。
/** * 共通レスポンス */ open class Response( @field:JsonProperty("status") val status: Int = 0, @field:JsonProperty("card_status") val cardStatus: Int = 0 )
/** * GETレスポンスのモデル */ data class GetResponse( @field:JsonProperty("card_type") val cardType: String = "", @field:JsonProperty("card_id") val cardId: String = "" ) : Response() { override fun toString(): String = "GetResponse(status='$status' cardStatus='$cardStatus' cardType='$cardType', cardId='$cardId')" }
/** * POSTレスポンスのモデル */ data class PostResponse( @field:JsonProperty("card_type") val cardType: String = "", @field:JsonProperty("card_id") val cardId: String = "", @field:JsonProperty("result_code") val resultCode: Int = 0 ) : Response() { override fun toString(): String = "PostResponse(status='$status' cardStatus='$cardStatus' resultCode='$resultCode')" }
この継承してるあたり、もうちょいうまくできないだろうか。
ビルドして実際に動作させると、Logcatに以下のログが表示されていればGET/POSTが成功している
getのログ D/AppCompatActivity: GetResponse(status='1' cardStatus='10' cardType='normal', cardId='ca1111') D/AppCompatActivity: get completed postのログ D/AppCompatActivity: PostResponse(status='2' cardStatus='20' resultCode='1') D/AppCompatActivity: post completed
ソース:https://github.com/banbara23/Kotlin-Jackson-Sample
ハマったところ
Jacksonが@JsonPropertyを認識してくれない
APIレスポンスをJacksonでシリアライズする際、共通レスポンスモデルであるResponse.ktがKotlin用のアノテーションでないとエラーになる。
Jacksonが継承元の変数を@JsonPropertyだと認識してくれなかった。
継承先は@JsonPropertyでも大丈夫なのに…
NG : @JsonProperty
OK : @field:JsonProperty
実際のコード
/** * 共通レスポンス */ open class Response( // NG //@JsonProperty("status") val status: Int = 0, //@JsonProperty("card_status") val cardStatus: Int = 0 // OK @field:JsonProperty("status") val status: Int = 0, @field:JsonProperty("card_status") val cardStatus: Int = 0 )
同様の問題が起きてないか調べたところ、以下のリンクに辿り着き、kotlinアノテーションを使うことで解決できた。 - https://github.com/FasterXML/jackson-module-kotlin/issues/56#issuecomment-321932309 - https://github.com/FasterXML/jackson-module-kotlin/issues/98#issuecomment-352016501
感想
nowが楽すぎてすごい
kotlinだとJavaよりコード量少なくて楽で良い
IllegalArgumentException: *** is not part of the schema for this Realm
環境
- Android Studio 2.3.3
- Java8
- Kotlin 1.1.51
- realm 3.5.0
現象
既存のJavaのAndroid StudioプロジェクトにKotlinを導入してビルドしたところ、起動時以下のエラーが必ず発生してお手上げとなった。
java.lang.RuntimeException: Unable to resume activity {my.package.MainActivity}: java.lang.IllegalArgumentException: MyMessageData is not part of the schema for this Realm at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3429) at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:3469) at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2732) at android.app.ActivityThread.-wrap12(ActivityThread.java) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1477) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6119) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776) Caused by: java.lang.IllegalArgumentException: MessageData is not part of the schema for this Realm at io.realm.internal.modules.CompositeMediator.getMediator(CompositeMediator.java:169) at io.realm.internal.modules.CompositeMediator.getTableName(CompositeMediator.java:87) at io.realm.RealmSchema.getTable(RealmSchema.java:218) at io.realm.RealmSchema.getSchemaForClass(RealmSchema.java:239) at io.realm.RealmQuery.<init>(RealmQuery.java:122) at io.realm.RealmQuery.createQuery(RealmQuery.java:76) at io.realm.Realm.where(Realm.java:1342)
MyMessageData is not part of the schema for this Realm
日本語訳:MyMessageDataは、このレルムのスキーマの一部ではありません
おかしいな、Kotlin追加する前までビルド通ってたんだけどなー
解決方法
なんと、app/build.gradleの宣言順を入れ替えるだけで解決した。
Before
apply plugin: 'com.android.application' apply plugin: 'realm-android' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' android { ...
After
apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'realm-android' // <= こいつを一番最後にする android { ...
apply plugin: 'realm-android'
よ、お前は一番最後だったのか…
これだけでビルドが通った。
エラーでググるとStackOverFlowにも回答がすぐに見つかったため、割と同様にハマっている人が多いのかもしれない。
参考リンク
- Kotlin+Realmで*** is not part of the schema for this Realm問題にハマった - Qiita https://qiita.com/u_nation/items/d401a2b589d3c101ce19
- java - Object is not part of the schema for this Realm - Stack Overflow https://stackoverflow.com/questions/37887303/object-is-not-part-of-the-schema-for-this-realm
- realm-java/build.gradle at master · realm/realm-java https://github.com/realm/realm-java/blob/master/examples/kotlinExample/build.gradle
KotlinでDatabaseException: mypackage.Item does not define a no-argument constructor.
状況
Android Studio: 3.0.1
Kotlin: 1.2.10
firebase-database: 11.8.0
現象
Kotlinの勉強がてらFirebaseのRealTime Databaseでデータを取得しようとしたらこんなエラーが出た。
com.google.firebase.database.DatabaseException: Class my.com.package.Item does not define a no-argument constructor. If you are using ProGuard, make sure these constructors are not stripped. at com.google.android.gms.internal.zzelx.zze(Unknown Source) at com.google.android.gms.internal.zzelw.zzb(Unknown Source) at com.google.android.gms.internal.zzelw.zza(Unknown Source) at com.google.android.gms.internal.zzelw.zza(Unknown Source) at com.google.android.gms.internal.zzelw.zza(Unknown Source) at com.google.firebase.database.DataSnapshot.getValue(Unknown Source) at my.package.MyListFragment$fetchData$1.onDataChange(MyListFragment.kt:83) at com.google.android.gms.internal.zzegf.zza(Unknown Source) at com.google.android.gms.internal.zzeia.zzbyc(Unknown Source) at com.google.android.gms.internal.zzeig.run(Unknown Source) at android.os.Handler.handleCallback(Handler.java:751) at android.os.Handler.dispatchMessage(Handler.java:95) at android.os.Looper.loop(Looper.java:154) at android.app.ActivityThread.main(ActivityThread.java:6119) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
※パッケージやクラス名は変更している
firebaseからの取得コードはこれ
/** * 一覧に表示するデータを取得 */ private fun fetchData() { // Write a message to the database val database = FirebaseDatabase.getInstance() val myRef = database.getReference(mPath) // Read from the database myRef.addValueEventListener(object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { // This method is called once with the initial value and again // whenever data at this location is updated. val items: List<Item>? = dataSnapshot.getValue(object : GenericTypeIndicator<ArrayList<Item>>() {}) Log.d(TAG, "Value is: " + items) recyclerView!!.adapter = MyRecyclerViewAdapter(items!!, mListener) } override fun onCancelled(error: DatabaseError) { // Failed to read value Log.w(TAG, "Failed to read value.", error.toException()) } }) }
エラーメッセージのみ抜き出すと
Class my.com.package.Item does not define a no-argument constructor. If you are using ProGuard, make sure these constructors are not stripped.
日本語に訳すと
クラスmy.com.package.Itemは、引数のないコンストラクタを定義しません。 ProGuardを使用している場合は、これらのコンストラクタが削除されていないことを確認してください。
解決方法
引数のないコンストラクタ…
もしかしてモデルとして作ったItemクラスがおかしいのか?
と思い見てみると、ああ、Boolean型のisVideoに初期値を定義していなかった。
// 修正前 data class Item( val caption: String = "", val code: String = "", val data: String = "", val isVideo: Boolean, // ←こいつの初期値がないのが原因 val thumbnail: String = "" )
とりあえずfalseでも突っ込んでおく
// 修正前 data class Item( val caption: String = "", val code: String = "", val data: String = "", val isVideo: Boolean = false, //初期値falseを追加 val thumbnail: String = "" )
ビルドして動作させると...動いた!
モデルのコンストラクタに初期値を忘れるとこうなるんだな、勉強になった。