Yahoo! JAPANアプリのデータベース処理改善 #yjcamp

138 Views

April 20, 17

スライド概要

2017/04/19 CAMPFIRE Android #1
https://yj-meetup.connpass.com/event/53419/

profile-image

2023年10月からSpeaker Deckに移行しました。最新情報はこちらをご覧ください。 https://speakerdeck.com/lycorptech_jp

シェア

またはPlayer版

埋め込む »CMSなどでJSが使えない場合

(ダウンロード不可)

関連スライド

各ページのテキスト
1.

Yahoo! JAPAN アプリのデータベース処理改善 2017年4月19日 CAMPFIRE Android #01 ヤフー株式会社 森脇 聖太 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

2.

自己紹介 森脇 聖太 (Shota Moriwaki) 2016年10月中途入社 前職ではアプリの受託開発 Android開発歴は4,5年くらい yysk_08 yysk 2 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

3.

リリース済みのアプリのデータベース処理を どうやって安全にモダンな環境に移行したか という話をします Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

4.

今日話すこと 1. データベース処理の改修に至った経緯 2. OSS選定の経緯 3. 導入したOSSの紹介 4. まとめ 4 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

5.

Yahoo! JAPAN アプリ Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

6.

Yahoo! JAPAN アプリ 公式 アプリ 天気もニュースも これひとつで ・Android4.1〜 ・2週間毎にリリース 6 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

7.

いつからあるの? Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

8.

2011年10月リリース Android2.1以上に対応 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

9.

Yahoo! JAPAN アプリの移り変わり 職場の座席を自動割り当て NEW NEW ピックアップ 総合ランキング グルメなセットお取り寄せ 編集 サービス よく見る アプリ スポーツ 路線 旅行 知恵袋 地図 R25 ファイナンス ショッピング ネタ ニュース オークション 映画 メール カカオ 50% 12℃/1℃ 67点 射手座 番組表 小選挙区制は本当にダメか トピックス 毒グモか かまれた男性入院 国内 「3人乗り自転車」に潜む危険 エンタメ ジンバブエ 国庫の残高2万円 スポーツ 海外 Yahoo!検索 検索 多彩なジャンルのニュースを楽しむ方法 メール お気に入り 中小快適 スポーツ ヤフオク! ショッピング すべて すべて ニュース 話題 芸能 スポーツ お 那覇空港あわや衝突 勘違いか 中国軍艦 自民がビラ100万枚 ドローン規制 経産省が危機感 大阪府議会 返還は1億円超 復帰マー君勝利 圧巻7回1失点 藤ヶ谷&瀧本 事務所コメント 9 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

10.

かなり昔から メンテナンスされ続けている Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

11.

データベース周りの 技術的負債を解消 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

12.

改修前のデータベース処理 ・SQLiteOpenHelper ・ContentProvider ・CursorLoader 12 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

13.

改修後のデータベース処理 ・SQLBrite ・SQLDelight ・AutoValue ・RxJava 13 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

14.

OSSの選定 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

15.

候補に挙がったOSS ・Realm ・Orma ・SQLBrite ・requery 15 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

16.

SQLBrite Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

17.

SQLBrite ・https://github.com/square/sqlbrite ・Square社製 ・SQLiteOpenHelperのラッパー ・クエリの結果をObservableで受け取ることができる ・テーブルの変更通知を受け取ることができる ・SQLDelightと合わせて使うと簡潔に書ける 17 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

18.

SQLBriteの導入 compile 'com.squareup.sqlbrite:sqlbrite:1.1.1' 18 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

19.
[beta]
SQLBriteのサンプル
SampleDatabaseHelper dbHelper = new SampleDatabaseHelper(context);
SQLBrite sqlBrite = new SQLBrite.Builder().build();
BriteDatabase db = sqlBrite.wrapDatabaseHelper(dbHelper, Schedulers.io());
Observable<SqlBrite.Query> speakers =
db.createQuery("campfire_speaker", "SELECT * FROM campfire_speaker");
speakers.subscribe(new Action1<SqlBrite.Query>() {
@Override
public void call(SqlBrite.Query query) {
Cursor cursor = query.run();
// TODO parse data...
}
});
ContentValues values = new ContentValues();
values.put("speaker_number", 1);
values.put("name", "Shota Moriwaki");
db.insert("campfire_speaker", values);
19
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
20.

SQLBriteを選んだ理由 ・クエリの結果をObservableで受け取ることができる ・テーブルの変更通知を受け取ることができる ・既存のSQLiteOpenHelperをラップする形で運用で きる ・データベースのマイグレーションの必要がない ・他ORMに比べて導入コストが低い 20 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

21.

とはいえ Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

22.

SQLを文字列で 扱うのはつらい Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

23.

SQLDelight Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

24.

SQLDelight ・https://github.com/square/sqldelight/ ・Square社製 ・SQLからJavaのモデルクラスを作成してくれる ・gradle-plugin ・AutoValueと合わせて使うとさらに簡潔に書ける 24 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

25.

SQLDelightの導入 buildscript { repositories { mavenCentral() } dependencies { classpath 'com.squareup.sqldelight:gradle-plugin:0.6.1' } } apply plugin: 'com.squareup.sqldelight' 25 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

26.

.sqファイルを作成 CREATE TABLE campfire_speaker ( _id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, speaker_number INTEGER NOT NULL, name TEXT NOT NULL ); select_all: SELECT * FROM campfire_speaker; insert_row: INSERT INTO campfire_speaker(speaker_number, name) VALUES (?, ?); 26 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

27.

モデルクラスのinterfaceを自動生成 public interface CampfireSpeakerModel { String TABLE_NAME = "campfire_speaker"; String _ID = "_id"; String SPEAKER_NUMBER = "speaker_number"; String NAME = "name"; String CREATE_TABLE = "" + "CREATE TABLE campfire_speaker (\n" + " _id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,\n" + " speaker_number INTEGER NOT NULL,\n" + " name TEXT NOT NULL\n" + " )"; String SELECT_ALL = "" + "SELECT *\n" + "FROM campfire_speaker"; 27 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

28.
[beta]
モデルクラスのinterfaceを自動生成
long _id();
long speaker_number();
@NonNull
String name();
interface Creator<T extends CampfireSpeakerModel> {
T create(long _id, long speaker_number, @NonNull String name);
}
final class Mapper<T extends CampfireSpeakerModel> implements RowMapper<T> {
private final Factory<T> campfireSpeakerModelFactory;
public Mapper(Factory<T> campfireSpeakerModelFactory) {
this.campfireSpeakerModelFactory = campfireSpeakerModelFactory;
}
@Override
public T map(@NonNull Cursor cursor) {
return campfireSpeakerModelFactory.creator.create(
cursor.getLong(0),
cursor.getLong(1),
cursor.getString(2)
);
}
}
28
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
29.
[beta]
モデルクラスのinterfaceを自動生成
final class Factory<T extends CampfireSpeakerModel> {
public final Creator<T> creator;
public Factory(Creator<T> creator) {
this.creator = creator;
}
public Mapper<T> select_allMapper() {
return new Mapper<T>(this);
}
}
final class Insert_row extends SqlDelightCompiledStatement.Insert {
public Insert_row(SQLiteDatabase database) {
super("campfire_speaker", database.compileStatement("
+ "INSERT INTO campfire_speaker(speaker_number, name)\n"
+ "VALUES (?, ?)"));
}
public void bind(long speaker_number, @NonNull String name) {
program.bindLong(1, speaker_number);
program.bindString(2, name);
}
}
29
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
30.
[beta]
モデルクラスを定義
@AutoValue
public abstract class CampfireSpeaker implements CampfireSpeakerModel {
public static final Factory<CampfireSpeaker> FACTORY =
new Factory<>(new Creator<CampfireSpeaker>() {
@Override
public CampfireSpeaker create(
long _id, long speaker_number, @NonNull String name) {
return new AutoValue_CampfireSpeaker(
_id, speaker_number, name
);
}
});
public static final RowMapper<CampfireSpeaker> SELECT_ALL_MAPPER =
FACTORY.select_allMapper();
}
30
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
31.
[beta]
SQLBrite
Observable<SqlBrite.Query> speakers =
db.createQuery("campfire_speaker", "SELECT * FROM campfire_speaker");
speakers.subscribe(new Action1<SqlBrite.Query>() {
@Override
public void call(SqlBrite.Query query) {
Cursor cursor = query.run();
// TODO parse data...
}
});
ContentValues values = new ContentValues();
values.put("speaker_number", 1);
values.put("name", "Shota Moriwaki");
db.insert("campfire_speaker", values);
31
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
32.
[beta]
SQLBrite + SQLDelight
Observable<List<CampfireSpeaker>> speakers =
db.createQuery(CampfireSpeaker.TABLE_NAME, CampfireSpeaker.SELECT_ALL)
.mapToList(new Func1<Cursor, CampfireSpeaker>() {
@Override
public CampfireSpeaker call(Cursor cursor) {
return CampfireSpeaker.SELECT_ALL_MAPPER.map(cursor);
}
});
speakers.subscribe(new Action1<List<CampfireSpeaker>>() {
@Override
public void call(List<CampfireSpeaker> campfireSpeakers) {
// TODO action
}
});
CampfireSpeaker.Insert_row statement =
new CampfireSpeakerModel.Insert_row(db.getWritableDatabase());
statement.bind(1, "Shota Moriwaki");
db.executeInsert(statement.table, statement.program);
32
Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.
33.

SQLBrite + SQLDelightを導入してよかった部分 ・少ないコストでモダンな開発環境に対応 ・データベースのマイグレーションを不要とすることで、 既存ユーザーのデータ損失リスクを限りなくゼロに ・SQLとJavaの記述を分離することによる可読性の向 上 33 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

34.

SQLBrite + SQLDelightを導入して辛かった部分 ・RxJava2に未対応 (2016/4現在) ・情報が少ない ・チーム内への布教活動がかなり必要 34 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

35.

まとめ Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

36.

まとめ ・既存アプリのデータベース改修は大変 ・でも世の中にはORMがいっぱい ・SQLiteOpenHelperを使ってるならSQLBrite + SQLDelightという選択肢もありだと思う 36 Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.

37.

ご清聴ありがとうございました Copyright © 2017 Yahoo Japan Corporation. All Rights Reserved.