Room使ってみた

Room Persistence Library 使ってみた。

Room Persistence Libraryとはなんぞ

The Room persistence library provides an abstraction layer over SQLite to allow fluent database access while harnessing the full power of SQLite.

https://developer.android.com/topic/libraries/architecture/room

とある。Androidのデータベースアクセスを抽象化したライブラリーっぽい。個人的にはかなり抽象化されているので作らなきゃいけないものも少し多いなっていうイメージだった。

データベースのテストを書く

今回はEntityとして、名前とid(自動インクリメント)を持つEntityをかいてみた。テストの時はinMemoryのデータベースを書いた方がいいらしい。

import android.arch.persistence.room.Room;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import static org.hamcrest.core.IsEqual.equalTo;
import static org.junit.Assert.assertThat;

@RunWith(AndroidJUnit4.class)
public class SimpleEntityTest {
    private EntryDao mEntryDao;
    private AppDatabase mDb;

    @Before
    public void createDb() {
        Context context = InstrumentationRegistry.getTargetContext();
        mDb = Room.inMemoryDatabaseBuilder(context,AppDatabase.class).build();
        mEntryDao = mDb.entryDao();
    }

    @After
    public void closeDb(){
        mDb.close();
    }

    @Test
    public void writeAndReadOneEntry(){
        Entry entry = new Entry();
        entry.setName("Test1");
        mEntryDao.insertAll(entry);
        Entry gotEntry = mEntryDao.getEntryByName("Test1");
        assertThat(gotEntry,equalTo(entry));

    }
}

Entity

EntryっていうEntityを書いた。

import android.arch.persistence.room.ColumnInfo;
import android.arch.persistence.room.Entity;
import android.arch.persistence.room.PrimaryKey;

import java.util.Objects;


@Entity(tableName = "entries")
public class Entry {

    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "entry_id")
    private long eid;

    @ColumnInfo(name = "name")
    private String name;


    public void setName(String name) {
        this.name = name;
    }

    public long getEid() {
        return eid;
    }

    public void setEid(long eid) {
        this.eid = eid;
    }

    public String getName() {
        return name;
    }
    
    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Entry entry = (Entry) o;
        return Objects.equals(name, entry.name);
    }

    @Override
    public int hashCode() {

        return Objects.hash(eid, name);
    }
}

equalsをOverrideしないとTestのequalToで等しくならないので、nameだけ同じなら同じってことにした。primary keyのautoGenerate=trueでオートインクリメントできるらしい。

Dao

DaoはEntityのインターフェースとなる部分。ここに動作を書く。

import android.arch.persistence.room.Dao;
import android.arch.persistence.room.Delete;
import android.arch.persistence.room.Insert;
import android.arch.persistence.room.Query;

import java.util.List;

@Dao
public interface EntryDao {
    @Query("SELECT * FROM entries")
    List<Entry> getAll();

    @Query("SELECT * FROM entries WHERE name LIKE :name LIMIT 1")
    Entry getEntryByName(String name);

    @Insert
    void insertAll(Entry... entries);

    @Delete
    void delete(Entry entry);
}

DaoアノテーションでDaoであることを示す。Queryアノテーションで実行するQueryを指定する。

Database

import android.arch.persistence.room.Database;
import android.arch.persistence.room.RoomDatabase;

@Database(entities = {Entry.class},version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract EntryDao entryDao();
}

データベース。RoomDatabaseを継承したものをつくる。

テストの実行

これでテストはパスした。
エンティティにDateの属性を付けようとしていろいろ調べたけど、jack(java android compiler kit)の警告が出たりして、時間がたりなかったのでやめた。utilのDate使うよりOffsetDateTime(jave8)を使うのがいいらしい。Dateはタイムゾーンを考慮しないので。でも、OffsetDateTimeを入れるとjackとソースコンパティビリティの警告出るんだよなぁ。build.gradleでjack enabled=trueを入れてcompileOptionを消せばいいんだけどなんだかなぁという感じだった(加藤恵)。あとでちゃんとやってみたい。

その他

python書きてぇなつってjavaandroid書いてる。kivyとpySerialでpic18f14k50でシリアル通信するデスクトップアプリ書こうかなって隙間時間はkivy調べてる。さっさとandroid書き終わりたいなぁ。ぜんぜんhackしてないけど許してにゃん。

AndroidのExpandableListViewとRecyclerView

結論からして相性が良くない。requestLayoutとか入れて実験してみたけど、できなかった。

やりたかったこと

RecyclerView>CardView>ExpandableListViewっていうのをやってコストを下げたかった。

やってみた

そのまま実装してみるとだめだったので、RecyclerView>ExpandableListViewをやってみたらこの時点でダメだった。f:id:b1u3:20180616211355p:plain
wrap_contentがうまくいかない。Android developers見てみたらwrap_contentはダメだぞって書いてあった。ExpandableListViewのGroupViewのonClickにRecyclerViewのrequestLayout()を入れてもダメだった(実装が悪い?)

妥協案

愚直にLinearLayoutにぶち込んだ。この分ならRelativeでも大丈夫な気がする。
f:id:b1u3:20180616211730p:plain

AndroidのFragmentのreplace

最近またAndroidやってます。ノリで書いてたら詰まったのでちょっとしたメモです。

動的なFragment

Google developersに載ってるようなタグを使ってxmlを使うと動的にFragmentを差し替えることができない。

解決法

googleの画面を1:2に配分してそれぞれに動的なフラグメントを割り当てたいとき、googleのコードのを適当なViewGroupに変更する。つまり、

<?xml version="1.0" encoding="utf-8"?>
<!-- activity_main.xml -->
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:id="@+id/fragment_container"
    >
    <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:id="@+id/fragment_list_container"
            />
    <RelativeLayout
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="2"
            android:id="@+id/fragment_content_container"
            />
</LinearLayout>
/*
MainActivity.java
*/
package com.b1u3dev.fragmentandlayout;

import android.os.Bundle;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;


public class MainActivity extends AppCompatActivity {
    private FragmentManager mFragmentManager;
    private String className=MainActivity.class.getSimpleName();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mFragmentManager=getSupportFragmentManager();
        mFragmentManager.beginTransaction().add(R.id.fragment_content_container,new ContentFragment()).commit();
        mFragmentManager.beginTransaction().add(R.id.fragment_list_container,new ListFragment()).commit();

    }

 
    public void onClick(View view){
        Log.d(className,"Fragment Size:"+mFragmentManager.getFragments().size());
        switch (view.getId()){
            case R.id.content_button:
                Log.d(className,"content_button was clicked");
                mFragmentManager.beginTransaction().replace(R.id.fragment_content_container,new SecondContentFragment()).commit();
                break;
            case R.id.list_button:
                Log.d(className,"list_button was clicked");
                break;
        }

    }
}

こんな感じで。
xxxFragmentは全部FragmentのサブクラスでonCreateViewをオーバーライドしてinflateしてる。
f:id:b1u3:20180615030417p:plain

その他のファイル

package com.b1u3dev.fragmentandlayout;
/*
ContentFragment.java
 */

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class ContentFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {

        return inflater.inflate(R.layout.fragment_content,container,false);
    }
}
package com.b1u3dev.fragmentandlayout;
/*
ListFragment.java
 */

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class ListFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_list,container,false);
    }
}
package com.b1u3dev.fragmentandlayout;
/*
SecondContentFragment.java
 */

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class SecondContentFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_content_second,container,false);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<!-- fragment_content.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Test Content"/>
    <Button
            android:id="@+id/content_button"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="Content Fragment Button"
            android:onClick="onClick"/>

</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- fragment_content_second.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">
    <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="SECOND FRAGMENT CONTENT BUTTON"/>
    <Button
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="SECOND FRAGMENT CONTENT BUTTON"/>
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<!-- fragment_list.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
                  android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent">
        <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="List Activity"/>
        <Button
                android:id="@+id/list_button"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="List Activity Button"
                android:onClick="onClick"/>

</LinearLayout>

あ、ボタンの名前修正するの忘れてた。

java 10.0.1 と Intelli J Idea

Intelli J Idea と java 10.0.1 と Android用の 8 の共存やってみた。萌黄えもなので。
普通に入れて設定したら

could not determine java version using executable ...

というのがsync failedと一緒に出てきた。
OS X の方はbrewでgradleをupgradeして、Intelli J のpreference>Build Tools>gradleからUse local gradle distributionにチェック。からのGradle homeに適切なlibexecを登録する。
Windowsの方は設定はsettingsから。ちなみに OS Xの方はdefault settingsから設定できない。

pythonのエントリーポイント

経緯

割と唐突にpythonソースコードを読んでみたくなったので、ちょっと覗いてみた。

今回の目的

インタープリターのエントリーポイントが見つかるまで。

探す

エントリーポイントっていうのはmain関数(UNIX/Linux)とwmain関数(windows)のことをここでは指す。_startではないです。pythonのcpythonリポジトリを最初に見ると、いろいろディレクトリがある。ひとつひとつ見たり、単語検索かけるのはあまり勉強味がないので、ある程度の推測とかを駆使して見つけていった。はじめにそのリポジトリを見るとインストール方法とかが載ってるREADME.rstが下部に表示されている。
UNIX系だと

./configure
make
make test
sudo make install

のような一般的なインストール方法になっている。

./configure

いうのは同ディレクトリ内のMakefile.pre.inというファイルをもとにした環境に適したmakefileを出力するスクリプト。これはautoconfというAutotoolsという一連の開発ツールの一つによって出力されたファイル。2万行ちかくあった。Makefile.pre.inを見れば、主な流れがわかる。

Makefile.pre.inを見てみる。最初の方には変数とか定数の定義が書かれている。これはmakefileの構文に従う。後半にmekefile特有のルールとかターゲットとかが書かれている。基本的にはallターゲットが実行されるので、all:ではじまる部分を見る。この部分は見てみると@で囲まれた単語があるので、ここは./configureで置換される部分。よって、ここだけではわからんみたいな。
読み進めていくと、コメントで

#Build the interpreter

の行があるので、インタープリターをビルドしてるんだなってわかる。んで、直後に$(BUILDPYTHON)っていうのがあるので、これがビルドされるプログラムの名前なのかなみたいな。だからもう少しみる。Programs/python.oというのが必要らしい。なんか重要っぽいなって思ったのでPrograms/python.cを見てみるとmainがあった。

結論

Programs/python.cにあるよ。

Arch Linux ARMを入れた

ブログを続けようと思ってたら、半年以上空けてしまいました。9月半ばぐらいに2つほど書いたんですけど完成しませんでした。下書きには残ってるんですけど、長くなりそうで続きを書きたくないのが本音です。今回は日記です。教えることはないです。

本題

gitサーバーが欲しいなって思って2年前に買ったラズパイmodel Bを取り出して作ってみることにしました。なんのOS入れようかなと思ってたところ、丁度Arch Linuxにはまっているので、Arch Linux ARMをいれることにしました。

続きを読む