Android Studioで全文検索
Android Studioで全文検索したいときは
Edit→Find→Find in path
を選択して出てきた子画面のText to findにキーワードを入れてFindボタン押下で実行できます。
Optionsの意味は↓みたいな感じ。
名前 | 意味 |
---|---|
Case sensitive | 大文字と小文字を区別して検索。 |
Whole words only (may be faster) | 単語として完全に一致したもののみ検索する。(速くなるかも) |
Regular expression | 正規表現で検索。 |
Comments only | コメントを対象に検索する。 |
String literals only | 文字リテラルを対象に検索する。 |
Android Studioのバージョンを0.6.0に上げたときに出るエラーの対処法
Android Studioのバージョンを0.6.0に上げた際、既存のプロジェクトで以下のようなエラーが出ました。
Error:The SDK Build Tools revision (19.0.3) is too low for project ':app'. Minimum required is 19.1.0
Android SDKのリビジョンが古いということなので、app/build.gradle
を書き換えます。
buildToolsVersion "19.0.3"
↓
buildToolsVersion "19.1.0"
上記設定に変更してクリーンしてもすんなり解決というわけにはいかなくて、以下のようなエラーが発生します。
Error:Cause: failed to find Build Tools revision 19.1.0 Please install the missing Build Tools from the Android SDK Manager.
インストールされているAndroid SDKの中に19.1.0のBuild Toolsがないとのこと。
SDK Managerを立ち上げて対象リビジョンのBuild Toolsをインストールします。
再度クリーンすることでエラーは解消されます。
新人教育に際して気をつけたいこと
先日、devlove名古屋のオブジェクト設計とリーン開発、その実践に参加した際、最後の質疑応答で増田さん(@masuda220 ](https://twitter.com/masuda220))が仰っておられたことが心に残ったので文章に残しておきます。
最後の質疑応答の時間に増田さんに対して、設計の価値を他の人に伝えるためにはどうすればよいか、という質問が投げられました。
その回答が、過去に自分が犯した設計上の間違いや苦労話で盛り上がる、そこを基点にしてどうすればそんな間違いや苦労をしなくて済むかという方向で話を進めれば伝わりやすいというものでした。
自分が以前新人教育を担当していたときのことを振り返ってみると「こうあるべきだ」ということを押し付け、それを素直に聞かない新人に対して苛立ち、さらに意固地になって押し付けるという悪循環に陥っていたように思う。
何も知らない新人に意見を押し付けるのではなく、自分で考え行動する様を見守りながら、失敗してどうすればいいかわからなくなっているときに初めて手を貸すくらいが丁度いいのかもしれない。
今年もまた新人がオフィスやってきた、こんな自分の下にも1人の新人がつく。
自分の下でいっぱい失敗してほしい。
その度に彼が少し前に進む手助けをしてあげられればな、と。
非同期処理にはThreadを直接使わずにExecutorServiceを使用する
一個前の記事 で
MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start();
みたいな感じでThread
クラスのコンストラクタにRunnable
実装したサブクラスを渡して非同期処理をしようと書いたけれど、以下の観点からおすすめできません。
- 複数Threadを作成する際、スレッド作成時間がパフォーマンスに影響を与える可能性がある。
- 作成したThreadがそのまま放置される。
そこで一度作成したスレッドを待機(プール)させ、必要に応じてスレッドを取り出してタスクを割り当て、タスクが完了したらスレッドを再度プールさせる(スレッドを再利用する)スレッドプールという仕組みを活用してパフォーマンスの向上を図ります。
スレッドプールはExecutor
クラスのメソッドで作成できます。
これによりExecutorService
オブジェクトが生成され、スレッドの実行を行います。
ExecutorService executorService = null; try { executorService = Executors.newFixedThreadPool(2); executorService.execute(new OneRunnable()); executorService.execute(new TwoRunnable()); } finally { executorService.shutdown(); }
Executors#newFixedThreadPool(int)
でスレッドプールを作成します。
execute
にRunnableオブジェクトを渡すと自動的にタスクをスレッド意に割り当てて実行されます。
Executors
は主に以下のようなメソッドを持ちます。
メソッド名 | 機能 |
---|---|
newFixedThreadPool | 引数で指定した数のスレッドを常時保持するスレッドプールを作成。 タスクは空いているスレッドに割り当てられます。 |
newCachedThreadPool | タスクを割り当てる度に新しいスレッドを生成し、そのスレッドを使いまわします。 一定時間使用されなかったスレッドは消滅します。 |
newScheduledThreadPool | タスクを一定時間ごとに実行するスレッドを持つスレッドプールを作成。 |
newSingleThreadExecutor | 1つのスレッドを使いまわすスレッドプールを作成。 割り当てられたタスクは順番に実行されます。 |
Threadクラスの継承ではなく、Runnableインタフェースを実装したクラスに処理を委譲する
javaで非同期処理を実現する際、以下のようにThread
クラスを継承したサブクラスを作成し
public class MyThread extends Thread { @Override public void run() { // 非同期処理 } }
下記のように呼び出すと思います。
MyThread thread = new MyThread();
thread.start();
これはそのまま以下のようにRunnable
インタフェースを実装したクラスで置き換えることが可能です。
public class MyRunnable implements Runnable { @Override public void run() { // 非同期処理 } }
MyRunnable runnable = new MyRunnable(); Thread thread = new Thread(runnable); thread.start();
Thread#run()
のデフォルト実装がThread
クラスのコンストラクタに渡されたRunnable
を実装したクラスのrunメソッドを実行するようになっているため上記のように置き換えが可能となります。
Runnable
インタフェースの実装を使うべき理由として以下が上げられます。
- 継承よりも委譲を使うことでクラス間の独立性が高まる。
Thread
クラスはスレッド生成、Runnable
実装は実行する処理を記述するものとしてそれぞれの責務を分離できる。
AndroidでSQLiteを使用する
データ定義
AndroidでSQLLiteを扱うにはSQLiteDatabase
クラスを使用します。
このSQLiteDatabase
のインスタンス生成を簡略化してくれるのがSQLiteOpenHelper
です。
SQLiteOpenHelper
は抽象クラスであり、DBオープン時、テーブルのバージョンアップ時の実装を追加することができます。
public class MySQLiteOpenHelper extends SQLiteOpenHelper { public MySQLiteOpenHelper(Context context) { // 任意のデータベースファイル名と、バージョンを指定する super(context, "pastingcontroller.db", null, 1); } /** * このデータベースを初めて使用する時に実行される処理 * @param db */ @Override public void onCreate(SQLiteDatabase db) { // テーブルの作成、初期データの投入等を行う。 } /** * アプリケーションの更新などによって、データベースのバージョンが上がった場合に実行される処理 */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // データの退避、テーブルの再構成等を行う。 } }
コンストラクタで任意の名称のデータベースを作成します。
onCreate
はこのヘルパクラスのインスタンスを使ってデータベースをオープンする際、コンストラクタで指定されたデータベースが無かった場合に実行されるメソッドです。
テーブルの作成や初期データの投入等を行います。
onUpgrade
はこのヘルパクラスのインスタンスを使ってデータベースをオープンする際、バージョンがあがっていた場合に実行されるメソッドです。(ここで言うバージョンはコンストラクタの第四引数のことです。)
テーブルの更新や、それに伴うデータの退避・再投入を行います。
たとえば、onCreate
でテーブルを作成する場合、以下のような実装になります。
@Override public void onCreate(SQLiteDatabase db) { db.execSQL( "create table sample_table (" + "_id integer primary key autoincrement not null, " + "text_column text not null, " + "num_column integer not null)" ); }
データ制御
データ操作を行うために、SQLiteDatabase
クラスのインスタンスを生成します。
作成したSQLiteOpenHelper
の実装を用いて以下のようにインスタンスを生成します。
SQLiteOpenHelper sqliteOpenHelper = new MySQLiteOpenHelper(getApplicationContext()); SQLiteDatabase db = sqliteOpenHelper.getWritableDatabase(); try { // データ操作 } finally { db.close(); }
getWritableDatabase
は読み書き両用のインスタンスを生成するのに用います。
読み取り専用のインスタンスが必要な場合はgetReadableDatabase
を使用します。
トランザクション
SQLiteでも意図的に記述することでトランザクション制御が可能です。
ループで大量のInsert等を投げる場合はトランザクション制御を行ったほうが処理が早くなるようです。
参考:SQLiteでINSERTが激しく遅い件
// トランザクション開始 db.beginTransaction(); try { /** Insert等のDB操作 */ db.setTransactionSuccessful(); } catch(Exception e) { e.printStackTrace(); } finally { // トランザクション終了 db.endTransaction(); }
beginTransaction
でトランザクションを開始し、endTransaction
でトランザクションを終了します。
トランザクション終了前にsetTransactionSuccessful
が呼ばれていればcommitされ、世ベレ帝無ければrollbackされます。なので、処理に成功した後は必ずsetTransactionSuccessful
を呼ぶ必要があります。
データ操作
登録
テーブルへのデータの登録(insert)は以下のように行います。
ContentValues values = new ContentValues(); values.put("text_column", "text"); values.put("num_column", 111); try { db.insert("sample_table", null, values); } finally { db.close(); }
登録に用いるデータははContentValues
オブジェクトに格納します。
insert
の第一引数にテーブル名、第三引数に登録したいデータを渡します。
第二引数は少々複雑です。
全ての項目がnull許可のテーブルに対して、空のContentValues
オブジェクトを渡した場合、発行されるSQLは以下のようになると思われます。
INSERT INTO foo;
しかし、SQLiteでは以下のように最低でも1つのカラム名を明記しなければ登録することができません。
INSERT INTO foo (somecol) VALUES (NULL);
上記の仕様を満たすために、第二引数には第三引数が空のオブジェクトだった場合にinsert文に加えるカラム名を渡すことになります。
参考:Insert method of SQLiteDatabase
更新
データの更新(update)は以下のように行います。
ContentValues values = new ContentValues(); values.put("num_column", 222); String whereClause = "text_column = ?"; String whereArgs[] = new String[1]; whereArgs[0] = "text"; try { db.update("sample_table", values, whereClause, whereArgs); } finally { db.close(); }
第一引数がテーブル名、第二引数が更新対象のContentValues
オブジェクト、第三引数がwhere句に指定する条件、第四引数が条件の"?"にバインドする値になります。
第四引数のwhereArgs
は文字列の配列です。数値型を条件にしたい場合は
String whereClause = "num_column = 111";
みたいな感じで直接条件文に記述することになります。
参考: Androidが再生産しているSQLインジェクション?
削除
データの削除(delete)は以下のようになります。
String whereClause = "text_column = ?"; String whereArgs[] = new String[1]; whereArgs[0] = "text"; try { db.delete("sample_table", whereClause, whereArgs); } finally { db.close(); }
読み取り
データの読み取り(select)は以下のようになります。
String[] columns = {"text_column ", "num_column"}; String selection = "text_column = ?"; String[] selectionArgs = {"text"}; String groupBy = null; String having = null; String orderBy = null; try { Cursor cursor = db.query("sample_table", columns, selection, selectionArgs, groupBy, having, orderBy); StringBuilder text = new StringBuilder(); while (cursor.moveToNext()){ String textColumn = cursor.getString(0); int numColumn = cursor.getInt(1); } } finally { db.close(); }
第一引数にテーブル名、第二引数に検索対象のカラム名(nullを指定すると全てが対象になります)、第三引数に検索条件、第四引数に検索条件へのバインド値を指定します。
必要に応じてgroup by句、having句、order by句を指定します。