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); }
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を消せばいいんだけどなんだかなぁという感じだった(加藤恵)。あとでちゃんとやってみたい。
AndroidのExpandableListViewとRecyclerView
結論からして相性が良くない。requestLayoutとか入れて実験してみたけど、できなかった。
やりたかったこと
RecyclerView>CardView>ExpandableListViewっていうのをやってコストを下げたかった。
やってみた
そのまま実装してみるとだめだったので、RecyclerView>ExpandableListViewをやってみたらこの時点でダメだった。
wrap_contentがうまくいかない。Android developers見てみたらwrap_contentはダメだぞって書いてあった。ExpandableListViewのGroupViewのonClickにRecyclerViewのrequestLayout()を入れてもダメだった(実装が悪い?)
妥協案
愚直にLinearLayoutにぶち込んだ。この分ならRelativeでも大丈夫な気がする。
AndroidのFragmentのreplace
最近またAndroidやってます。ノリで書いてたら詰まったのでちょっとしたメモです。
解決法
googleの画面を1:2に配分してそれぞれに動的なフラグメントを割り当てたいとき、googleのコードの
<?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してる。
その他のファイル
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のエントリーポイント
今回の目的
インタープリターのエントリーポイントが見つかるまで。
探す
エントリーポイントっていうのは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にあるよ。