Flipper で SQLite テーブルを直接見る

Stetho の Facebook が作成した Android / iOS どちらも使えるデバッグツールです。

👉 Open-sourcing Flipper: a new extensible debugging tool - Facebook Engineering 

👉 Flipper · Extensible mobile app debugging 

👉 facebook/flipper: A desktop debugging platform for mobile developers. 

We wanted to create a platform that gives us all the flexibility we need to build more advanced features and support for iOS. One of Flipper's core concepts is its extensibility using plugins. Plugins are written in React and we provide a set of ready-to-use UI components that allow developers to build great plugin UIs with a few lines of code.

iOSのより高度な機能とサポートを構築するために必要なすべての柔軟性を提供するプラットフォームを作成したかったのです。 Flipperのコアコンセプトの1つは、プラグインを使用した拡張性です。プラグインはReactで記述されており、開発者が数行のコードで優れたプラグインUIを構築できる、すぐに使用できるUIコンポーネントのセットを提供します。

👉 Stetho Guidance · Flipper 

dependencies、Application 内に記述しながら、セットアップ。


<activity android:name="com.facebook.flipper.android.diagnostics.FlipperDiagnosticActivity"
        android:exported="true"/>


repositories {
  jcenter()
}

dependencies {
  debugImplementation 'com.facebook.flipper:flipper:0.26.0'
  debugImplementation 'com.facebook.soloader:soloader:0.5.1'
  releaseImplementation 'com.facebook.flipper:flipper-noop:0.26.0'
}


public class MyApplication extends Application {

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, false);

    if (BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)) {
      final FlipperClient client = AndroidFlipperClient.getInstance(this);
      client.addPlugin(new InspectorFlipperPlugin(this, DescriptorMapping.withDefaults()));
      client.start();
    }
  }
}

👉 Getting Started · Flipper 

ここでは Database プラグインを追加します。


client.addPlugin(new DatabasesFlipperPlugin(context));

👉 Databases Plugin Setup · Flipper 

あとは、PC上のデバッグクライアントを開くと直感的に参照できるようになります。

👉 android/architecture-samples at dagger-android 

使いやすいです。動作も軽快です。


res を複数作って機能・画面別にディレクトリ分けすると分かりやすい

👉 Split layouts into subfolders in Android Studio - ProAndroidDev 

以下のコードを見ながら。

👉 GitHub - android/architecture-samples at dagger-android 

コードは、画面別にディレクトリ分けしてるが、レイアウトファイルは、複雑なファイル名で res ディレクトリ直下に一緒くたに入ってる。

ここで、build.gradle で設定記述を追加する。


android {
  sourceSets {
    main {
      res.srcDirs = [
          'src/main/res',
          'src/main/res/layouts/tasks',
          'src/main/res/layouts/taskdetail',
          'src/main/res/layouts/addedittask',
          'src/main/res/layouts/statistics',
      ]
    }

こうすることで、レイアウトファイル群を画面別に分けることができる。

Android Studio はデフォルトで、一つのディレクトリのみ


android {
  sourceSets {
    main {
      res.srcDirs = [
          'src/main/res'
      ]

と等価な設定を持ってるので、それに、画面別ディレクトリを複数追加することで、Android Studio は、リソースファイルとして複数ディレクトリからファイルを読み込むことができるようになります。

あとは、それぞれのリソースファイルを振り分けて置くだけ。

以下、やってみた動画。



実際は、レイアウトファイルがもっと多くなるはずなので、分かりやすさはより顕著になる。

👉 Split layouts into subfolders in Android Studio - ProAndroidDev 


SQLDelight で View を使うべし

👉 Drive your UI with SQLDelight’s views | Leandro Favarin 
👉 GitHub - cashapp/sqldelight: SQLDelight - Generates typesafe Kotlin APIs from SQL 

SQLDelight は、すべてのクエリーに対して自動的にモデルオブジェクトを作成します。

以下シンプルな名前付きクエリー。


bandsOrderedByName:
SELECT id, name
FROM band
ORDER BY name DESC;

bandsOrderedByAge:
SELECT id, name
FROM band
ORDER BY age;

これから以下が作成される。


data class BandsOrderedByName(id: String, name: String)

data class BandsOrderedByAge(id: String, name: String)

実際は、もっと複雑になります。

以下、join句を使ったクエリーの場合。


SELECT
  band.id,
  band.name,
  album.*
FROM band
JOIN album ON band.id = album.band_id;

SQL View を使うとエレガントになります。


👉 SQLite Query Language: CREATE VIEW 


CREATE VIEW bandWithAlbum AS
SELECT
  band.id,
  band.name,
  album.*
FROM band
JOIN album ON band.id = album.band_id;

bandsOrderedByName:
SELECT *
FROM bandWithAlbum
ORDER BY name DESC;

bandsOrderedByAge:
SELECT *
FROM bandWithAlbum
ORDER BY age;

SQLDelight は、BandWithAlbum タイプを生成します。

続いて、ページネーションの例。


count:
SELECT count(*)
FROM bandWithAlbum;

paged:
SELECT *
FROM bandWithAlbum
LIMIT ?
OFFSET ?;

SQLDelight が生成するモデルは、data クラスなので、DiffUtil コールバックはすぐに書けます。


object BandItemCallback : ItemCallback<BandWithAlbum>() {
  override fun areItemsTheSame(oldItem: BandWithAlbum, newItem: BandWithAlbum): Boolean {
    return oldItem.id == newItem.id
  }

  override fun areContentsTheSame(oldItem: BandWithAlbum, newItem: BandWithAlbum): Boolean {
    return oldItem == newItem
  }
}

また、enum クラスを使ったソートオプション。


enum class Sort { NAME, AGE }

fun bandsSorted(by: Sort): Flow<List<BandWithAlbum>> = when (by) {
  NAME -> db.bandsOrderedByName()
  AGE -> db.bandsOrderedByAge()
}.asFlow().mapToList()

逆に、これらのようなSQL処理をプログラムで実行すると効率は落ちます。
👉 The Resurgence of SQL (Droidcon NYC 2017) - Speaker Deck 

まとめ

欲しいタイプを View にすると、少ないコードで実現できます。

ユーザーの要求は、技術が発達するにつれてますます激しくなることは明らかです。良きユーザエクスペリエンスのための簡単な実装方法を常に把握しておくことが重要になります。