「Tips」カテゴリーアーカイブ

ArrayAdapterがNullPointException

NullPointExceptionという例外でアプリが終了するのはよくあるバグです。ログを見ればどこで例外を起こしているか分かるのですが、今回は次のようにArrayAdapterの中で例外が起きていて困りました。

このNullPointExceptionの原因は、ArrayAdapterを作る時に渡したList<>の中にnullが入っていたことでした。

FATAL EXCEPTION: main
Process: jp.nosuz.secretkeyring, PID: 11911
java.lang.NullPointerException
    at android.widget.ArrayAdapter.createViewFromResource(ArrayAdapter.java:394)
    at android.widget.ArrayAdapter.getView(ArrayAdapter.java:362)
    at android.widget.AbsListView.obtainView(AbsListView.java:2263)
    at android.widget.ListView.makeAndAddView(ListView.java:1790)
    at android.widget.ListView.fillDown(ListView.java:691)
    at android.widget.ListView.fillFromTop(ListView.java:752)
    at android.widget.ListView.layoutChildren(ListView.java:1630)
    at android.widget.AbsListView.onLayout(AbsListView.java:2091)
    at android.view.View.layout(View.java:14948)
    at android.view.ViewGroup.layout(ViewGroup.java:4631)
    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    at android.view.View.layout(View.java:14948)
    at android.view.ViewGroup.layout(ViewGroup.java:4631)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    at android.view.View.layout(View.java:14948)
    at android.view.ViewGroup.layout(ViewGroup.java:4631)
    at android.support.v7.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:433)
    at android.view.View.layout(View.java:14948)
    at android.view.ViewGroup.layout(ViewGroup.java:4631)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    at android.view.View.layout(View.java:14948)
    at android.view.ViewGroup.layout(ViewGroup.java:4631)
    at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
    at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
    at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
    at android.view.View.layout(View.java:14948)
    at android.view.ViewGroup.layout(ViewGroup.java:4631)
    at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
    at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
    at android.view.View.layout(View.java:14948)
    at android.view.ViewGroup.layout(ViewGroup.java:4631)
    at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1991)
    at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1748)
    at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1004)
    at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5692)
    at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
    at android.view.Choreographer.doCallbacks(Choreographer.java:574)
    at android.view.Choreographer.doFrame(Choreographer.java:544)
    at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
    at android.os.Handler.handleCallback(Handler.java:733)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:212)
    at android.app.ActivityThread.main(ActivityThread.java:5151)
    at java.lang.reflect.Method.invokeNative(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:515)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:877)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:693)
    at dalvik.system.NativeStart.main(Native Method)

参照

Spinner: NullPointerException at ArrayAdapter.createViewFromResource –stackoverflow

Androidで高速なファイルコピー

Android(Java)でファイルコピーをするのには、単純にInputから読んで、それをそのままOutputに書き出す方法が単純です。しかしFileChannelを使用するとシンプルかつ高速にファイルをコピーすることができます。

ただしFileChannelを使用したコピーでは一度にコピーできるサイズが2GBに制限されるという仕様があるので、どこまでコピーしたか確認しながらコピーするように修正しました。

public static boolean copy(File src, File dst) {
        boolean result = false;
	
        FileInputStream inStream = null;
        FileOutputStream outStream = null;
        try {
            inStream = new FileInputStream(src);
            outStream = new FileOutputStream(dst);
            FileChannel inChannel = inStream.getChannel();
            FileChannel outChannel = outStream.getChannel();
            long pos = 0;
            while (pos < inChannel.size()) {
                pos += inChannel.transferTo(pos, inChannel.size(), outChannel);
            }
            result = true;
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (inStream != null) inStream.close();
                if (outStream != null) outStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        if ((! result) && dst.exists()) dst.delete();

        return result;
    }

純粋にJavaでのコピーならが、Files.copy()を使用するのが一番簡単です。

参照

How to make a copy of a file in android? – stackoverflow

AndroidのService#OnStartCommand()の返り値

Androidのサービスは、起動する時に呼ばれるメソッドOnStartCommandの返り値によって強制終了された時の振る舞いが変わります。

START_STICKY
強制終了されても自動的にサービスが再起動されます。再起動時にはOnStartIntentに渡されるintentがnullの可能性があります。
START_NOT_STICY
強制終了されても再起動はされない。
START_REDELIVER_INTENT
強制終了されても自動的にサービスが再起動されます。この時START_STICKYとは異なり最初に起動された時のintentが再起動する時にも渡されます。

START_STICKY or START_REDELIVER_INTENT

サービスが強制終了された時に自動的に再起動するための返り値が二種類あるけど、intentの有無の他に違いはないのでしょうか?

そこで簡単に実験してみたところ、START_STICKYとSTART_REDELIVER_INTENTではサービスが再起動されるまでの時間が異なるようです。具体的には、START_STICKYは強制終了してもすぐに再起動されますが、START_REDELIVER_INTENTは再起動されるまでに分単位で時間がかかりました。

返り値 再起動までの時間
START_STICKY すぐに再起動
START_REDELIVER_INTENT 再起動まで1分くらい

サービスが強制終了させられてもすぐに同じintentでサービスを開始したい場合は、intentの内容をSharedPreferenceやデータベースに書いておき、START_STICKYを指定してサービスを起動するのが良さそうです。そしてintentがnullでサービスが起動されたら保存しておいたintent内容を読んで再開するという方法を取ります。

参照

Service – Developers

UbuntuでCapsLockキーをControlキーに

LinuxでCapsLockとControlキーを入れ替えるには一般的にxmodmapコマンドを使用します。しかしUbuntuでCapsLockキーとControlを入れ替えるには、dconfコマンドで簡単に変更できます。

参照したページではUbuntu 14.04LTSが対象と書かれていましたが、Ubuntu 16.04LTSでも有効でした。

dconfでCapsLockキーもControlキーに

今回はControlキーが壊れたようで認識しなくなったので、交換ではなく次のようにしてCapsLockもControlキーとすして使えるようにしました。

$ dconf write /org/gnome/desktop/input-sources/xkb-options "['ctrl:nocaps']"

xmodmapでCapsLockキーもControlキーに

xmodmapコマンドを使用してCapsLockキーもControlキーと使用できるようにするには、例えば設定を記述するファイルとして~/.Xmodmapファイルを作成して、そこに次のように設定を書き込みます。

keycode  66 = Control_L Control_L Control_L Control_L
remove Lock = Control_L
add Control = Control_L

これをxmodmapコマンに読み込ませます。

$ xmodmap ~/.Xmodmap

この方法は、dconfと異なりログアウトすると設定を忘れてしまうので、.bash_profileにxmodmap ~/.Xmodmapを追加しておく必要があります。

参照

UbuntuTips/Desktop/HowToSetCapsLockAsCtrl – ubuntu Japanese Team Wiki

Android Studioの編集画面を二分割

別のファイルや編集中のファイルの別の箇所を参照しながら編集する時には、画面を分割して同時に見られるそうにすると便利です。

Android Studioで編集画面を分割するには、メニュータブからは、「Window→Editor Tabs→Spilit」と進みます。

標準のキーマップではこの操作にショートカットが設定されていませんが、Emacsに変更すると、次のようにEmacsでの画面分割キーシーケンスを使用できます。

縦に二分割(上下に分割)
Ctrl-x, 2
横に二分割(左右に分割)
Ctrl-x, 3
分割画面を結合
ファイル名タブの「X」をクリックするとタブが消え、全てのタブがなくなると画面が結合されます。またCtrl-x, 1では、全ての分割を一度に消して一つの画面にできます。

分割した画面の割合は、画面の境界をドラッグすることで自由に変更できます。

分割した画面で同じ操作を繰り返すと、さらに場面を分割することができます。

screenshot-from-2016-12-14-16-43-28

別のウィンドウへ

画面を分割ではなく別のウィンドウで開くことも可能です。

別のウィンドウで開くには、ファイル名のタブをつかんでAndroid Studioのウィンドウの外にドラッグします。

Android Studioでプロジェクトのパッケージ名を変更する方法

  1. package_name(app -> java -> package_name)の上で右クリックする。
  2. Refactor -> Renameを選択する。
  3. Rename packageボタンをクリックする。
  4. パッケージ名を聞かれるので、新しいパッケージ名を入力してRefactorボタンをクリックする。Search in comments and stringsとSearch for text occurencesをチェックしておく。
  5. Do Refactorボタンをクリックする。

参考にしたstackoverflowの回答では、build.grade(Module:app)のApplication IDを手で編集するようになっていましたが、新しいパッケージ名を入力するダイアログでSearch in comments and stringsとSearch for text occurencesをチェックしておいたからか、AndroidManifest.xmlとbuild.grade(Module:app)もいい具合に変更されました。

プロジェクトの名前を変更する

ちなみにプロジェクト名を変更したい時は、次のようにします。

  1. Android Studioで開いているプロジェクトを閉じる。
  2. プロジェクトのディレクトリー名を変更する。
  3. Android Studioで名前を変更したプロジェクトを開く。

参照