2013年5月31日金曜日

[Android]AndroidのSQLiteはFULL AUTO VACUUM (auto_vacuum = 1)

普通(Android以外?)で利用するSQLiteでは、データ使用したデータ領域を開放しません。なので、delete/insertを繰り返すと、データが肥大化するため、フラグメンテーションを解消する必要があります。そのコマンドが、Vacuumです。 たとえば、日に1度データの入れ替えを行うために、全マスタをdeleteしてサーバから最新をとってinsertするというようなシステムでは、フラグメンテーションを解消しないと、性能的にも問題が発生する恐れがあります。そのため、Vacuumするのですが、先日TLを眺めていると「AndroidのSQLiteはauto vacuumだよー」的な発言が。。。 

「ウソだろ!」と思って、手元にある2.3系と4.2系で以下のようなコードで確認してみました。
コードはこんな感じ。


        DBHelper helper = new DBHelper(this);
        SQLiteDatabase database = helper.getReadableDatabase();
        Cursor cursor = database.rawQuery("PRAGMA auto_vacuum"null);

        cursor.moveToFirst();
        Log.d("tag""auto_vacuum = " + cursor.getString(0));


で、実行結果は2.3/4.2の両方とも
05-31 23:44:22.833: D/tag(19169): auto_vacuum = 1

auto_vacuum は0:NONE 1:FULL 2:INCREMENTALらしいです。そのため、自動的にVacuumされます。よってVacuumする必要はまったくありません。。。

参考情報はこの辺
http://tools.oesf.biz/android-2.3_r1.0/xref/external/sqlite/dist/Android.mk
http://tools.oesf.biz/android-2.3_r1.0/xref/external/sqlite/dist/sqlite3.c

2013年5月28日火曜日

[Java]文字列の16進数変換&復元

ソケットでバイナリ通信する場合や、バイナリ形式のファイルを操作するときがあります。 で、その処理がうまくいっているかをダンプしたい時って結構あると思います。
そんな時、.NETであれば「[.NET]文字列の16進数変換&復元」でConvertというクラスで簡単に操作できます。で、Javaでそんなクラスってないかなぁ?って調べてみたらありました。

バイト列と文字列の相互変換って結構あるので、こういうのがあると助かるんですよね。Java1.6から利用可能なので、大半の仕事で適用可能ですね。
コードはこんな感じ。


public static void main(String[] args) {

    // 文字列からUTF8のバイト列に変換
    byte[] data = "1192つくろう鎌倉幕府!!".getBytes();

    // 001122形式の文字列に変換
    String hexText = DatatypeConverter.printHexBinary(data);
    System.out.println("16進文字列 [" + hexText + "]");

    // UTF8のバイト列からstringに変換
    String decText = new String(DatatypeConverter.parseHexBinary(hexText));
    System.out.println("復元結果 [" + decText + "]");
}

■実行結果
16進文字列 [31313932E381A4E3818FE3828DE38186E98E8CE58089E5B995E5BA9CEFBC81EFBC81]
復元結果 [1192つくろう鎌倉幕府!!]

・・・しかし、AndroidのSDKには含まれません。。ということで参考になるソースはこちら。 DatatypeConverterImpl

[Java]ストリームを転送する

ファイルからファイルであれば、[Java]ファイルを簡単にコピーするでOKですが、適当な入力ストリームから出力ストリームに出力したい。みたいな時があります。
たとえば、特定のURLからファイルをダウンロードしてファイルに保存したいみたいなケースですね。単純なのでその場で書いてしまうケースが多いですが、それをクラス化してみました。

// try-with-resourcesをサポートするためにjava.io.Closeableをインプリメント
public class StreamTransmitter implements Closeable {

    final private InputStream input;
    final private OutputStream output;
    final int bufSize;

    public StreamTransmitter(InputStream is, OutputStream os) {
        input = is;
        output = os;
        bufSize = 2048;
    }

    public void transfer() throws IOException {
        byte[] buf = new byte[bufSize];

        int read;
        while ((read = input.read(buf)) != -1) {
            output.write(buf, 0, read);
        }
        output.flush();
    }

    @Override
    public void close() throws IOException {

        // 内包するStreamもCloseableなのでtry-with-resourcesでOK
        try (InputStream in = input; OutputStream out = output;) {
        }
    }
}

このクラスを利用した例。
public static void main(String[] args) {

    String in = "/temp/LICENSE.txt";
    String out = "/temp/LICENSE_OUT.txt";

    try (StreamTransmitter st = new StreamTransmitter(new FileInputStream(
            in), new FileOutputStream(out))) {

        st.transfer();

        // try-with-resourcesなので、明示しなくてもOK
        st.close();
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }

}

比較的簡単ですね。

2013年5月21日火曜日

[Java]一定時間経過してから逐次処理を実行する(Queue)

処理をキューイングして、逐次処理をする。よくあるパターンです。一般的にこういう処理を実装するのにはQueue<E>を使うと思います。
では、キューに入れるけど、一定時間経過してからしか処理したい。または、連打された場合、最後の処理だけ実行したい。みたいなことがあると思います。
こういう時に、BlockingQueue<E>を使います。BlockingQueueはインタフェースです。そのため、その実装であるjava.util.concurrent.DelayQueue<E>を使います。

でも、addできるのは、java.util.concurrent.Delayedをインプリメントしたクラスだけ。

実装した例は以下のような感じ。


    public static void main(String[] args) throws InterruptedException {

        long[] waits = { 1086012 };
        String[] tests = { "10""8""6""0""1""2" };

        // 遅延時間のばらばらのデータをqueueに入れる
        BlockingQueue<Task> queue = new DelayQueue<Task>();
        for (int i = 0; i < waits.length; i++) {
            queue.add(new Task(tests[i], waits[i]));
        }

        // 空になるまでループする。
        while (!queue.isEmpty()) {
            Task task = queue.take();
            task.execute();
        }

    }

    /**
     * java.util.concurrent.Delayedを実装した例.
     * 
     */

    static class Task implements Delayed {

        final private long execTime;
        final private String text;

        /**
         * テキストと遅延時間を指定したコンストラクタ.
         * 
         * @param text
         * @param wait
         */

        Task(String text, long wait) {
            this.text = text;
            this.execTime = System.nanoTime() + TimeUnit.SECONDS.toNanos(wait);
        }

        /*
         * (non-Javadoc)
         * 
         * @see java.lang.Comparable#compareTo(java.lang.Object)
         */

        @Override
        public int compareTo(Delayed o) {
            long diff = execTime - ((Task) o).execTime;
            if (diff == 0) {
                return 0;
            }

            return (diff > 0) ? 1 : -1;
        }

        /*
         * (non-Javadoc)
         * 
         * @see
         * java.util.concurrent.Delayed#getDelay(java.util.concurrent.TimeUnit)
         */

        @Override
        public long getDelay(TimeUnit unit) {
            // 残遅延時間を返却
            System.out.println(String.format("%tT getDelay text=[%s] w=[%d]",
                    new Date(), text, execTime - System.nanoTime()));
            return execTime - System.nanoTime();
        }

        /**
         * 処理.
         */

        public void execute() {
            System.out.println(String.format("%tT execute  text=[%s]秒後",
                    new Date(), text));
        }

    }

■実行結果
00:51:24 getDelay text=[0] w=[-910031]
00:51:24 execute text=[0]秒後
00:51:24 getDelay text=[1] w=[892204037]
00:51:25 getDelay text=[1] w=[-1095530]
00:51:25 execute text=[1]秒後
00:51:25 getDelay text=[2] w=[998366343]
00:51:26 getDelay text=[2] w=[-1155035]
00:51:26 execute text=[2]秒後
00:51:26 getDelay text=[6] w=[3995886907]
00:51:30 getDelay text=[6] w=[-13674573]
00:51:30 execute text=[6]秒後
00:51:30 getDelay text=[8] w=[1985765928]
00:51:32 getDelay text=[8] w=[-11499437]
00:51:32 execute text=[8]秒後
00:51:32 getDelay text=[10] w=[1986226602]
00:51:34 getDelay text=[10] w=[-11083182]
00:51:34 execute text=[10]秒後

Queueに登録する順は関係ありませんが、キューの処理順は、compareTo()メソッドの戻り順で処理されます。そのため、遅延時間によって処理順を決めています。 「execute」とログが出ている行を追っかけていくと、指定した通りの時間で処理されていますね。 ログをそこだけに絞り込むと、以下のようになります。遅延指定した時間に実行されています。
00:51:24 execute text=[0]秒後
00:51:25 execute text=[1]秒後
00:51:26 execute text=[2]秒後
00:51:30 execute text=[6]秒後
00:51:32 execute text=[8]秒後
00:51:34 execute text=[10]秒後

2013年5月16日木曜日

[Java]Mapで指定キーに近い値を得る

SortedSet<E>は、指定の値の近似値を取得することができます。APIリファレンスを見ると、似たような名前のMapがあります。 それが、SortedMap<K,V>です。
名称からSortedSet<E>と似たようなことができるんじゃないか?と思ってリファレンスを見てみると、想定通りインタフェースがありました。 確認すると、NavigableMap<K,V>というインタフェースがあり、ここに定義されていました。関係するメソッドは以下の通り。 これらを式で書いてみました。

  • lowerEntry(K key) : K < key
  • floorEntry(K key) : K <= key
  • ceilingEntry(K key) : K >= key 
  • higherEntry(K key) : K > key 
これ以外にも、先頭や最後とるとか、便利そうなメソッドが提供されています。

指定の値以上のデータをループしてとる。みたいなことをやりたい場合はこんな感じ。

    public static void main(String[] args) {

        NavigableMap<Integer, String> map = new TreeMap<Integer, String>();
        for (int i = 1; i < 10; i++) {
            int key = (int) Math.pow(2, i);
            String value = String.format("key is %d", key);
            map.put(key, value);
        }
        // [2, 4, 8, 16, 32, 64, 128, 256, 512]

        int next = 50;
        Map.Entry<Integer, String> entry;
        while ((entry = map.higherEntry(next)) != null) {
            System.out.printf("key=%d value=%s\n", entry.getKey(), entry.getKey());
            next = entry.getKey();
        }
    }

key=64 value=64
key=128 value=128
key=256 value=256
key=512 value=512

2013年5月15日水曜日

[Java]更新できないSetをメンバ変数で初期化する

何かの照合用にメンバー変数にSetを持っておきたいことって時々あると思います。
でも引数付のコンストラクタがないので、インスタンスイニシャライザやスタティックイニシャライザで処理することは多いと思います。でも定義と宣言が分かれてしまうので、ちょっとわかりづらくなるケースがあります。

というのを組みわせで解決してみました。がこんな感じ。

// 更新できないSetをメンバ変数として生成する
private final Set<String> set = Collections.unmodifiableSet(
        new HashSet<String>(
                Arrays.asList(
                        "aaa",
                        "bbb",
                        "ccc",
                        "ddd"
                )
            )
        );

ていうか、素直にユーティリティ系のメソッドを準備すべきですよね。
    private final Set<Integer> set2 = newUmSet(
            1,
            2,
            3,
            4
    );

    @SafeVarargs
    static <E> Set<E> newUmSet(T... args){
        Set<E> set = new HashSet<E>(Arrays.asList(args));
        return Collections.unmodifiableSet(set);
    }

2013年5月12日日曜日

[Android]FragmentTransactionをロールバックする


そういえば。FragmentTransactionのrollbackってどうやってやるんだろう?
と思ってFragmentTransactionのリファレンスを見てみました。
きっとrollbackみたいなメソッドがあるんだろうなぁ。と思って眺めていても見つからない。
で、いろんなサイトを見渡してみると、
 ロールバック=FragmentTransaction#commitを呼ばない。
ということらしい。

「トランザクション=DB」みたいな感覚があるので、commitもrollbackもしないなんて、なんか気持ち悪いなぁと思って調べてみました。

一般的にFragmentの操作は以下のように行います。
  • FragmetnManarerを取得する
  • FragmetnManarer#beginTransactionでFragmentTransactionを取得する
  • FragmentTransaction#addなどで、Fragmentの操作を行う
  • FragmentTransaction#commitを呼ぶ→UIが切り替わる
です。

調べてみると、FragmentTransactionの実装クラスBackStackRecordでは、Fragmentの操作をリストで管理しており、実装は以下のようになっていました。
  • add/replase/deleteが呼ばれるたびに、内部のリンクリストへ情報を追加
  • commitでためていたリストをFragmentManagerへ渡す
  • FragmentManagerはHandler経由でUI処理
という感じでした。

一口にトランザクションって言っても実装方法はそれぞれですが、インタフェースとしての対称性としてどうなのか?と思ってしまいます。
  • FragmetnManarerでbeginTransactionするのに、commitはFragmentTransaction
  • FragmentTransactionにもFragmetnManarerにもrollbackがない
デザイン的に、Builderです。という風に言われるとなるほどそうかもしれない。と思えます(クラス名的にダメじゃないと思いますが)。
たとえばAlertDialog.Builderでいろいろ値を設定し、createやshowを呼ばなかったら。と同じですね。

2013年5月6日月曜日

[Java]コードから動的にinstanceofしたい

特定の型を継承または実装しているオブジェクトに対して、何等か処理をしたい場合、判定にinstanceofを使います。 しかし、instanceofはJavaの構文であるため、動的に判定することはできません。

何とかする方法ないかなぁと調べてみると、以下のような方法がありました。Class.isInstanceかisAssignableFromを使います。
public static void main(String[] args) {

    Object baseClass = new BaseClass();
    Object subClass = new SubClass();

    Object baseClassWithSubOthreType = new BaseClassWithSubOthreType();
    Object subClassWithSubOthreType = new SubClassWithSubOthreType();

    // instance of
    System.out.println("baseClass is Object=" + (baseClass instanceof Object));
    System.out.println("baseClass is BaseClass=" + (baseClass instanceof BaseClass));
    System.out.println("baseClass is SubClass=" + (baseClass instanceof SubClass));
    System.out.println("baseClass is BaseOthreType=" + (baseClass instanceof BaseOthreType));

    System.out.println("subClass is Object=" + (subClass instanceof Object));
    System.out.println("subClass is BaseClass=" + (subClass instanceof BaseClass));
    System.out.println("subClass is SubClass=" + (subClass instanceof SubClass));
    System.out.println("subClass is BaseOthreType=" + (subClass instanceof BaseOthreType));

    System.out.println("baseClassWithSubOthreType is Object=" + (baseClassWithSubOthreType instanceof Object));
    System.out.println("baseClassWithSubOthreType is BaseClass=" + (baseClassWithSubOthreType instanceof BaseClass));
    System.out.println("baseClassWithSubOthreType is SubClass=" + (baseClassWithSubOthreType instanceof SubClass));
    System.out.println("baseClassWithSubOthreType is BaseOthreType=" + (baseClassWithSubOthreType instanceof BaseOthreType));

    System.out.println("subClassWithSubOthreType is Object=" + (subClassWithSubOthreType instanceof Object));
    System.out.println("subClassWithSubOthreType is BaseClass=" + (subClassWithSubOthreType instanceof BaseClass));
    System.out.println("subClassWithSubOthreType is SubClass=" + (subClassWithSubOthreType instanceof SubClass));
    System.out.println("subClassWithSubOthreType is BaseOthreType=" + (subClassWithSubOthreType instanceof BaseOthreType));

    // isInstance
    System.out.println("-----------");
    // 比較対象のクラスが自クラスの型に対してい代入可能か判定する
    System.out.println("baseClass is Object=" + Object.class.isInstance(baseClass));
    System.out.println("baseClass is BaseClass=" + BaseClass.class.isInstance(baseClass));
    System.out.println("baseClass is SubClass=" + SubClass.class.isInstance(baseClass));
    System.out.println("baseClass is BaseOthreType=" + BaseOthreType.class.isInstance(baseClass));

    System.out.println("subClass is Object=" + Object.class.isInstance(subClass));
    System.out.println("subClass is BaseClass=" + BaseClass.class.isInstance(subClass));
    System.out.println("subClass is SubClass=" + SubClass.class.isInstance(subClass));
    System.out.println("subClass is BaseOthreType=" + BaseOthreType.class.isInstance(subClass));

    System.out.println("baseClassWithSubOthreType is Object=" + (Object.class.isInstance(baseClassWithSubOthreType)));
    System.out.println("baseClassWithSubOthreType is BaseClass=" + (BaseClass.class.isInstance(baseClassWithSubOthreType)));
    System.out.println("baseClassWithSubOthreType is SubClass=" + (SubClass.class.isInstance(baseClassWithSubOthreType)));
    System.out.println("baseClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isInstance(baseClassWithSubOthreType));

    System.out.println("subClassWithSubOthreType is Object=" + Object.class.isInstance(subClassWithSubOthreType));
    System.out.println("subClassWithSubOthreType is BaseClass=" + BaseClass.class.isInstance(subClassWithSubOthreType));
    System.out.println("subClassWithSubOthreType is SubClass=" + SubClass.class.isInstance(subClassWithSubOthreType));
    System.out.println("subClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isInstance(subClassWithSubOthreType));

    // isAssignableFrom
    System.out.println("-----------");
    // 比較対象のクラスが自クラスのサブクラスか判定する
    System.out.println("baseClass is Object=" + Object.class.isAssignableFrom(baseClass.getClass()));
    System.out.println("baseClass is BaseClass=" + BaseClass.class.isAssignableFrom(baseClass.getClass()));
    System.out.println("baseClass is SubClass=" + SubClass.class.isAssignableFrom(baseClass.getClass()));
    System.out.println("baseClass is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(baseClass.getClass()));

    System.out.println("subClass is Object=" + Object.class.isAssignableFrom(subClass.getClass()));
    System.out.println("subClass is BaseClass=" + BaseClass.class.isAssignableFrom(subClass.getClass()));
    System.out.println("subClass is SubClass=" + SubClass.class.isAssignableFrom(subClass.getClass()));
    System.out.println("subClass is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(subClass.getClass()));

    System.out.println("baseClassWithSubOthreType is Object=" + (Object.class.isAssignableFrom(baseClassWithSubOthreType.getClass())));
    System.out.println("baseClassWithSubOthreType is BaseClass=" + (BaseClass.class.isAssignableFrom(baseClassWithSubOthreType.getClass())));
    System.out.println("baseClassWithSubOthreType is SubClass=" + (SubClass.class.isAssignableFrom(baseClassWithSubOthreType.getClass())));
    System.out.println("baseClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(baseClassWithSubOthreType.getClass()));

    System.out.println("subClassWithSubOthreType is Object=" + Object.class.isAssignableFrom(subClassWithSubOthreType.getClass()));
    System.out.println("subClassWithSubOthreType is BaseClass=" + BaseClass.class.isAssignableFrom(subClassWithSubOthreType.getClass()));
    System.out.println("subClassWithSubOthreType is SubClass=" + SubClass.class.isAssignableFrom(subClassWithSubOthreType.getClass()));
    System.out.println("subClassWithSubOthreType is BaseOthreType=" + BaseOthreType.class.isAssignableFrom(subClassWithSubOthreType.getClass()));


}

// BaseClass
static class BaseClass {

}

// SubClass
static class SubClass extends BaseClass {

}

// BaseClass + SubOthreType
static class BaseClassWithSubOthreType implements SubOthreType {

}

// SubClass + SubOthreType
static class SubClassWithSubOthreType extends SubClass implements SubOthreType {

}

// BaseOthreType
interface BaseOthreType {

}

// SubOthreType
interface SubOthreType extends BaseOthreType {

}

■出力結果(どれも同じ結果が出ます)
baseClass is Object=true
baseClass is BaseClass=true
baseClass is SubClass=false
baseClass is BaseOthreType=false
subClass is Object=true
subClass is BaseClass=true
subClass is SubClass=true
subClass is BaseOthreType=false
baseClassWithSubOthreType is Object=true
baseClassWithSubOthreType is BaseClass=false
baseClassWithSubOthreType is SubClass=false
baseClassWithSubOthreType is BaseOthreType=true
subClassWithSubOthreType is Object=true
subClassWithSubOthreType is BaseClass=true
subClassWithSubOthreType is SubClass=true
subClassWithSubOthreType is BaseOthreType=true
-----------
baseClass is Object=true
baseClass is BaseClass=true
baseClass is SubClass=false
baseClass is BaseOthreType=false
subClass is Object=true
subClass is BaseClass=true
subClass is SubClass=true
subClass is BaseOthreType=false
baseClassWithSubOthreType is Object=true
baseClassWithSubOthreType is BaseClass=false
baseClassWithSubOthreType is SubClass=false
baseClassWithSubOthreType is BaseOthreType=true
subClassWithSubOthreType is Object=true
subClassWithSubOthreType is BaseClass=true
subClassWithSubOthreType is SubClass=true
subClassWithSubOthreType is BaseOthreType=true
-----------
baseClass is Object=true
baseClass is BaseClass=true
baseClass is SubClass=false
baseClass is BaseOthreType=false
subClass is Object=true
subClass is BaseClass=true
subClass is SubClass=true
subClass is BaseOthreType=false
baseClassWithSubOthreType is Object=true
baseClassWithSubOthreType is BaseClass=false
baseClassWithSubOthreType is SubClass=false
baseClassWithSubOthreType is BaseOthreType=true
subClassWithSubOthreType is Object=true
subClassWithSubOthreType is BaseClass=true
subClassWithSubOthreType is SubClass=true
subClassWithSubOthreType is BaseOthreType=true

・・・そもそもタイプ別で分岐しないといけないというのは設計が悪い。というケースが多いのかなぁと思います。 しかし、実装レベルで楽をするために。というのはありかなぁと思います。
たとえば、例外発生時のエラーメッセージの判定を一括して行う処理を実装する場合、これをMap<Exceptionのラッパー,String>で処理できるとずいぶんすっきりすると思います。

キーになる情報のhashcode()を工夫すれば、楽に実装できるような気がします。 instanceofだとif文たくさんになる図が目に浮かびますね。

しかし、この2つは、何が違うんでしょう?リファレンスを見ると、
  • isInstanceは、Java 言語の instanceof 演算子と動的に等価
  • isAssignableFromは、型として等しいか。プリミティブのタイプも判定できる
ということらしいです。
・・・じゃあ、相互互換ってどうなんだろう?ということでやってみました。コードと結果は以下の通り

    System.out.println("Integer isInstance int = " + int.class.isInstance(new Integer(1)));
    System.out.println("int isInstance Integer = " + Integer.class.isInstance(1));

    System.out.println("Integer isAssignableFrom int = " + int.class.isAssignableFrom(Integer.class));
    System.out.println("int isAssignableFrom Integer = " + Integer.class.isAssignableFrom(int.class));

--以下出力結果

Integer isInstance int = false
int isInstance Integer = true
Integer isAssignableFrom int = false
int isAssignableFrom Integer = false

Class.isInstanceでは、双方向でtrueを予想していたのですが、Integerとintは互換があり、逆は互換がない。っていう結果になりました。
Class.isAssignableFromに関しては、両方false。理由がよくわからない。。。

2013年5月5日日曜日

[java]メソッドで配列化する

javaでその辺にある引数やローカル変数で配列を作る場合に、new 型[]{arg1, arg2,arg4};とするのが一般的です。
その他の方法として、可変長引数を使ってメソッド化する方法があります。コードはこんな感じ。
public static void main(String[] args) {
// Stringの場合
String[] stringArray = Converter.convert("a""b""c");
// 数値はラッパー型になる。intで処理できないのが残念。。。
Integer[] intArray = Converter.convert(123);
// オーバーロードすればそれなりに

}

public static class Converter {
static <T> T[] convert(T... args) {
    // テンプレート型指定でキャスト不要
    // 引数をそのまま返すだけ!
    return args;
}
この方式で何かいいことあるか?といわれると。。。とくにありません。

[Java]C#のusingのような構文でリソースを開放する(try-with-resources)

時代はJava8に突入しようとしていますが、Java7の話題です。。。
C#のusingは、リソースを開放するのがすごく楽になる構文です。 コード的にはこんな感じ。
class Program
{
    static void Main(string[] args)
    {
        using (Hoge1 hoge1 = new Hoge1())
        {
            hoge1.execute();
        }
    }
}

class Hoge1 : IDisposable
{
    public void execute()
    {
        Console.WriteLine("Hoge1 execute");
    }

    public void Dispose()
    {
        Console.WriteLine("Hoge1 Dispose");
    }
}

Java(6)と比べて非常にすっきりしています。 そのJava6では、こんな感じで非常に残念。
public static void main(String[] args) {
    Hoge1 hoge = null;
    try {
        hoge = new Hoge1();
        hoge.execute();
    } finally {
        if (hoge != null) {
            try {
                hoge.close();
            } catch (IOException e) {
                // ほとんどのケースで、ここで例外が出てもどうしようもない。。。
                // 処理的には割り切りでしょうか。
            }
        }
    }
}

でもJava7のtry-with-resourcesだとこんな感じ。以下の例では、複数のAutoCloseableに対応した場合です。
public static void main(String[] args) {
    try (Hoge2 hoge2 = new Hoge2(); Hoge1 hoge1 = new Hoge1();) {
        hoge1.execute();
    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

}

static class Hoge1 implements AutoCloseable {

    public void execute() {
        System.out.println("Hoge1 execute");
    }

    @Override
    public void close() throws IOException {
        System.out.println("Hoge1 close");

        throw new IOException("Hoge1 close");
    }

}

static class Hoge2 implements AutoCloseable {

    public void execute() {
        System.out.println("Hoge2 execute");
    }

    @Override
    public void close() throws IOException {
        System.out.println("Hoge2 close");
    }
}

try構文の特徴も引き継ぐので、ブロック内にcatch文を書くこともできます。
(これ、地味ですが微妙にいい感じ)

C#のusingで複数のIDisposableに対応した書き方をすると、以下の通り。
    using (Hoge1 hoge1 = new Hoge1())
    using (Hoge2 hoge2 = new Hoge2())
    {
        hoge1.execute();
        hoge2.execute();
    }

当時、usingを複数書く構文には違和感を覚えましたが、すっかり慣れてしまいました。あと、import分に対応する文がusingであり、そこも微妙に違和感がありました。
Javaのtry-with-resourcesでは、1つのブロック中に記載するので、逆に違和感が。。。

2013年5月3日金曜日

[Android]設定アプリの「データを削除」を無効化する

Androidのアプリケーションでは、データベースやプレファレンスでデータを保存します。
 たとえば、SQLiteのデータベースの場合一般的には「/data/data/[アプリケーションパッケージ名]/databases」にファイルが生成されます。 しかし、このデータは、設定アプリで削除することができます。 それを抑止する方法として、その処理を無効化する方法がります。その方法とは以下の通りです。

 ■AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.your_app"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="15"
        android:targetSdkVersion="15" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:manageSpaceActivity="com.example.your_app.ManageActivity" >
        <activity
            android:name=".GridLayoutActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

</manifest>

「android:manageSpaceActivity」を指定することで、設定アプリがこのアクティビティ呼ぶようになります。 ボタンの表示も「データを消去」から「容量を管理」に表現が変わりました(端末によって差はあるかもしれません)。

そのため 、onCreate()でfinish()しちゃえば、ほぼ何も起こらない。ということが実現できます。

逆に
  • SDカードに管理しているデータもまとめて消す
  • プレファレンスだけ残してデータだけです
みたいかことも可能です。応用範囲はいろいろ。企業ユースでは結構ニーズがあるかもしれません。