2014/12/30

Android:Calabash for Android 導入

はじめに

Calabash for Androidは, Ruby製のビヘイビア駆動開発(BDD:Behaviour Driven Development)ツール Cucumberを使ってAndroidアプリをテストできるライブラリ.
Rubyのパッケージ管理システムgemでインストールできる.

今回の確認環境は次の通り.

  • Mac OSX Mavericks 10.9.5
  • RVM version 1.26.7
  • Ruby 2.2.0p0

準備

Rubyの環境を整えるためにHomebrewをインストールする

ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

Homebrewが正しくインストールされたかをチェック. エラーが出た場合はメッセージに従う

brew doctor

RVMをインストールする

\curl -sSL https://get.rvm.io | bash -s stable

PATHを設定するために.bashをリロード

source ~/.bash_profile

RVMのバージョンを確認

rvm version

Rubyの最新バージョンを確認

rvm list known

Ruvyの最新バージョンをインストールし, デフォルトに設定する.

rvm install ruby-2.2.0 --default

PATHを設定するために.bashをリロード

source ~/.bash_profile

Rubyのバージョンを確認

ruby -v

Rubyのインストールロケーションを確認

which ruby

Calabash for Androidのインストール

gem install calabash-android

AndroidSDKのパスをANDROID_HOME環境変数で定義

// .bash_profileに下記を追記
export ANDROID_HOME=[AndroidSDKへのパス]

PATHを設定するために.bashをリロード

source ~/.bash_profile

ANDROID_HOMEが正しく設定できているか確認

echo $ANDROID_HOME

テスト対象のAndroidプロジェクトに移動.
AndroidManifest.xmlがあるディレクトリに移動し下記コマンドを実行.

calabash-android gen

genによりcucumberのスケルトンが作成される(作成される各種ファイルについては後述)

calabashテスト実行時にインストールされるテストアプリに付与する署名を指定する.
この署名はテスト対象のアプリと同じ署名とする. 署名の指定は次のコマンドで行う.

# 実行するとカレントディレクトリに.calabash_settingファイルが生成される
calabash-android setup

テストを実行するには同階層で下記コマンドを実行する.

calabash-android run [テスト対象のapk]

実行するとテストが実行される.
cucumberが途中で上手く動作しない場合は-vオプションを付与することで詳細なログが出力される.

NOTE
calabash-android gen, calabash-android setup, calabash-android runはAndroidManifest.xmlと同階層で実行する.

参考

2014/12/27

Android:Findbugs

はじめに

FindBugsは次の機能を持つ静的検査ツールである.

  • Javaクラスバイナリに対する解析によりバグパターンの検査を行う
  • 解析結果を出力する

FindBugsはJavaクラスバイナリからバグパターンを解析するプログラム. ソースコードリポジトリへのコミット前後でFindBugsによるコード解析を行い, 潜在的な不具合を排除するツールとして非常に強力である.

Javaソースコードを解析したい場合はPMDが利用できる. 実動作により近いクラスバイナリを検査するという点ではFindBugsが有利であるが, ソースコード自体の品質を向上させたい場合はPMDやLintを使用する.
Groovyコードは同じくクラスコードを生成するがFindBugsによる正しい検査はできない.

また, 解析結果を出力する機能もあり, 各種CIツールとの親和性も高い.

FindBugsの動作環境

  • プラットフォーム非依存(GNU/Linux, MacOS X, Windows可)
  • J2SE 1.5以上のランタイムが必要
  • メモリ512MB以上が必要で, 解析対象が巨大である程これは増加する

FindBugs GUI

FindBugsはそれ単体をプログラムとして配布されている.
http://findbugs.sourceforge.net/ja/manual/installing.html#d0e94

FindBugsを単体実行するGUIツールも用意されているが, 昨今ではIDEアプリケーションのプラグインとして導入・実行されるケースが主であるためここでは割愛する.
http://findbugs.sourceforge.net/ja/manual/gui.html

Bug Pattern

下記ページに日本語訳(FindBugsv2.0.2)が纏められている.
http://www.acroquest.co.jp/webworkshop/JavaTroubleshooting/findbugs/

FindBugsを実行する際にはこれらのBug Patternを検査項目としてインプットする.
プロジェクトの特性に合わせて検査項目はフィルタされることもある.

Analysis Properties

FindBugsがソース解析する際の観点を分析プロパティとして実行時に指定することができる.
分析プロパティには主に次の2点を指定する.

  • 分析対象のメソッドの意味
  • 分析の精度

1点目, 分析対象メソッドの意味を指定することでより正確な分析結果を得ることができ, 誤検出を減らすことができる.
2点目, FindBugsが消費するメモリや分析時間が問題となる場合は精度を落とすことで解決できる場合がある. ただし, 検出されるべきバグを見逃す可能性が高くなるデメリットもある.

分析プロパティは-propertyで指定する. 次はその例.

$ findbugs -textui -property "cfg.noprune=true" myApp.jar

-propertyで指定できる分析オプションの一覧は下記を参照.
http://findbugs.sourceforge.net/ja/manual/analysisprops.html

FindBugs Plug-in

IntelliJ IDEA系 IDEにFindBugsを導入する場合は FindBugs-IDEA プラグインが便利.
https://plugins.jetbrains.com/plugin/3847?pr=idea

FindBugsは日本語情報も充実しており, Webで検索すればいくらでも見つけることができる.

CIツールでFindBugsを自動実行させたい場合, Gradleには標準でFindBugs-pluginが用意されている.
http://gradle.monochromeroad.com/docs/userguide/findbugs_plugin.html

NOTE
上記GradleのFindBugsプラグインはjavaプラグインと併用されることを前提としている.
Androidプロジェクトで作成されるbuild.gradleにはjavaプラグインが適用されないため, FindBugsプラグインを動作させるには少し工夫する必要がある.

次のgroovyコードをbuild.gradleに適用するとfindbugsタスクが追加され, Gradle consoleからfindbugsを実行することができるようになる.

apply plugin: 'findbugs'

task findbugs(type: FindBugs) {
    ignoreFailures = true
    classes = fileTree('build/intermediates/classes/debug/')
    source = fileTree('src/main/java/')
    classpath = files()
    effort = 'max'
}
tasks.withType(FindBugs) {
    reports {
        xml.enabled = false
        html.enabled = true
    }
}

参考

以上.

2014/12/26

Android:Lint

はじめに

Android LintはAndroidプロジェクトの潜在的不具合を検出するための支援プログラムである.
Android Lintでは次のようなAndroidに特化した問題を検出することができる.

  • 多言語化対応の不足 (翻訳されていない, るいは利用されていない文言)
  • レイアウトパフォーマンスの問題(旧layoutopt toolで検出されていたもの+α)
  • 未使用リソース
  • iconに関する問題(densityの間違いや間違ったサイズ etc…)
  • AndroidManifest関連のエラー
  • などなど他多数

Android Lintは単独動作できるコンポーネントでありCIツールとの連携が望まれている.
Lintのチェックリストは随時更新されており, Android SDK toolsとして配布されている.
検査結果をレポートすることもできる.

Android Lintの検査内容

Android SDK toolsに同梱されているlintバイナリに--showコマンドを発行すれば検査内容の一覧を確認できる.

$ lint --show

各検査項目にはPriority, Severity, Categoryが設定されている.

Priority
1~10(max:10). 問題の優先度.

Severity
Warning, Error, Fatal. 問題の重要度.

Category
Performance, Usability, Correctness, Security etc… 問題の種類.

Android Lintによる検査はリリースビルドを実行した際に自動実行される.
ここでは致命的なエラーのみを対象に検査され, 問題があった場合はビルドを中止する.

GradleのlintOptionでこれを停止することもできる.

android {
    lintOptions {
        abortOnError false
    }
}

Android Lintの実行

Android StudioであればGradleのタスクとしてlintが標準で作成されている.
GradleでAndroid Lintの挙動を制御したい場合は次のオプションを指定できる.

android {
    lintOptions {
        // set to true to turn off analysis progress reporting by lint
        quiet true
        // if true, stop the gradle build if errors are found
        abortOnError false
        // if true, only report errors
        ignoreWarnings true
        // if true, emit full/absolute paths to files with errors (true by default)
        absolutePaths true
        // if true, check all issues, including those that are off by default
        checkAllWarnings true
        // if true, treat all warnings as errors
        warningsAsErrors true
        // turn off checking the given issue ids
        disable 'TypographyFractions','TypographyQuotes'
        // turn on the given issue ids
        enable 'RtlHardcoded','RtlCompat', 'RtlEnabled'
        // check *only* the given issue ids
        check 'NewApi', 'InlinedApi'
        // if true, dont include source code lines in the error output
        noLines true
        // if true, show all locations for an error, do not truncate lists, etc.
        showAll true
        // Fallback lint configuration (default severities, etc.)
        lintConfig file("default-lint.xml")
        // if true, generate a text report of issues (false by default)
        textReport true
        // location to write the output; can be a file or 'stdout'
        textOutput 'stdout'
        // if true, generate an XML report for use by for example Jenkins
        xmlReport false
        // file to write report to (if not specified, defaults to lint-results.xml)
        xmlOutput file("lint-report.xml")
        // if true, generate an HTML report (with issue explanations, sourcecode, etc)
        htmlReport true
        // optional path to report (default will be lint-results.html in the builddir)
        htmlOutput file("lint-report.html")

        // set to true to have all release builds run lint on issues with severity=fatal
        // and abort the build (controlled by abortOnError above) if fatal issues are found
        checkReleaseBuilds true
        // Set the severity of the given issues to fatal (which means they will be
        // checked during release builds (even if the lint target is not included)
        fatal 'NewApi', 'InlineApi'
        // Set the severity of the given issues to error
        error 'Wakelock', 'TextViewEdits'
        // Set the severity of the given issues to warning
        warning 'ResourceAsColor'
        // Set the severity of the given issues to ignore (same as disabling the check)
        ignore 'TypographyQuotes'
    }
}

参考

2014/11/26

Android: SQLiteOpenHelperコンストラクタの引数CursorFactoryについて

Overview

SQLiteOpenHelperのコンストラクタを見てみると第三引数にCursorFactoryを受け取るようになっている.

SQLiteOpenHelper (Context, String, CursorFactory, int)

SQLiteデータベースを使用したサンプルコードではnullが指定されるケースが多く, あまり活用事例を見ない.

db = new MyDatabase(context, DB_NAME, null /*CursorFactory*/, DB_VERSION);

本稿ではCursorFactoryの利用する方法について記載する.

SQLiteDatabaseにクエリを発行するとCursorが返却される.
Android標準では, このCursor生成処理を拡張できるCursorFactoryクラスを用意している.
CursorFactoryを使ってCursorクラスを得るまでの一般的な流れは下記.

CursorFactoryを実装することで次のことが実現できる.

  • Cursor生成のタイミングをフックする
  • クエリ結果として独自Cursorを返す

ただし, 2点目の独自Cursorを返すについては注意事項がある(後述).

API Reference.

CursorFactoryと周辺のクラスを次に記す.

SQLiteOpenHelper - API Reference - Android Developers
SQLiteDatabaseの生成とバージョンを管理するクラス.

SQLiteDatabase - API Reference - Android Developers
SQLiteデータベースの生成・破棄・問合せといったSQLの実行や管理を行うクラス.

SQLiteDatabase.CursorFactory - API Reference - Android Developers
SQLiteDatabaseへのクエリ結果を格納したCursorのファクトリクラス. ここで生成されるCursorインスタンスがクエリの戻り値(Cursor)として使われる.

SQLiteCursor - API Reference - Android Developers
SQLiteへのクエリ結果を格納したCursorの具象クラス. 内部のSQLiteCursorを直接操作する場合は別途スレッドセーフを自前で担保する必要がある.

SQLiteQueryBuilder - API Reference - Android Developers
SQLiteDatabaseへのクエリ構築を手助けするビルダクラス.

Set CursorFactory

Android標準でCursorの生成はSQLiteDirectCursorDriver.queryで行われる.

public Cursor query(CursorFactory factory, String[] selectionArgs) {
    ...
        if (factory == null) {
            cursor = new SQLiteCursor(this, mEditTable, query);
        } else {
            cursor = factory.newCursor(mDatabase, this, mEditTable, query);
        }
    ...
}

CursorFactoryが指定されていればCursorFactory.newCursorメソッドを経由してCursorを生成する.
ここで独自のCursorFactoryが指定されていればCursorの生成をフックし, 独自Cursorを返すことができる.

implements

CursorFactoryはSQLiteDatabaseの取得時に指定する.
SQLiteDatabaseにCursorFactoryを設定するメソッドはいくつかある.

しかし, 一般的にSQLiteDatabaseのメソッドを直接操作することはせず, SQLiteOpenHelperやSQLiteQueryBuilderを経由する.
そのため, SQLiteOpenHelperやSQLiteQueryBuilderにもCursorFactoryを設定するためのAPIが用意されている.

public class MyDBHelper extends SQLiteOpenHelper{
    public MyDBHelper(Context context){
        super(context, DB_NAME, CURSOR_FACTORY, DB_VERSION);
    }
    ...
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
                    String[] selectionArgs, String sortOrder) {
    SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
    builder.setCursorFactory(CURSOR_FACTORY);
    ...
}

なお, CursorFactoryが指定されない場合はCursorのサブクラスであるSQLiteCursorが生成される.

次に, 独自のCursorFactoryを用意する.
CursorFactoryにはカーソルを生成するnewCursorメソッドが用意されており, ここでCursorを生成する.

public class MyCursorFactory implements CursorFactory {
    @Override
    public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
            String editTable, SQLiteQuery query) {
        return new SQLiteCursor(masterQuery, editTable, query);
    }
}

独自CursorFactoryを設定するためにSQLiteOpenHelperのコンストラクタ引数にこれを指定する.
他にもクエリ発行時にCursorFactoryを指定することもできる(後述).

public class MyContentProvider extends ContentProvider {
    @Override
    public boolean onCreate() {
        db = new MySQLiteOpenHelper(context, DB_NAME, 
                new MyCursorFactory(), DB_VERSION);
        ...
    }
}

Query API with CursorFactory

SQLiteDatabaseにクエリを問い合わせるために次のメソッドが用意されている.

queryWithFactory/rawQueryWithFactoryメソッドはCursorFactoryを直接指定してクエリを実行する.
一方, query/rawQueryメソッドは引数cursorFactoryにnullを指定して最終的にrawQueryWithFactoryを呼ぶ.

rawQueryWithFactoryメソッドは引数cursorFactoryにnullが指定されるとSQLiteDatabaseに設定されているCursorFactoryを使用する. この挙動にあたる部分のソースコードは下記.

// rawQuery系メソッドでは引数にnullを指定すると設定した独自CursorFactoryが使用される.
public Cursor rawQueryWithFactory(
        CursorFactory cursorFactory, String sql, String[] selectionArgs,
        String editTable, CancellationSignal cancellationSignal) {
    acquireReference();
    try {
        SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(
                this, sql, editTable, cancellationSignal);
        return driver.query(
                cursorFactory != null ? cursorFactory : mCursorFactory,
                selectionArgs);
    } finally {
        releaseReference();
    }
}

MEMO
SQLiteDatabase.queryメソッドは簡易な検索に向いている.
複雑なクエリを組み立てるにはデータバインドにも対応しているSQLiteDatabase.rawQueryメソッドが便利.

SQLiteDatabaseにクエリを発行するにはいくつか方法がある. 下記はその一例.

  1. SQLiteDatabaseインスタンスに直接クエリを投げる
  2. SQLiteQueryBuilder経由でクエリを投げる

SQLiteDatabase

前述の通りqueryWithFactory/rawQueryWithFactoryメソッドの引数にCursorFactoryを指定できる.
nullを指定した場合はSQLiteDatabaseに設定したCursorFactoryが使用される.

SQLiteQueryBuilder

結果的にSQLiteDatabaseのrawQueryWithFactoryが実行される.
SQLiteQueryBuilderにはCursorFactoryを指定するメソッドが用意されている.

Attention

SQLiteDatabaseを使ったアプリケーションではContentProviderも使われることが多い.
CursorFactoryとContentProviderを併用する場合に注意すべき点がある.

ContentProviderサイドで生成されたCursorは直接クライアントサイドには届かない.
SQLiteDatabaseで生成されるCursorはWrapやコピーを経てクライアントサイドに届く.
つまり, CursorFactoryで独自Cursorを返却しても, それを独自Cursorとして使用(Cast)できるのはそれを生成したContentProviderの処理内に限定される.
クライアントサイドで独自Cursorにキャストし直すことはできない.

Usage

CursorFactoryは使いどころの難しいAPIではあるが, たとえばCursor生成をフックして次のようにクエリをロギングする仕組みは楽に作ることができる.

public class MyCursorFactory implements CursorFactory {
    @Override
    public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
            String editTable, SQLiteQuery query) {
        if (debug) {
            Log.d(TAG, query.toString());
        }
        return new SQLiteCursor(masterQuery, editTable, query);
    }
}

以上.

2014/11/19

Android: MaterialDesign - Creating Apps with Material Design - part.3 Creating Lists and Cards

Creating Lists and Cards

To create complex lists and cards with material design styles in your apps, you can use the RecyclerView and CardView widgets.

material designを適用したリストやカードUIを作成するために, RecyclerViewCardViewを使用できます.

Create Lists

Figure 2
Figure 2 - Lists with RecyclerView.

The RecyclerView widget is a more advanced and flexible version of ListView. This widget is a container for displaying large data sets that can be scrolled very efficiently by maintaining a limited number of views. Use the RecyclerView widget when you have data collections whose elements change at runtime based on user action or network events.

RecyclerView widgetは, より最新で柔軟性のあるListViewのバージョンです. このwidgetは限られたviewをリサイクルして使い回すことで, 非常に効率的にスクロールできる大きなデータセットを表示するためのコンテナです. ユーザのアクションやネットワクークのイベントに基づいて, 実行時に要素が変わるデータの集まりを持つ場合は, RecyclerView widgetを使用してください.

The RecyclerView class simplifies the display and handling of large data sets by providing:

  • Layout managers for positioning items
  • Default animations for common item operations, such as removal or addition of items

You also have the flexibility to define custom layout managers and animations for RecyclerView widgets.

RecyclerViewのクラスは以下の機能が提供され, 表示や大きなデータセットの扱いを容易にします.

  • アイテムを位置付けるレイアウトマネージャー
  • アイテムの削除や追加のような一般的な操作のためのデフォルトアニメーション

また, RecyclerView widget用のカスタムレイアウトマネージャーやアニメーションを定義するための柔軟性を兼ね備えています.

Figure 1
Figure 1. The RecyclerView widget.

To use the RecyclerView widget, you have to specify an adapter and a layout manager. To create an adapter, extend the RecyclerView.Adapter class. The details of the implementation depend on the specifics of your dataset and the type of views. For more information, see the examples below.

RecyclerView widgetを使用するには, adapterと layout managerを指定する必要があります. adapterを生成するには, RecyclerView.Adapterクラスを継承します. 実装の詳細は, データセットやviewの種類によって異なります. 詳細は本ページ下部のExamplesを参照.

A layout manager positions item views inside a RecyclerView and determines when to reuse item views that are no longer visible to the user. To reuse (or recycle) a view, a layout manager may ask the adapter to replace the contents of the view with a different element from the dataset. Recycling views in this manner improves performance by avoiding the creation of unnecessary views or performing expensive findViewById() lookups.

layout managerは, RecyclerView内のアイテムを位置付けし, ユーザからは見えなくなったviewを再利用するタイミングを決定します. viewをリサイクルするために, レイアウトマネージャーはadapterに異なるデータでview要素の入れ替えを求めることがあります. このようにviewのリサイクルすることで, 不要なviewの生成やコストのかかるfindViewById()のルックアップ処理を防ぐことでパフォーマンスを改善します.

RecyclerView provides these built-in layout managers:

  • LinearLayoutManager shows items in a vertical or horizontal scrolling list.
  • GridLayoutManager shows items in a grid.
  • StaggeredGridLayoutManager shows items in a staggered grid.

To create a custom layout manager, extend the RecyclerView.LayoutManager class.

RecyclerViewはビルトインされた次のレイアウトマネージャーを提供します.

カスタムレイアウトマネージャーを作成するには, RecyclerView.LayoutManagerクラスを継承します.

Animations

Animations for adding and removing items are enabled by default in RecyclerView. To customize these animations, extend the RecyclerView.ItemAnimator class and use the RecyclerView.setItemAnimator() method.

RecyclerViewでは, アイテムの追加や削除のアニメーションがデフォルトで有効になっています. これらのアニメーションをカスタマイズするには, RecyclerView.ItemAnimatorクラスを継承し, RecyclerView.setItemAnimator()メソッドを使用します.

Examples

The following code example demonstrates how to add the RecyclerView to a layout:

以下のコードは, レイアウトにRecyclerViewを追加する方法の一例です.

<!-- A RecyclerView with some commonly used attributes -->
<android.support.v7.widget.RecyclerView
    android:id="@+id/my_recycler_view"
    android:scrollbars="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

Once you have added a RecyclerView widget to your layout, obtain a handle to the object, connect it to a layout manager, and attach an adapter for the data to be displayed:

レイアウトにRecyclerView widgetを追加したら, それを取得してlayout managerを登録し, データを表示するためのadapterをアタッチします.

public class MyActivity extends Activity {
    private RecyclerView mRecyclerView;
    private RecyclerView.Adapter mAdapter;
    private RecyclerView.LayoutManager mLayoutManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.my_activity);
        mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);

        // use this setting to improve performance if you know that changes
        // in content do not change the layout size of the RecyclerView
        mRecyclerView.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutManager = new LinearLayoutManager(this);
        mRecyclerView.setLayoutManager(mLayoutManager);

        // specify an adapter (see also next example)
        mAdapter = new MyAdapter(myDataset);
        mRecyclerView.setAdapter(mAdapter);
    }
    ...
}

The adapter provides access to the items in your data set, creates views for items, and replaces the content of some of the views with new data items when the original item is no longer visible. The following code example shows a simple implementation for a data set that consists of an array of strings displayed using TextView widgets:

adapterはデータセット内の各項目へのアクセスやviewの生成, 元のアイテムが見えなくなった時に新たなデータ項目でviewの内容を入れ替える仕組みを提供します. 以下のコードは, TextView widgetsを使用して表示したString配列から構成されたデータセットを実装する簡単な例です.

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
    private String[] mDataset;

    // Provide a reference to the views for each data item
    // Complex data items may need more than one view per item, and
    // you provide access to all the views for a data item in a view holder
    public static class ViewHolder extends RecyclerView.ViewHolder {
        // each data item is just a string in this case
        public TextView mTextView;
        public ViewHolder(TextView v) {
            super(v);
            mTextView = v;
        }
    }

    // Provide a suitable constructor (depends on the kind of dataset)
    public MyAdapter(String[] myDataset) {
        mDataset = myDataset;
    }

    // Create new views (invoked by the layout manager)
    @Override
    public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        // create a new view
        View v = LayoutInflater.from(parent.getContext())
                               .inflate(R.layout.my_text_view, parent, false);
        // set the view's size, margins, paddings and layout parameters
        ...
        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Replace the contents of a view (invoked by the layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        // - get element from your dataset at this position
        // - replace the contents of the view with that element
        holder.mTextView.setText(mDataset[position]);

    }

    // Return the size of your dataset (invoked by the layout manager)
    @Override
    public int getItemCount() {
        return mDataset.length;
    }
}

Create Cards

Figure 3
Figure 3. Card examples.

CardView extends the FrameLayout class and lets you show information inside cards that have a consistent look across the platform. CardView widgets can have shadows and rounded corners.

CardViewはFrameLayoutクラスを継承し, あらゆるプラットホームで一貫した外観を持つカード内の情報を表示することができます.

To create a card with a shadow, use the card_view:cardElevation attribute. CardView uses real elevation and dynamic shadows on Android 5.0 (API level 21) and above and falls back to a programmatic shadow implementation on earlier versions. For more information, see Maintaining Compatibility.

影があるカードを作成するには, card_view:cardElevation属性を使用します. CardViewは, Android 5.0 (API level 21)以上ではリアルなエレベーションとダイナミックな影を使い, それ以前のバージョンではプログラム的な影の実装に代替されます. 詳細は, Maintaining Compatibilityを参照.

Use these properties to customize the appearance of the CardView widget:

  • To set the corner radius in your layouts, use the card_view:cardCornerRadius attribute.
  • To set the corner radius in your code, use the CardView.setRadius method.
  • To set the background color of a card, use the card_view:cardBackgroundColor attribute.

これらのプロパティを使用してCardView widgetの外観をカスタマイズします.

  • レイアウトの角の丸みを設定するには, card_view:cardCornerRadius属性を使用します.
  • コード上で角の丸みを設定するには, CardView.setRadiusメソッドを使用します.
  • カードの背景色を設定するには, card_view:cardBackgroundColor属性を使用します.

The following code example shows you how to include a CardView widget in your layout:

以下のコード例は, レイアウトにCardView widgetを含む方法です.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    ... >
    <!-- A CardView that contains a TextView -->
    <android.support.v7.widget.CardView
        xmlns:card_view="http://schemas.android.com/apk/res-auto"
        android:id="@+id/card_view"
        android:layout_gravity="center"
        android:layout_width="200dp"
        android:layout_height="200dp"
        card_view:cardCornerRadius="4dp">

        <TextView
            android:id="@+id/info_text"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </android.support.v7.widget.CardView>
</LinearLayout>

For more information, see the API reference for CardView.

詳細は, CardViewのAPIリファレンスを参照.

Add Dependencies

The RecyclerView and CardView widgets are part of the v7 Support Libraries. To use these widgets in your project, add these Gradle dependencies to your app’s module:

RecyclerViewとCardViewのwidgetsはv7サポートライブラリの一部です. これらのwidgetを使用するには, 以下のGradleの依存関係をアプリのモジュールに追加します.

dependencies {
    ...
    compile 'com.android.support:cardview-v7:21.0.+'
    compile 'com.android.support:recyclerview-v7:21.0.+'
}

LICENSE
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.
Original source: http://developer.android.com/training/material/lists-cards.html

2014/11/07

Android: Creating Apps with Material Design - Using the Material Theme

Using the Material Theme

The new material theme provides:

  • System widgets that let you set their color palette
  • Touch feedback animations for the system widgets
  • Activity transition animations

新しいmaterial themeは以下を提供します.

  • カラーパレットを設定できるシステムウィジェット
  • システムウィジェット用タッチフィードバックアニメーション
  • Activity transitionアニメーション

You can customize the look of the material theme according to your brand identity with a color palette you control. You can tint the action bar and the status bar using theme attributes, as shown in Figure 3.

ブランドの特徴にマッチしたmaterial themeな外観とするためにカラーパレットをカスタマイズすることができます.

The system widgets have a new design and touch feedback animations. You can customize the color palette, the touch feedback animations, and the activity transitions for your app.

The material theme is defined as:

  • @android:style/Theme.Material (dark version)
  • @android:style/Theme.Material.Light (light version)
  • @android:style/Theme.Material.Light.DarkActionBar

For a list of material styles that you can use, see the API reference for R.style.

システムウィジェットは新しいデザインとタッチフィードバックアニメーションを持っています. アプリのカラーパレットやタッチフィードバックアニメーション, Activity transitionをカスタマイズすることができます.

material themeは, 次のように定義します.

  • @android:style/Theme.Material (dark version)
  • @android:style/Theme.Material.Light (light version)
  • @android:style/Theme.Material.Light.DarkActionBar

利用可能なmaterial styleの一覧は, R.styleのAPIリファレンスを参照.

Figure 1
Figure 1. Dark material theme

Figure 2
Figure 2. Light material theme

Note: The material theme is only available in Android 5.0 (API level 21) and above. The v7 Support Libraries provide themes with material design styles for some widgets and support for customizing the color palette. For more information, see Maintaining Compatibility.

注意:material themeは, Android5.0 (API level 21)以上で利用可能です. v7サポートライブラリは, いくつかのウィジェットのためのmaterial designのstyleを提供し, カラーパレットのカスタマイズをサポートます. 詳細は, Maintaining Compatibilityを参照.

Customize the Color Palette

To customize the theme’s base colors to fit your brand, define your custom colors using theme attributes when you inherit from the material theme:

ブランドに合うthemeのベースカラーにカスタマイズするためには, material themeを継承しtheme属性を使用してカスタムカラーを定義します.

<resources>
  <!-- inherit from the material theme -->
  <style name="AppTheme" parent="android:Theme.Material">
    <!-- Main theme colors -->
    <!--   your app branding color for the app bar -->
    <item name="android:colorPrimary">@color/primary</item>
    <!--   darker variant for the status bar and contextual app bars -->
    <item name="android:colorPrimaryDark">@color/primary_dark</item>
    <!--   theme UI controls like checkboxes and text fields -->
    <item name="android:colorAccent">@color/accent</item>
  </style>
</resources>

Figure 3
Figure 3. Customizing the material theme.

Customize the Status Bar

The material theme lets you easily customize the status bar, so you can specify a color that fits your brand and provides enough contrast to show the white status icons. To set a custom color for the status bar, use the android:statusBarColor attribute when you extend the material theme. By default, android:statusBarColor inherits the value of android:colorPrimaryDark.

material themeを使用すると, あなたのブランドに合ったステータスバー色にカスタマイズできます. ステータスバーの色は白色のステータスアイコンが見える範囲で指定します. ステータスバーにカスタムカラーを設定するには, material themeを拡張して, android:statusBarColor属性を使用します. デフォルトでandroid:statusBarColorandroid:colorPrimaryDarkの値を継承します.

You can also draw behind the status bar yourself. For example, if you want to show the status bar transparently over a photo, with a subtle dark gradient to ensure the white status icons are visible. To do so, set the android:statusBarColor attribute to @android:color/transparent and adjust the window flags as required. You can also use the Window.setStatusBarColor() method for animations or fading.

また, ステータスバーの背後に描画することもできます. 例えば, 写真の上に透明なステータスバーを表示したい場合, わずかに濃いグラデーションを敷いて白のステータスアイコンが見えるようにします. そのためには, android:statusBarColor属性に@android:color/transparentを設定し, 必要に応じてwindow flagを調整します. また, アニメーションやフェージングのためにWindow.setStatusBarColor()メソッドを使用できます.

Note: The status bar should almost always have a clear delineation from the primary toolbar, except for cases where you show edge-to-edge rich imagery or media content behind these bars and when you use a gradient to ensure that the icons are still visible.

注意:ステータスバーにコンテンツを表示したり, 全画面表示のケースを除き, ステータスバーはプライマリツールバーと明確に分けられるべきです.

When you customize the navigation and status bars, either make them both transparent or modify only the status bar. The navigation bar should remain black in all other cases.

ナビゲーションバーやステータスバーをカスタマイズする際, それら両方を透明にするかステータスバーのみを変更します. その他の全てのケースにおいてはナビゲーションバーは黒のままとすべきです.

Theme Individual Views

Elements in XML layout definitions can specify the android:theme attribute, which references a theme resource. This attribute modifies the theme for the element and any child elements, which is useful for altering theme color palettes in a specific portion of an interface.

XMLレイアウトで定義する要素には, android:theme属性でthemeリソースを指定できます. この属性は, 要素や子要素のテーマも変えるため, 特定部分のテーマカラーパレットを変更するのに便利です.

LICENSE
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

Original source: http://developer.android.com/training/material/theme.html

2014/11/06

Android: Creating Apps with Material Design - Getting Started

Getting Started

To create apps with material design:

  1. Review the material design specification.
  2. Apply the material theme to your app.
  3. Create your layouts following material design guidelines.
  4. Specify the elevation of your views to cast shadows.
  5. Use system widgets for lists and cards.
  6. Customize the animations in your app.

Material designに対応したアプリを作成するためには…

  1. Material designの仕様を確認する
  2. Material themeを適用する
  3. Material designガイドラインに従ったlayoutを作成する
  4. Viewの影をおとすelevationを指定する
  5. リストとカードのためのwidgetを利用する
  6. animationをカスタマイズする

Maintain backward compatibility
You can add many material design features to your app while maintaining compatibility with versions of Android earlier than 5.0. For more information, see Maintaining Compatibility.

下位互換性の維持
Android 5.0以前のバージョンとの互換性を維持しつつ, 多くのMaterial designの機能をアプリに追加できます. 詳細はMaintaining Compatibilityを参照.

Update your app with material design
To update an existing app to incorporate material design, update your layouts following material design guidelines. Also make sure to incorporate depth, touch feedback, and animations.

Material design化されたアプリにアップデート
Material designを組み込むために既存のアプリを改良するには, レイアウトをMaterial designのガイドラインに従って更新します. また, 奥行きやタッチフィードバック, アニメーションを組み込むようにして下さい.

Create new apps with material design
If you are creating a new app with material design features, the material design guidelines provide you with a cohesive design framework. Follow those guidelines and use the new functionality in the Android framework to design and develop your app.

新しいMaterial design化されたアプリの作成
もし, Material designの機能を持つ新しいアプリを作成する場合は, Material designのガイドラインがデザインフレームワークにもなる. これらのガイドラインに従い, Androidフレームワークの新しい機能を利用・設計してアプリを開発します.

Apply the Material Theme

To apply the material theme in your app, specify a style that inherits from android:Theme.Material:

アプリにMaterial themeを適用するには, android:Theme.Materialを継承したstyleを指定します.

<!-- res/values/styles.xml -->
<resources>
  <!-- your theme inherits from the material theme -->
  <style name="AppTheme" parent="android:Theme.Material">
    <!-- theme customizations -->
  </style>
</resources>

The material theme provides updated system widgets that let you set their color palette and default animations for touch feedback and activity transitions. For more details, see Using the Material Theme.

Material themeはカラーパレット, タッチフィードバックのための標準アニメーション, Activity transitionsを提供する刷新されたWidgetを提供します. 詳細は, Using the Material Themeを参照.

Design Your Layouts

In addition to applying and customizing the material theme, your layouts should conform to the material design guidelines. When you design your layouts, pay special attention to the following:

  • Baseline grids
  • Keylines
  • Spacing
  • Touch target size
  • Layout structure

Material themeを適用しカスタマイズすることに加えて, レイアウトをMaterial designガイドラインこれに準拠させる必要があります. レイアウトの設計には, 次の点に注意しましょう.

  • Baseline grids
  • Keylines
  • Spacing
  • Touch target size
  • Layout structure

Specify Elevation in Your Views

Views can cast shadows, and the elevation value of a view determines the size of its shadow and its drawing order. To set the elevation of a view, use the android:elevation attribute in your layouts:

Viewには影を設定することができ, Viewのelevation値は影のサイズと描画順序を決定します. Viewにelevationを設定するためには, レイアウトのattributeにandroid:elevationを使用します.

<TextView
    android:id="@+id/my_textview"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/next"
    android:background="@color/white"
    android:elevation="5dp" />

The new translationZ property lets you create animations that reflect temporary changes in the elevation of a view. Elevation changes can be useful when responding to touch gestures.

For more details, see Defining Shadows and Clipping Views.

新しいプロパティtranslationZは, Viewのelevationの変更をアニメーションさせることができます. elevationの変更はタッチジェスチャーの反応を表現する時に役立ちます.

詳細は, Defining Shadows and Clipping Viewsを参照.

Create Lists and Cards

RecyclerView is a more pluggable version of ListView that supports different layout types and provides performance improvements. CardView lets you show pieces of information inside cards with a consistent look across apps. The following code example demonstrates how to include a CardView in your layout:

RecyclerViewListViewと比べて拡張性に富み, 様々なレイアウトタイプをサポートしパフォーマンスの向上を見込めます. CardViewは情報をカード内に表示し, アプリ間で一貫した外観を提供します. 次のコードはレイアウト内にCardViewを含める方法です.

<android.support.v7.widget.CardView
    android:id="@+id/card_view"
    android:layout_width="200dp"
    android:layout_height="200dp"
    card_view:cardCornerRadius="3dp">
    ...
</android.support.v7.widget.CardView>

For more information, see Creating Lists and Cards.

詳細はCreating Lists and Cardsを参照.

Customize Your Animations

Android 5.0 (API level 21) includes new APIs to create custom animations in your app. For example, you can enable activity transitions and define an exit transition inside an activity:

Android5.0 (API level 21)は, カスタムアニメーションを作成するための新しいAPIを含んでいます. 例えば, Activity transitionsと終了時のtransitionを定義することができます.

public class MyActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // enable transitions
        getWindow().requestFeature(Window.FEATURE_CONTENT_TRANSITIONS);
        setContentView(R.layout.activity_my);
    }

    public void onSomeButtonClicked(View view) {
        getWindow().setExitTransition(new Explode());
        Intent intent = new Intent(this, MyOtherActivity.class);
        startActivity(intent,
                      ActivityOptions
                          .makeSceneTransitionAnimation(this).toBundle());
    }
}

When you start another activity from this activity, the exit transition is activated.

To learn more about the new animation APIs, see Defining Custom Animations.

このActivityから別のActivityを起動する時, 終了transitionが始動します.

新しいアニメーションのAPIについてさらに学ぶには, Defining Custom Animationsを参照.

LICENSE
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

Original source: http://developer.android.com/training/material/get-started.html

2014/11/05

Android:Creating Apps with Material Design

Creating Apps with Material Design

Material design is a comprehensive guide for visual, motion, and interaction design across platforms and devices. To use material design in your Android apps, follow the guidelines described in the material design specification and use the new components and functionality available in Android 5.0 (API level 21).

Material designは, プラットフォームやデバイスの垣根を越えたビジュアル, モーション, インタラクションのためのデザインガイドです.
Material designをAndroidアプリで使用するには, ガイドラインに従い, Android 5.0(API level 21)で利用可能となった新しいコンポーネントや機能を使用してください.

This class shows you how to create material design apps with the following elements:

  • The material theme
  • Widgets for cards and lists
  • Custom shadows and view clipping
  • Vector drawables
  • Custom animations

This class also teaches you how to maintain compatibility with versions of Android earlier than 5.0 (API level 21) when you use material design features in your app.

ここでは, 以下の要素を持つMaterial designなアプリの作成方法を記載します.

  • Material theme
  • カードとリストのためのウィジェット
  • 影とViewクリップ
  • ベクターDrawable
  • カスタムアニメーション

さらに, Android5.0(API level 21)以前のバージョンへの互換性を維持する方法を記載します.

Lessons

Getting Started
Learn how to update your app with material design features.

Using the Material Theme
Learn how to apply material design styles to your app.

Creating Lists and Cards
Learn how to create lists and cards with a consistent look and feel using system widgets.

Defining Shadows and Clipping Views
Learn how to set elevation for your views to create custom shadows and how to clip views.

Working with Drawables
Learn how to create vector drawables and how to tint drawable resources.

Defining Custom Animations
Learn how to create custom animations for views and activity transitions with shared elements.

Maintaining Compatibility
Learn how to maintain compatibility with platform versions earlier than Android 5.0.


LICENSE
Portions of this page are modifications based on work created and shared by the Android Open Source Project and used according to terms described in the Creative Commons 2.5 Attribution License.

Original source: http://developer.android.com/training/material/index.html

2014/10/31

Android: SQLite3 LockとTransaction Immediate/Exclusive

Intro

AndroidのSQLite3のLockとTransaction Immediate/Exclusiveについてまとめた.
基礎となる知識としては こちら を参照.

Locking And Concurrency In SQLite3

Oracle等のDBMSでは”行ロック”など細かくロック粒度を制御できるが, SQLiteではこれができない.
SQLiteのロック粒度は”データベース単位”のみである. このためRead/Writeロック取得~解放までの間, 他のトランザクションはデータベースにアクセスできない. (このことは”データベースを分割する/しない”の判断基準の1つにはなりそう)

AndroidではSQLiteのトランザクションモードとしてImmediateとExclusiveをAPIで指定できる. 特に指定しない場合はExclusiveモードとなる.

Transaction Immediate

ロック取得中は他トランザクションの書き込みを制限し, 読み込みを許可するmode.
Exclusive modeより緩い分離レベルであるためトランザクションの並列化を促進でき, パフォーマンス面で期待できる. ただし, Phantom Readが発生する可能性を考慮する必要がある.

IMMEDIATE.
トランザクション開始時にRESERVEDロックを取得する. BEGIN IMMEDIATEによりRESERVEDロックが取得されると, 他のデータベースコネクションはデータベースに書き込んだり, BEGIN IMMEDIATE or BEGIN EXCLUSIVEを実行することができなくなるが, データベースからの読み込みを継続することはできる.
ANSI/ISO SQL標準では REPEATABLE_READ に相当する.

アプリケーションがデータベースへアクセスするのに書き込み専用トランザクション と 読み込み専用トランザクションのようにRead/Writeを分離できるのであればImmediate modeは有効に作用する.

Immediate modeではPhantom Readが発生するため, 次のようなシーケンスでは一貫性が損なわれる.

Created with Raphaël 2.1.0Transaction ATransaction AShared ResourceShared ResourceTransaction BTransaction BBEGIN TRANSACTIONBEGIN TRANSACTIONREAD ALL (x)DELETE (x)INSERT (y)COMMIT TRANSACTIONREAD ALL (y)Expected xBut was y-Phantom Read-

Transaction Exclusive

ロック取得中は他トランザクションの読み込み/書き込みを制限するmode.
Immediateより厳しい分離レベルであるためトランザクションの直列化が促進され, データの一貫性が向上する. ただしパフォーマンスはImmediateに劣る可能性が高い.

EXCLUSIVE
トランザクション開始時にEXCLUSIVEロックを取得する. BEGIN EXCLUSIVEによりEXCLUSIVEロックが取得されると, READ_UNCOMMITTEDの接続を除いた全てのデータベースコネクションからデータを読み込むことができなくなる. データベースへの書き込みについては例外なくトランザクションの完了まで許可されない.
ANSI/ISO SQL標準では SERIALIZABLE に相当する.

データベースに対して完全なデータの一貫性が求められる場合はExclusive modeを指定する.

Implements

クライアントからTransaction modeを指定するにはSQLiteDataBaseクラスを使用する.

  • SQLiteDataBase.beginTransaction()
  • SQLiteDataBase.beginTransactionWithListener(SQLiteTransactionListener)
  • SQLiteDataBase.beginTransactionNonExclusive()
  • SQLiteDataBase.beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)

beginTransactionNonExclusive/beginTransactionWithListenerNonExclusiveはAPI Level11で追加されたAPI.

beginTransaction/beginTransactionWithListenerで開始されたトランザクションはExclusive modeで,
beginTransactionNonExclusive/beginTransactionWithListenerNonExclusiveで開始されたトランザクションはImmediate modeで動作する.

// Transaction Exclusive mode
db.beginTransaction();
try {
    // do something.
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}

// Transaction Immediate mode
db.beginTransactionNonExclusive();
try {
    // do something.
    db.setTransactionSuccessful();
} finally {
    db.endTransaction();
}

SQLiteDatabaseLockedException

ロックされているデータベースに対し, SQLiteDatabase経由でクエリを実行した場合の挙動は下記.

SQLiteDatabase.query

ロックが取得できるまでトランザクションは待機される. API Level16以降はCancellationSignalを使ってクエリをキャンセルすることができる.

SQLiteDatabase.insert

SQLiteDatabaseがSQLiteDatabaseLockedExceptionをキャッチし, 呼出し元に-1を返す.

public long insert(String table, String nullColumnHack, ContentValues values) {
    try {
        return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
    } catch (SQLException e) {
        Log.e(TAG, "Error inserting " + values, e);
        return -1;
    }
}

SQLiteDatabase.insertOrThrow

SQLiteDatabaseLockedExceptionがスローされる.

public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
        throws SQLException {
    return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
}

SQLiteDatabase.update

SQLiteDatabaseLockedExceptionがスローされる.

SQLiteDatabase.delete

SQLiteDatabaseLockedExceptionがスローされる.

SQLiteDatabase.beginTransaction

SQLiteDatabaseLockedExceptionがスローされる.

Reference

File Locking And Concurrency In SQLite Version 3

以上.

Android: 同時実行制御とSQLite3

Intro

同時実行制御とは, 共有リソースにアクセスする複数のトランザクションを, いかに並列処理しつつ競合によるデータ破壊を回避するかを扱う分野である.
昨今のアプリケーションではクライアントが複数いることは珍しくない. 同時アクセスを許容せず, クライアントからの要求を直列的に処理できれば同時実行制御の課題は解決されるが, アプリケーションのレスポンスは極端に低下し使い物にならない.
パフォーマンスの低下を抑えるためにトランザクションを並列化し, かつリソースの整合性を保証しなければならないため, 同時実行制御を考える必要が出てくる.

Conflict

並列処理されるトランザクションの競合を制御しなかった場合の問題は主に4種類ある.
基本的にデータの一貫性は保証されない.

Dirty Read (w-r)

コミットされていないデータを別のトランザクションから参照できてしまう問題. トランザクションがロールバックされた場合に, 別のトランザクションから参照されていたデータはDirtyになる.
Created with Raphaël 2.1.0Transaction ATransaction AShared ResourceShared ResourceTransaction BTransaction BBEGIN TRANSACTIONBEGIN TRANSACTIONREAD (x=0)WRITE (x=1)READ (x=1)ROLLBACK TRANSACTIONExpected x=0But was 1-Dirty Read-

Lost Update / Overwrite (w-w)

別のトランザクションによる書き込みで, 更新したデータが上書きされてしまう問題.
Created with Raphaël 2.1.0Transaction ATransaction AShared ResourceShared ResourceTransaction BTransaction BBEGIN TRANSACTIONBEGIN TRANSACTIONWRITE (x=1)WRITE (x=0)COMMIT TRANSACTIONCOMMIT TRANSACTIONExpected x=1But was 0-Lost Update-

Non-Repeatable Read / Fuzzy Read (r-w)

トランザクション内で別のトランザクションによるデータ更新が反映されてしまい, 同じレコードでも参照するたびに結果が変わる.
Created with Raphaël 2.1.0Transaction ATransaction AShared ResourceShared ResourceTransaction BTransaction BBEGIN TRANSACTIONBEGIN TRANSACTIONREAD (x=0)WRITE (x=1)COMMIT TRANSACTIONREAD (x=1)Expected x=0But was 1-Fuzzy Read-

Phantom Read / Inconsistent Read (r-w)

同じトランザクション内でも, データの参照結果が増減する. 別のトランザクションによるデータ更新 or 削除が参照できることが原因で発生する.
Created with Raphaël 2.1.0Transaction ATransaction AShared ResourceShared ResourceTransaction BTransaction BBEGIN TRANSACTIONBEGIN TRANSACTIONREAD ALL (x)DELETE (x)INSERT (y)COMMIT TRANSACTIONREAD ALL (y)Expected xBut was y-Phantom Read-

Isolation

Isolationは分離性や隔離性, 独立性と訳される(本項では分離性とする). 分離性とはトランザクションを並列処理する中で, どこまでトランザクション同士の干渉を回避して読み取り一貫性を保証するかの度合いである.

Consistency and Isolation

分離レベルとはトランザクションの並列性/直列性の度合いを定義したものである. ACID特性の中でも分離性は柔軟解釈されている.
データベースソフトウェアでは分離レベルを選択できるものが多い. これは分離レベルの度合いにより拡張性や性能に大きな影響を与えるためである.
分離レベルは緩いほどパフォーマンスが向上する. 反対に厳しくするほどパフォーマンスは犠牲になる. ただし, 分離レベルを厳しくするほどデータの一貫性は保証されていく. 分離レベルには次のものがある.
  1. Read Uncommitted
  2. Read Committed
  3. Monotonic View
  4. Repeatable Read
  5. Serializability
Serializabilityは特定のリソースに対するアクセスを同時に1つのトランザクションしか実行させない形式, つまり各トランザクションが完全に直列実行されるため, トランザクションは交差することなく完全に分離される. Serializabilityは最も厳しい分離レベルである. このレベルは同時実行制御に関係する多くの問題が解決されるため一貫性の面においては理想的なレベルであるが, トランザクションが直列化することによりパフォーマンスを大きく損なうため多くの場合採用するに至らない.
少なくない数のクライアントを抱えているアプリケーションやリアルタイム性を求められるアプリケーションの場合, Serializability分離レベルを選択するのは現実的ではない1.
“では, パフォーマンスを向上しよう”という話になり, トランザクションを並列化させる方向に進んでいく. つまり同時実行制御を考える必要が出てくる.
  • トランザクションの並列化を促進する程, パフォーマンスは向上する.
  • トランザクションの直列化を促進する程, パフォーマンスは低下する.
ただし,
  • トランザクションの並列化を促進する程, 同時実行制御は複雑になる.
  • トランザクションの直列化を促進する程, 同時実行制御は単純になる.

Isolation Levels

トランザクションの代表的な分離レベル(ANSI/ISO表記)を次に記載する.
データ一貫性を保証するために全ての分離レベルではDirty Writeを回避する必要がある. 分離レベルが緩い(より並列化させる)ほど同時実行制御は複雑化しデータ破壊のリスクが高まる.

Read Uncommitted

他のトランザクションによるコミット前データの読み込みを許容する分離レベル. トランザクションは極めて並列に実行されるためパフォーマンスは高い. ただし, この分離レベルではDirty Writeは回避されるもののDirty Read, Fuzzy Read, Phantom Read, Lost Updateなど様々な問題が発生するため, 競合が発生する状況下ではデータ破壊の可能性も高くなる.

Read Committed

コミット済みデータが読み込まれることを保証する分離レベル. ただし, トランザクションの途中でも他のトランザクションがコミットしたデータが読み込まれる.
この分離レベルではDirty Readを回避できるが Fuzzy Read, Phantom Read, Lost Updateは回避できない.

Repeatable Read

トランザクションで読み込んだデータはコミットするまで変更されないことを保証する分離レベル.
保証するのは読み込んだデータに対してのみであり, それ以外のレコード追加などは保証されないので範囲指定問合せによるPhantom Readは防げない.

Serializability

完全な一貫性を保証する分離レベル. 全てのトランザクションが直列に実行されているように見える特徴がある.
競合問題と分離レベルの関係をまとめる.
- Dirty Read Lost Update Fuzzy Read Phantom Read
READ UNCOMMITTED 発生する 発生する 発生する 発生する
READ COMMITTED 発生しない 発生する 発生する 発生する
REPEATABLE READ 発生しない 発生しない 発生しない 発生する
SERIALIZABLE 発生しない 発生しない 発生しない 発生しない

Exclusive control

Shared lock, Exclusive lock

トランザクションの競合が発生すると前述の競合問題が発生する. 競合を回避してデータの一貫性を保証するためには共有リソースへのアクセスを制限するロックメカニズムを導入して排他制御を促進する.
排他制御とは, 他のトランザクションから共有リソースへのアクセスを制限(ロック)するものである. ロックはトランザクションの完了まで保持されたあと解除され, 他のトランザクションはそれまで共有リソースへアクセスすることができない.
ロックは他のトランザクションのアクセスを制限するという特性から, パフォーマンスに大きく影響する. そのためロックにはいくつか形態があり, 用途によって使い分けられる. ロックの形態は大きく2つ存在する.

Shared lock - 共有ロック

トランザクションがデータを読み込む場合のロック方式. データが共有ロックされている間は他のトランザクションでもデータを読み込むことができるが, データを書き込むことはできない.

Exclusive lock - 排他ロック

トランザクションがデータを書き込む場合のロック方式. データが排他ロックされている間は, 他のトランザクションからデータを読み込むことも書き込むこともできない.
また, ロックするデータの範囲をロック粒度(またはロックレベル)という. ロックするデータの範囲にはテーブル全体, 行全体, 特定の列がある. ロック粒度の程度には一長一短がある.
  • ロック粒度を小さくするとトランザクションの並列性が高くなる
  • ロック粒度を小さくするとトランザクションの競合頻度は高くなる
また,
  • ロック粒度を大きくするとトランザクションの並列性が低くなる
  • ロック粒度を大きくするとトランザクションの競合頻度は低くなる

Pessimistic mechanism, Optimistic mechanism

ロックを取得する期間も重要である. トランザクションは競合を検知するとデータの一貫性を保証するためにロールバックを実行し, トランザクション処理をキャンセルする. ロックの期間を短くするのは大切だが, 短くし過ぎてロールバックが頻発するのは問題である.
  • ロック取得の期間を長くすれば競合の排除を促進できる
  • ロック取得の期間を短くすれば競合の排除は抑制される
また,
  • ロック取得の期間を長くすると空振り(ロールバック)に終わる頻度は下がる
  • ロック取得の期間を短くすると空振り(ロールバック)に終わる頻度が上がる
ただし,
  • ロック取得の期間を長くすると別のトランザクションの待ち時間が延びる
  • ロック取得の期間を短くすると別のトランザクションの待ち時間が縮まる
さらに,
  • ロック取得の期間を長くするとデッドロックの発生確率が上がる
  • ロック取得の期間を短くするとデッドロックの発生確率は下がる
ロックにはパフォーマンスの話題がつきもので, 最適なロックの粒度と期間を選択しないとパフォーマンスの低下を招く.
ロックを取得するタイミングには2通りに大別される. 悲観的ロック と 楽観的ロック である.
悲観的ロックはロックの期間を長くとる. 反対に, 楽観的ロックではロックの期間を短くとる. システムの特性にあわせてどちらの方式を採用するかを決める.

Pessimistic mechanism - 悲観的方式

排他制御において, “トランザクションは高頻度で競合する前提”で考えられた悲観的な方式. 主にロックで排他制御を実現する.
悲観的方式では, トランザクションがデータにアクセスし始めた時点からコミットまでロックを保持する. この間, 別のトランザクションがリソースにアクセスすることを制限する.
悲観的方式はトランザクションを直列処理するため, 排他制御の扱いは容易に映るが, デッドロックやパフォーマンスの面で注意が必要である.

Optimistic mechanism - 楽観的方式

排他制御において, “トランザクションは低頻度で競合する前提”で考えられた楽観的な方式. 主に競合検出で排他制御を実現する.
楽観的方式では, トランザクション中に別のトランザクションによるデータ更新がなかったかを検査(競合検出)してからコミットする. トランザクションが更新操作中でも別のトランザクションからの操作は制限されない. コミット時に競合検出された場合は当該トランザクションはロールバックされる.
悲観的方式と楽観的方式は一長一短であり, それだけで優劣をつけるものではなく要件にあって選択すべきものである.
リソースの更新リクエストが頻発すると予想されるシステムでは, 書き込みに最適化させたい場合が多い. このケースでは悲観的方式を選択できる.
リソースの参照リクエストが中心となるシステムでは読み込みに最適化させたい場合が多い. このケースでは楽観的ロックが選択できる.
一般的にRDBMSは書き込み操作に最適化されており, 悲観的方式が採用される2.
一方, Webサーバは読み込み操作に最適化されており, レスポンス性を高めるために楽観的方式が採用されやすい.

SQLite3

SQLite3における同時実行制御関連の情報.

ロック粒度

DBMSによってはロックの粒度を行や列単位で指定できるものがある. ただし, SQLiteではデータベース全体のロック粒度しか選択できない.
SQLite.org - Appropriate Uses For SQLite … High Concurrency

ロック状態

SQLiteは共有ロック, 排他ロックの仕組みを備えており, プロセスを跨いで同時実行されても適切に処理される.
SQLiteのトランザクションには複数のモードが存在し, 各モードや実行内容によって取得されるロック種別が変わる.
UNLOCKED
非ロック状態. どのプロセスも書き込み/読み込みしていないデータベースの初期状態.
SHARED
データを書き込みを伴わずに読み込む場合に取得されるロック. 他のプロセスからの読み込みを許可するが書き込みは許可しない. SHAREDロックは複数のプロセスに保持させることができるため, 他のプロセスからの読み込みに対してオープンである.
RESERVED
将来データを書き込む予定だが現状読み込みしかされていない場合に取得されるロック. SHAREDロックとRESERVEDロックを共存させることができるが, RESERVEDロックはある時点で1つしか保持されない.
PENDING
EXCLUSIVEロックを取得してデータの書き込みを実行しようとSHAREDロックの開放を待機する場合に取得されるロック. 全てのSHAREDロックが開放されるとEXCLUSIVEロックが取得される.
EXCLUSIVE
データを書き込むために必要なロック. PENDINGロックからEXCLUSIVEロックに状態遷移するため, EXCLUSIVEロックが取得されているタイミングでは他のすべてのロックは解放されている. EXCLUSIVEと他のロックとを共存させることはできない.
See. SQLite.org - File Locking And Concurrency In SQLite Version 3

分離レベル

SQLiteにおける分離レベルはANSI/ISO SQL標準の表記とは異なっており, ロックを取得するタイミングに主眼を置いた表記となっている.
DEFERRED
トランザクション開始時にはロックを取得せず, データの読み込み/書き込みをする時点までロック取得を延期する. そのため, BEGINステートメントによるトランザクション開始のみでは何のロックも取得されない. ロックの取得がBEGIN~データの読み込み/書き込みまで延期されるため, 別トランザクションによるデータ書き込みの割り込みが発生する可能性がある. (ANSI/ISO SQL標準では READ_COMMITTED に相当. )
IMMEDIATE
トランザクション開始時にRESERVEDロックを取得する. BEGIN IMMEDIATEによりRESERVEDロックが取得されると, 他のデータベースコネクションはデータベースに書き込んだり, BEGIN IMMEDIATE or BEGIN EXCLUSIVEを実行することができなくなるが, データベースからの読み込みを継続することはできる. ( ANSI/ISO SQL標準では REPEATABLE_READ に相当. )
EXCLUSIVE
トランザクション開始時にEXCLUSIVEロックを取得する. BEGIN EXCLUSIVEによりEXCLUSIVEロックが取得されると, READ_UNCOMMITTEDの接続を除いた全てのデータベースコネクションからデータを読み込むことができなくなる. データベースへの書き込みについては例外なくトランザクションの完了まで許可されない. ( ANSI/ISO SQL標準では SERIALIZABLE に相当. )
See. SQLite.org - BEGIN TRANSACTION
See. SQLite.org - PRAGMA read_uncommitted

Reference

InfoQ - Web開発者が知っておくべき八つの分離レベル
以上.

  1. eBayやAmazonといった巨大Webアプリケーションが楽観的で緩やかな一貫性保証の方が, 古くからの悲観的メカニズムより拡張性に富んでいることを証明した例もある
  2. Oracle Databaseには楽観的ロックのイメージが強いが, 悲観的ロックも指定できる