こんな記事。
Google Chrome上でマイクから録音した声をリアルタイムで文字に書き起こしてくれる「The Recording Studio」 - GIGAZINE
ん。
そんなに、「OSの日本語音声入力」は使えないのか?
ということでやってみたけど、まあ使える。
一体、なんなの。
こんな記事。
Google Chrome上でマイクから録音した声をリアルタイムで文字に書き起こしてくれる「The Recording Studio」 - GIGAZINE
ん。
そんなに、「OSの日本語音声入力」は使えないのか?
ということでやってみたけど、まあ使える。
一体、なんなの。
少し古いですが、ふと思い出した記事です。
ネットワークを使ったすべてのアプリに必須な考え方だと思います。
Loading data from multiple sources with RxJava
ネットワーク経由で問い合わせするデータがある場合、必要なときにそのまま取得することができますが、ディスクやメモリにキャッシュするほうが効率的です。
1. たまにネットワーク経由で新鮮なデータを取得する。
2. それ以外は、その結果をキャッシュしてできるだけ早く取得する。
RxJava を使ってこの実装をすると良いです。
Observable を使って、ネットワーク、ディスク、メモリーのそれぞれから取得します。
シンプルに2つのオペレーター concat() と first() を使います。
concat() は、複数の Observable を並び順に合成して、first() はその並び順から最初のものを実行します。よって、concat().first() とすると、複数のソースから最初のものが取得されます。
// Our sources (left as an exercise for the reader)
Observable<Data> memory = ...;
Observable<Data> disk = ...;
Observable<Data> network = ...;
// Retrieve the first source with data
Observable<Data> source = Observable
.concat(memory, disk, network)
.first();
このパターンの鍵となるのは、concat() が 必要なときにだけ、それぞれの子Observableを subscribe することです。
first() はシーケンスを早く止めるので、データがキャッシュされている場合に不要な遅いソースの問い合わせはありません。 言い換えれば、メモリから結果が返された場合、ディスクやネットワークにアクセスする必要はありません。 逆に、メモリとディスクがデータを持っていない場合は、新しくネットワークリクエストを行います。
当然、次のステップは、ソースが入ってきたときにそれを保存することです。ネットワーク要求の結果をディスクに保存したり、ディスクへのリクエストをメモリにキャッシュしなければ、何の意味もありません。上記のコードは、常にネットワークリスエストを行うだけです。
私の考えた対策は、それぞれのソースがそれを発行するときにデータを保存/キャッシュすることです。
Observable<Data> networkWithSave = network.doOnNext(data -> {
saveToDisk(data);
cacheInMemory(data);
});
Observable<Data> diskWithCache = disk.doOnNext(data -> {
cacheInMemory(data);
});
これで、networkWithSaveとdiskWithCacheを使用した場合、データはロード時に自動的に保存されます。
(この方法のもう1つの利点は、networkWithSave / diskWithCache はどこでも使用できることです。)
このままでは、古いままのデータが常に返されます。新鮮なデータの取得のために、たまにはサーバから取得しなければなりません。
解決策は、first() で。これはフィルタリングも実行できます。価値のないデータを除去するように設定します。
Observable<Data> source = Observable
.concat(memory, diskWithCache, networkWithSave)
.first(data -> data.isUpToDate());
これで、「最新」と見なされる最初のアイテムのみを発行します。したがって、そのデータソースが古い場合は、新しいデータが見つかるまで次のソースに進みます。
first() の代わりに takeFirst() を使うこともできます。
2つの違いは、いずれのソースも有効なデータを発行しない場合、first()は NoSuchElementException をスローするのに対し、takeFirst() は例外なしで完了することです。
どちらを使用するかは、データ不足を明示的に処理する必要があるかどうかによって異なります。
バージョン1.0アナウンスされています。
Announcing SQLDelight 1.0 – Alec Strong – Medium
神も。
In 2018 I was most thankful for SQL Delight.
In 2019 I'm most looking forward to SQL Delight.— Jake Wharton (@JakeWharton) January 4, 2019
データベース周りにRoom他のライブラリをご利用の方も試してみてはどうでしょうか。
square/sqldelight: Generates typesafe Kotlin APIs from SQL
SQLDelight は、以下のようなSQLステートメントからデータベース、テーブル、タイプセーフなAPIを作成できます。
HockeyPlayer.sq
CREATE TABLE hockeyPlayer (
player_number INTEGER NOT NULL,
full_name TEXT NOT NULL
);
CREATE INDEX hockeyPlayer_full_name ON hockeyPlayer(full_name);
INSERT INTO hockeyPlayer (player_number, full_name)
VALUES (15, 'Ryan Getzlaf');
selectAll:
SELECT *
FROM hockeyPlayer;
insert:
INSERT INTO hockeyPlayer(player_number, full_name)
VALUES (?, ?);
定義したAPIは以下のように利用できます。
val driver = AndroidSqliteDriver(Database.Schema, this, "test.db")
val database = Database(driver)
val playerQueries = database.hockeyPlayerQueries
// selectAll
println(playerQueries.selectAll().executeAsList())
// Prints [HockeyPlayer.Impl(15, "Ryan Getzlaf")]
// insert
playerQueries.insert(player_number = 10, full_name = "Corey Perry")
playerQueries.insert(player_number = 999, full_name = "フグ田 サザエ")
// selectAll
println(playerQueries.selectAll().executeAsList())
// Prints [HockeyPlayer.Impl(15, "Ryan Getzlaf"), HockeyPlayer.Impl(10, "Corey Perry")]
利用前の build で build/generated/ 以下に書き出されますが、gradle のバージョンが限定されるように見えました。何か設定が足りないのかもしれません。
gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.3-all.zip
実際サンプルを動かす風景。
利用方法など今回バージョン1.0 で変わっているようなので、
数回に渡って実装に利用できるとこまでやってみたいと思います。
SQDelight の データベースバージョン
Reddit: Announcing SQLDelight 1.0 – Alec Strong – Medium
SQLDelight 1.0 使い方 #2