Android Stethoを使ってアプリのDBの中身を見る(Kotlin)

Stethoという素晴らしいライブラリがあります。

http://facebook.github.io/stetho/

Facebookさんが作ってくれていて、Androidの開発の手助けをしてくれます。

今回は、DBの中身を見る、ということだけが目的で使ってみます。

シンプルに簡単にやるのが目的なので、デバッグビルドの場合だけStethoを動かすとかではありません

①導入する

アプリのbuild.gradleの dependenciesブロック内 に下記のように書きます。

dependencies{
implementation 'com.facebook.stetho:stetho:1.5.1'
}

②Stethoでアプリの中身を見られるようにする

アクティビティ内で下記のようにします。

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        Stetho.initialize(
            Stetho.newInitializerBuilder(applicationContext)
                .enableDumpapp(Stetho.defaultDumperPluginsProvider(applicationContext))
                .enableWebKitInspector(Stetho.defaultInspectorModulesProvider(applicationContext))
                .build()
        )
   //中略
}

最初、

        Stetho.initializeWithDefaults(this)

だけでいいのかな?と思ってましたが、dumpappってのやらないとDBの中身見れません。

余談ですが、 dumpappってやると、鳥のウソの画像出てくるんですが、なんででしょうね?

④Stethoを使う

Chromeを起動して、下記のようにURLに打ち込みます。

chrome://inspect/#devices

USBで接続しているデバイスが見られます。

んで、アプリ名の下にinspectというリンクが出ますので、それをクリックします。

DevtoolというのがChromeの別ウィンドウで立ち上がります。

Resourcesというタブを開き、左のWeb SQLというところをクリックします。

おおー。中身が見れる―。

ChromeでStethoでデータベースの中身を見れます。
StethoでAndroidアプリのデータベースの中身を見れます。

感激ですね!!o(>▽<)o

ちな、なぜWeb SQLなんでしょう?誰か知ってたら教えてください。┌o ペコッ

Fatal error: Uncaught –> Smarty: undefined extension class ‘Smarty_Internal_Method_Register_Function’

明けましておめでとうございます。今年もよろしくお願いいたします。

さて、新年早々、上記のエラーで環境構築できなくて、2時間ほどはまった(´;ω;`)

どっちかというと社内向けメモですがご容赦ください。

多分、こんなことではまる人も少ないとは思うんだけど、書いておきます。

環境は、WindowsにXAMPP for Windows 7.2.26 です。

PHPはPHP Version 7.2.26。

PHPのテンプレートエンジン、Smartyを呼び出すところは下記の通り。


<?php
/**
 * Class Template
 * テンプレートエンジン管理
 * @since 2018/12/06
 */
class Template {
	
    private static $smarty = null;
    
    /**
	 *  Smarty取得
	 *  @param string $template_dir templateディレクトリ
	 *  @param string $compile_dir compileディレクトリ
     *
     *  @return object Smarty  
	 */
    public static function getSmarty(
        $template_dir = TEMPLATE_DIR,
        $compile_dir = TEMPLATE_COMPILE
    ) {
        if(self::$smarty !== null) {
            return self::$smarty;
        }

        self::$smarty = new Smarty();
        self::$smarty->template_dir = $template_dir;
        self::$smarty->compile_dir = $compile_dir;

        self::$smarty->register_function('autoversion', 'Template::autoVersion');
        
        return self::$smarty;
    }

    /**
	 *  ファイル名にバージョン管理用タイムスタンプ追加
	 *  @param array $params 
     *      $params['file'] アプリのルートディレクトリからのファイル相対パス
	 *  @param object Smarty
     * 
     *  @return string クエリーストリングにタイムスタンプを追加したファイル名  
	 */
    public static function autoVersion($params, &$smarty)
    {
        $file = $params['file'];
        $path = SERVER_PATH . $file;
        if(!file_exists($path)) {
            return $file;
        }

        $mtime = filemtime($path);
        return $file . '?' . $mtime;
    }

    
}

Smartyを取得するところ。

$smarty = Template::getSmarty();

そしたら

Fatal error: Uncaught –> Smarty: undefined extension class ‘Smarty_Internal_Method_Register_Function’

で止まってしまうんですよねー。

ググっても、下記のページしか出てこず。

https://www.smarty.net/forums/viewtopic.php?p=94694&sid=369b32fbbe502599af4fa43ef106a2c2

Smartyは3.1.34(これを執筆当時の最新版)で最新版なのになー。

Smartyの設定が悪いのかな?

と思って試行錯誤すること数時間。

詰まって上記のSmarty呼び出し部分を書いた人に聞いたら、これ、Smarty2系でしか動作しない書き方らしい…。

なので、Smarty2.6.31をダウンロードし、インストールしたら動作するようになりました。

Smartyのダウンロードサイトはこちら。

https://www.smarty.net/download

人に聞くって大事だね(´ω`)

AppDatabase_Impl does not exist というエラー

Kotlinで書いているAndroidアプリでRoomというのを使ってみてます。

次のサイトを見てやっていたのですが

Android公式サイト Room を使用してローカル データベースにデータを保存する
https://developer.android.com/training/data-storage/room?hl=ja

次の箇所で、表題のエラーが出て、アプリが落ちることに。

    val db = Room.databaseBuilder(
                applicationContext,
                AppDatabase::class.java, "database-name"
            ).build()

下記のStack Overflowに答えがありました。

https://stackoverflow.com/questions/46665621/android-room-persistent-appdatabase-impl-does-not-exist/53187335

ありがとう、Stack Overflow。君なしではもはや生きられない…。

めっちゃ雑なんですけど、Gradleのサンプルを載せておきます。

大事なことは、Gradle->syncの後、いったんプロジェクトをクリアすることです!!

アプリのGradleです。

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

apply plugin: 'kotlin-kapt'

android {
    compileSdkVersion 28
    defaultConfig {
        applicationId "jp.onlineconsultant.taskadmin"
        minSdkVersion 14
        targetSdkVersion 28
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    sourceSets {
        main.java.srcDirs += 'src/main/kotlin'

        androidTest.java.srcDirs += 'src/androidTest/kotlin'
    }
}

androidExtensions {
    experimental = true
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:design:28.0.0'
    implementation 'com.android.support:support-annotations:28.0.0'
    implementation 'android.arch.lifecycle:extensions:1.1.1'
    kapt "android.arch.lifecycle:compiler:1.1.1"
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
    implementation 'android.arch.persistence.room:runtime:1.1.1'
    annotationProcessor 'android.arch.persistence.room:compiler:1.1.1'
    kapt "android.arch.persistence.room:compiler:1.1.1"

    implementation 'com.facebook.stetho:stetho:1.5.1'
}
小鳥

Android Kotlin LiveData 超簡単なサンプルコード

超簡単にLiveDataを説明すると、

「データが変わるとビューが変わる」

ってことが簡単に実装できる方法です。

Android 本家DeveloperサイトのLiveData公式説明は下記をどうぞ。
https://developer.android.com/topic/libraries/architecture/livedata?hl=ja#work_livedata

リスナーやオブザーバーを自分で作ったりしなくてもいいので簡単ですし、メモリーリークが発生しない、アクティビティのライフサイクルのことを気にしなくてよいので使い勝手がよいです。

ViewModelというのを使わないといけないので(多分、使わなくてもいいんだろうけど使うことを推奨)ViewModelについても知っていた方がいいですが、これも超簡単に説明すると、

「ビューのデータ部分」

ということです。

gradleなどへの記述は、上記公式サイトを見てください。

ではいっきまーす。

//LiveDataActivity.kt内の記述
import android.arch.lifecycle.Observer //Android.utilのやつではないので注意
import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.view.View
import android.widget.Button
import android.widget.TextView
import jp.onlineconsultant.taskadmin.ui.main.TaskViewModel


class LiveDataActivity : AppCompatActivity() {

    private lateinit var model: TaskViewModel


    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)

        val hello: TextView = findViewById(R.id.hello)

        val button = findViewById<View>(R.id.button) as Button
        button.setOnClickListener {

            model.currentTask.setValue("ほげぇぇ")

        }


        // Get the ViewModel.
        model = ViewModelProviders.of(this).get(
            TaskViewModel::
            class.java
        )

        // Create the observer which updates the UI.
        val nameObserver = Observer<String> { newName ->
            // Update the UI, in this case, a TextView.
            hello.text = newName
        }

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.currentTask.observe(this, nameObserver)

        model.currentTask.value = "ほげほげ"
    }

}
<!-- activity_live_data.xmlの記述-->
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".LiveDataActivity">

    <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

        <TextView
                android:id="@+id/hello"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Hello World!" />

        <Button
                android:id="@+id/button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerHorizontal="true"
                android:layout_centerVertical="true"
                android:text="@string/task_input_voice" />
    </LinearLayout>

</android.support.design.widget.CoordinatorLayout>
//TaskViewmodel.kt内に記載
class TaskViewModel : ViewModel() {

        // Create a LiveData with a String
        val currentTask: MutableLiveData<String> by lazy {
            MutableLiveData<String>()
        }

    }

ボタンをクリックすると、

「ほげほげ」


「ほげぇぇ」

に変わります。

Kotlin ジェネリクスの型投影 変位指定 共変でout Anyとは をサンプルコードと共にめちゃ簡単に説明してみる

Kotlin勉強中の私です。Javaから移行組です.

今回、次のようなソースコードを見ていて、疑問に思ったことがいくつかありました。出典は、Android StudioでLoginの機能を自動的に作ってみたところ、Loginの結果を表示するクラスとして、下記のResultクラスが存在しています。

sealed class Result<out T : Any> {

    data class Success<out T : Any>(val data: T) : Result<T>() 
    data class Error(val exception: Exception) : Result<Nothing>()

    override fun toString(): String {
        return when (this) {
            is Success<*> -> "Success[data=$data]" 
            is Error -> "Error[exception=$exception]"
        }
    }
}

特に気になったのは

data class Success<out T : Any>(val data: T) : Result<T>() 

の部分ですね。

ジェネリクスは、Javaでも使っていたので知ってはいましたが、 out ?? ってなりました。

そこで、初めて「変位指定」とか「型投影」とか「不変」「共変」「反変」という言葉たちに出会いました。

もう、いきなりつまずきそうですよね?!字からして、難しそうで、逃げたくなりますよね??

「共変とはなんぞや?変位指定とはなんぞや!?」

と独眼鉄になっちゃいますね(´ω`)

しかも、説明を読んでも、さっぱりわかりませんでした。
特に、outがスーパータイプを指定することはわかりましたが、Anyって指定すると、なんでもいいわけじゃん?なんで必要なの?となりました。

Kotlin公式サイト ジェネリクス
https://dogwood008.github.io/kotlin-web-site-ja/docs/reference/generics.html

自分でコード書いてみたら、腑に落ちました。(もっと早くやれよ)

class Container<T>(var value:T)

上記のように、Containerクラスを作ります。とってもシンプルなクラスですね。ただ単に、入れ物です。value というプロパティだけがあります。

下記のようなshowというメソッドがあって、引数にContainerを指定します。(Android Studioで実行しています。)

    
fun show(container:Container<out Any>){
        Log.d(TAG, container.toString())
}

show()を呼び出します。

val inta:Container<Int> = Container(23)
show(inta)

val stringa:Container<String> = Container("うきき")
show(stringa)

今、ここでIntもStringもshowの引数として入れられましたね?

では、showメソッドの引数を、次のように引数の型指定から「out」をなくしてみます。

 fun show(container:Container<Any>){ //この行からoutがなくなっています。
Log.d(TAG, container.toString())
}
val inta:Container<Int> = Container(23)
show(inta)

val stringa:Container<String> = Container("うきき")
show(stringa)

すると、呼び出し元のshow(inta)、show(stringa)でコンパイルエラーが起こります。

Type mismach.Required Containre<Any>

って出ます。

ああ。つまり、outを指定するのは、Anyのサブタイプならなんでもいいよってためにつけるわけですね。٩( ‘ω’ )و

「ウォーーーーーターーーーー!!!」

とヘレンケラーが叫んだ瞬間もこんな感じだったでしょう。(大げさ)

これが、共変です。

反変はその反対です。(ざっくり)

不変は、outを消したときと同様です。

それにしても共変って日本語わかりにくくないですか??!「共に」「変わる」ってなんだ?オラさっぱりわからなかったぞ!!

初心者なので、理解が間違っているところがあるかもです。(´;ω;`)
ぜひぜひご指摘くださーい。