2011年12月22日木曜日

[Android]SDK16のLintつかってみた (その2)

最近会社のマシンでもADT16に入れ替えました。
今日作業中にふと気がついたんですが、Lintってリアルタイムにエラー表示するんですね。
ちょっとびっくりです。で、ADT15と互換性があるらしく、特に設定変更することなく、すんなり動作しちゃいました。ADT14の人は15にすることをお勧めします。

2011年12月15日木曜日

[Android]SDK16のLintつかってみた

とりあえず、ちょっと使ってみた。
1.レイアウトxml(これってlayoutopt?)
 ・リソース使っていないと起こられます
 ・パフォーマンス系の指摘をしてくれる
  Set android:baselineAligned="false" on this element for better performance
  みたいな
 ・ViewGroup系(レイアウト)のなかに1つしかViewが入ってないと教えてくれます
  
2.使ってないを教えてくれる
 ・リソース系(画像とかstringsとか)を教えてくれます。これすごく便利!

3.足りないを教えてくれる
 ・足りないアイコンを教えてくれる(mdpi入れてなかったらばれた)

[Android]ソースの添付

Android SDK 16では、SDK Managerで、SDKのソースがダウンロードできるようになっています。
ダウンロード対象に、ソースを含めると、
 [SDK dir]/sources/android-xx
 (xxはAPIレベル)
にソースがダウンロードされます。ダウンロードできるのはandroid4.0(ICS)のみのようです。

eclipseでプロジェクトに設定された
 Google APIs [Android x.x]
  android.jar
に「ソースの添付」でこのフォルダ指すと、IDEから直接参照できるようになります。
べんりー。

[Android]ADT16?

Android4.0(ICS)がリリースされてから、まだそれほどたっていませんが、今日またADT(SDK)がバージョンアップした模様。以下、変更点。
 ・Lint 導入された。
 ・エミュレータでセンサーが使えるようになった
 ・Macでカメラサポート
みたいです。

Lintはあの、C言語とかで有名なLintなら、コンパイラーより厳密なチェックをしてくれることになるので、導入する価値がありそう。

2011年11月9日水曜日

[Android]マーケットにアプリを公開しました

アイコンがなかなか作れず(コレに一番時間がかっかった)公開に手間取っていました、アプリをAndroidマーケットに公開しました。

 WifiNotify

このアプリは、

  • Wifi接続したら通知する
  • 接続した位置を記録する
  • 記録した位置を、地図とリストで閲覧できる

だったんですが、今は、

  • WiFiのOn/Off
  • テザリングのOn/Off
ができるようになっています。
公衆無線LAN(ドコモのmoperaなど)を契約してるけど、アクセスポイントがよくわからないので、つながったら使ってやってもいいと思っている人は利用価値があるかも。

不具合や要望などあれば気軽にご連絡ください。

2011年10月21日金曜日

[Android]ICS(Android4.0)が出てきた(その3) レイアウトファイルのフォーマット

何個まで続くかわかりませんが、とりあえずその3。です。
レイアウトエディタのフォーマットについて。

今までもソース(xml)フォーマット(整形のほうね)機能がありましたが、今度のフォーマッターはすげー。見やすい上に、アトリビュートの順序まで並び替えてくれる。
整形するとチョー気持ちいい!。・・・ctrl+fを連打したくなります。

2011年10月20日木曜日

[Android]ICS(Android4.0)が出てきた(その2) エミュレータのカメラサポート

何個まで続くかわかりませんが、とりあえずその2。です。
エミュレータのカメラについて。

今までのエミュレータのカメラは、フェイクカメラ(シマシマ模様のアレ)が動いていましたが、今日、ICSでエミュレータを動作させたところ、標準搭載されているカメラのLEDが光るじゃないですが!
で、カメラアプリを動かしてみると、なんとエミューれたでカメラが動作しました。
ちなみに、マシンはDELLのXPS1530という、一世代前のマシンです。

[Android]ICS(Android4.0)が出てきた(その1) ADT14のライブラリプロジェクト

何個まで続くかわかりませんが、とりあえずその1。です。
ライブラリプロジェクトについて。
今まで、何度かこのblogに書きましたが、ライブラリプロジェクトについて。

今でのADT(SDKなのかもしれませんが...)では、ソースやリソースを直接取り込むことで、参照関係を作っていましたが、今日、ADT14にUPしてみると、ライブラリプロジェクトには、本当にjarが作成され、参照しているプロジェクトに、そのjarが自動的に取り込まれるようになりました。本当の意味でライブラリ用プロジェクトになったみたいです。

とりあえず、ADT12以前でライブラリプロジェクトを使っていたworkspaceをADT14環境で開くときは、いったんライブラリプロジェクトの参照を解除してから付け直すことで、最新化できます。

2011年10月8日土曜日

[Android]fill_parentとmatch_parent

AndroidのUIをXMLでレイアウトするときに、Widgetsの幅や高さを最大で取れるという指定をするとき、"fill_parent"と書いていました。
いまさらながらですが、Android2.2(Froyo)からコレが"match_parent"というようになったようです。今はどちらを指定してもよいようですが、そういわれると"match_parent"にしたくなっちゃいますね。

2011年9月3日土曜日

[Android]WebViewでHTML5のgeolocationをつかう

Androidのセンサーを使ってもいいんですが、HTMLとWebViewとgoogleMapで地図表示みたいなことをしたときの話。
まぁ、↓を使ってみたんですが、これがうまくいかない。
navigator.geolocation.getCurrentPosition(showPosition, handleError);

で調べてみると、WebChromeClient#onGeolocationPermissionsShowPromptをオーバーライドしてコールバックで結果を返す必要があるということがわかりました。
でも、プロンプト出さずにcallback.invoke(origin, true, false);を呼べば、それらしく動作する。
これはコンテンツをつくるがわのモラルの話?になるんだろうか。コードはこんなかんじ。

webView.setWebChromeClient(new WebChromeClient(){
    public void onGeolocationPermissionsShowPrompt(final String origin, final Callback callback) {
        super.onGeolocationPermissionsShowPrompt(origin, callback);
        //プロンプトなして、Geolocationを有効にする
        SCAlertDialog.SCBuilder builder = new SCAlertDialog.SCBuilder(SharedInfo.getInstance().getContext());
        builder.setMessage("じおろける?");

        builder.setPositiveButton(
            R.string.OK,
            new DialogInterface.OnClickListener(){
                public void onClick(DialogInterface dialog, int whichButton){
                    //finish()はonPause()にいろいろと手を入れる必要があるので・・・
                    callback.invoke(origin, truefalse);
                }
            }
        );
        builder.setNegativeButton(R.string.Cancel, null);

        AlertDialog dialog = builder.create();
        Window window = dialog.getWindow();
        WindowManager.LayoutParams lp = window.getAttributes();
        lp.token = SekaiApps.sharedApplication().getWindow().getAttributes().token;
        window.setAttributes(lp);

        // ダイアログを表示
        dialog.show();
    }

});

WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);    // javascriptを有効にする

2011年8月17日水曜日

[Android]JavaScript⇔Android(Java)の連携

時々、HTML(JavaScript)とAndoroidを連携したいときがあります。そんなときは、WebViewを使えば、簡単に連携することができます。連携方法は以下の通り。

1.JavaScript→Android
 AndroidのWebViewには、addJavascriptInterfaceというメソッドがあり、ここに登録することで、Javaのメソッドを呼び出すことができます。うーん簡単。
 しかし引数に渡せるのは、比較的プリミティブなものだけ。というルールがある(配列とか結構ダメ)。
 とはいえ、引数も戻り値も指定できる。

2.Android→JavaScript
 WebView#loadUrlメソッドを使って、JavaScriptを呼び出す。
webView.loadUrl("javascript:testFunction('abc');");
 コレで、testFunctionというスクリプトを引数'abc'で呼び出したことになる。
 しかし、戻り値をもらうインタフェースがない...。

3.JavaScriptからどうやって戻り値をもらう?
 手っ取り早いのはコールバック形式。
 事前準備として、WebView#addJavascriptInterfaceで、コールバック(値を保持する変数のセッターとか)を登録
 webView.loadUrlでJavaScriptを呼び出すこの時、addJavascriptInterfaceで登録したメソッドを呼び出す
 メソッド呼び出しが終わったら、addJavascriptInterfaceで登録したインタフェースから値を取り出す
 登録したクラスより値を取り出すことで、結果が取得できる。

 ・・・結果の受け取りだけですが...エレガントじゃないですが、いたし方がない...。

2011年8月13日土曜日

[Android]WebViewでWebViewClientを指定する

WebViewはWebViewClientをセットしないとブラウザを起動するんです。
いままでWebViewClientを指定しなかったことがなかったので、知りませんでした...。

2011年8月11日木曜日

[Android]USBドライバ(その3)

GDD Blog: [Android]USBドライバ(その2)で、簡単に接続させる方法を調べましたが、結局見つかりませんでした。その代わり、デバイスIDを調べる方法がありました!

デバイスマネージャ→プロパティ→詳細タブを表示し、プルダウンから「ハードウエアID」を選択し、android_winusb.infにそのIDを書くだけ。コレならいちいち調べなくてもいいので楽チン。

NexusOneの場合、
%CompositeAdbInterface% = USB_Install, USB\VID_18D1&PID_4E12&MI_01

となっており、この右辺にハードウエアIDで出てくる長いほうを書けばOKっぽい。

2011年8月8日月曜日

[Android]デバッグが開始できない(デバイスを認識しない)

AndroidSDKとADTって、結構頻繁にアップデートがあります。
いつもはeclipseの環境をコピーしてからアップデートしていたんですが、今日は時間がなくて、とりあえずアップデートしてみました。IDEはいい感じで動いていたんですが、デバック開始時にこんなメッセージが!

Automatic Target Mode: Unable to detect device compatibility. Please select a target device.

そして「Android SDK及びADVマネージャを開く」ボタンを押しても空っぽの画面が。。。
eclipseやマシンを再起動しても、状況は変わらず。

そうだ。アレだ!とEclipseを-cleanオプション付きで起動したら直りました。
いつもコレを忘れちゃうんですよね。

2011年7月20日水曜日

[他]javaのパッケージ名登録サービスを利用する

androidアプリケーションでは、パッケージ名を使って、アプリケーションをユニークにするという仕組みがあります。しかしjavaの実装上、適当にパッケージ名をつけることができます。
また、企業であればドメインを使うのが一般的ですが、じゃあ個人は?といったとき、ちょっと困ってしまいます。で、調べてみると「パッケージ名登録」というサービスが...。しかも無料で利用できます。
サービスの提供元は、財団法人インターネット協会です。公的な団体だと思いますので、個人でアプリ開発してマーケットに登録するような人は1つ持っておくといいかもしれません。

2011年7月19日火曜日

[Android]ScrollViewとHorizontalScrollView

サイズの異なるデバイスの動作確認をしたいとき、小さな画面のデバイスを使うと、画面仕切れません。そこで、縦横にスクロールさせるためにScrollViewとHorizontalScrollViewを組みあわせてコンテンツをスクロールさせることがありました。

しかし、微妙なタッチを認識するのか、たてにスクロールさせようとしているのに横向きのスクロールがヒットしたりして、イライラします。

で、対面に座っている人に、「いいのがアルヨ」と教えてもらったのが、この2DScrollView。コレコレ。こういうの探していたのよ。
クラスも1つだし、Apache License2.0って書いてあるので、比較的使いやすいかも。

2011年7月5日火曜日

[Android]スキーマ起動に対応する

ブラウザのハイパーリンクで何かアクションしたい場合に使えます。
amコマンドでINTENT投げても起動します。えぇまぁ、 AndroidMAnifest.xmlにかくだけですよ。

<!-- xxx://com.foo.bar/appname で起動可能になる -->
<intent-filter>
    <action android:name="android.intent.action.VIEW"/>
    <category android:name="android.intent.category.DEFAULT"/>
    <category android:name="android.intent.category.BROWSABLE"/>
    <data android:scheme="xxx" android:host="com.foo.bar" android:path="/appname"/>
</intent-filter>

2011年7月4日月曜日

[Android]AppWidgetからActivityを呼び出したらエラーが...

先日、AppWidgetsを作成していて、ボタンを押下したらActivityを起動するために、ボタン押下でブロードキャストし、AppWidgetProvider#onReceiveで、Context#startActivityしてみると、こんなエラーが...。

java.lang.RuntimeException: Unable to start receiver com.dustroid.dust04.CountDownAppWidget: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

で、調べてみると、Intent#setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)っていう風にする必要があるらしいことがわかった。っていうかメッセージをよく見ればわかるよね。
...英語読めるようになりたいな。

2011年6月21日火曜日

[Android]GPS関係のデバッグについて

Androidのエミュレータでは、DDMS経由で位置情報を変更することができます。が、エミュレータって遅いんですよね。実記で位置情報を変更する方法する方法として、擬似ロケーションを使う方法があります。

擬似ロケーションを使うには、以下の2つの手順があります。


  1. 設定→アプリケーション→開発の「擬似ロケーションを許可」をチェック
  2. 設定ソフトを使って設定(Location Spooferなど)


意外と便利。

2011年6月9日木曜日

[Android]入力がめんどくさい

androidでの入力はとにかくめんどくさい。特にwifiのWEPキーの入力等、あの入力をしなければいけないと考えるだけで、ハラがたってくるというか、やる気がうせてしまいます。
そんな私と似たタイプの方は、adb shellのinputコマンドがお勧め、入力はこんなかんじ。

C:\Users\genzo>adb shell
# input text abcdefg


ただ、残念なことに、漢字と一部のコード($など)の入力ができません。IMEが起動中の場合は、そのまま入力したことになります。

たしか、PCのブラウザを使って入力を転送するアプリがAndoroidマーケットにがあった気が...。

2011年6月3日金曜日

[Android]WebViewが見つからない!

先日、androidでlayoutファイルを設定していました。
上半分がWebViewで下半分にボタンと画像を表示したような画面です。layoutの定義も終わり、ためしに動作させてみたところ、例外発生。なんだろ?と思って確認すると、WebViewがみつからない。的なメッセージがLogcatに...。メッセージはこんなかんじ。

java.lang.ClassNotFoundException: android.view.WebVeiw in loader dalvik.system.PathClassLoader

は?WebViewが見つからない。。。ってよく見るとWebV*ei*wになってんじゃなぁーい。
ってことで単純なスペルミスでした。
XMLの編集でエラーって出て多様な気がするんですが。。。気のせいでしたっけ?

2011年5月25日水曜日

[Android]いきなりデバッガが動作しなくなった!

まぁ結果からいえば、証明書の有効期限切れなんですが、前日まで動作していたデバッガが急に動作しなくなりました。メッセージはこんな感じ。

Debug certificate expired on Error generating final archive: Debug Certificate expired on 11/5/21 0:28 


・・・デバッグ用の証明書の有効期限がデフォルトで1年。。。注意しなきゃね。

2011年5月17日火曜日

[Java]文字列 or Readerから構文解析っぽいことをする

いまどきはxmlやjsonを使った構文解析をしますが、先日URLのクエリーをURLデコードせずに取りひつようがあり、メンドクセーなーと思いながら、URLEncodedUtils.parseのソースを見ていると、java.util.Scannerというクラスを発見しました。このクラス、文字列をトークンにごとに取り出す機能があり、それを流用しました。ちなみにトークンには正規表現が使えますので、大半のことはできちゃうきがします。
ちなみに文字列の解析だけではなく、int型やdoubleなどいろんなタイプで値を取り出すことができます。

コードはこんな感じ。
try {
    URI uri = new URI("http://localhost/hoge?foo1=foo1&bar1=bar1&nihonngo=%e6%97%a5%e6%9c%ac%e8%aa%9e%e3%82%82Ok");
    String query = uri.getRawQuery();
    List<NameValuePair> result = new ArrayList<NameValuePair>();
    Scanner scanner = new Scanner(query);
    scanner.useDelimiter("&");
    while (scanner.hasNext()) {
        final String[] nameValue = scanner.next().split("=");
        if (nameValue.length == 0 || nameValue.length > 2){

        }else{
            String name = nameValue[0];
            String value = nameValue[1];
            result.add(new BasicNameValuePair(name, value));
        }
    }

    Log.d("", result.toString());
    //[foo1=foo1, bar1=bar1, nihonngo=%e6%97%a5%e6%9c%ac%e8%aa%9e%e3%82%82Ok]って出る

catch (URISyntaxException e) {
}

2011年4月26日火曜日

[Android]URLのQueryを配列に変換

AndroidというよりApacheの話になるんですが、めんどくさいURLのクエリーを配列に落としてくれるクラスがあります。知らないころガリガリ書いてたなぁ。コードはこんな感じ。

try {
    URI uri = new URI("http://localhost/hoge?foo1=foo1&bar1=bar1&nihonngo=%e6%97%a5%e6%9c%ac%e8%aa%9e%e3%82%82Ok");
    List<NameValuePair> list = URLEncodedUtils.parse(uri, "UTF-8");
    Log.d("test",list.toString());
    //[foo1=foo1, bar1=bar1, nihonngo=日本語もOk]って表示される
catch (URISyntaxException e) {
}

2011年4月18日月曜日

[Android]Androidはオープンソース(その2)

GDD Blog: [Android]Androidはオープンソースでも紹介しました、ソースの検索サイトですが、ほかにも検索できるサイトがありましたので、紹介します。

サイトはhttp://grepcode.com/です。

このサイトは、Android以外のコードも検索できます。また、eclipseのプラグインもあります。
ちょっと使ってみましたが、ソースがない場合にボタンが表示され、コードが表示されます。
結構便利ですよ。

2011年4月16日土曜日

[Android]Androidはオープンソース

Androidはオープンソースです。オープンソースですが、いつも環境がそろっているとは限りません。
そんな時はここ。ソースの検索サイトを無料で公開してくれています。2.2系2.3系があるみたいです。

2011年4月8日金曜日

[Java]グローバル例外ハンドラ

.NETではグローバル例外ハンドラというのがあり、処理されなかった例外を一手に処理するハンドラがあります。で、Javaは?と思って調べてみると、あるんですね。
この手の機能は一般的なものなんですかねぇ。java 1.5からの機能みたいですね。
しかしこのメソッドはセッターであるため、1つしか設定できないんです。というわけで、複数でハンドリングする場合は、注意しないと上書きされちゃいますね。

サンプルコードは以下のとおり。

import java.lang.Thread.UncaughtExceptionHandler;
class TestApp {
    public static void main(String[] args) {
        //スタティックメソッドのほうは、どこで例外が発生しても情報が取得できる!
        Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
            public void uncaughtException(Thread t, Throwable e) {
                System.out.println(e.getMessage());
            }
        });
        new TestApp().exception();
    }
    public void exception(){
        throw new RuntimeException("テストだよーん");
    }
}

2011年4月4日月曜日

[Android]NativeActivtyをちょっと調べてみた

Android2.3(Gingerbread)に搭載された機能の1つで、NativeActivtyっていうのがある。
名前から察するところ、Native(c/c++)のみで実装できるActivityか?と思ってたんですが、どうも違うことがわかりました。よくよく調べてみると、NativeActivity自体はJavaで実装されており、決まった形の関数を実装する形で連携するようです。

簡単にまとめると以下のとおり。

  • NativeActivityはJavaで実装されている
  • NativeActivityはJNIを使う
  • NativeActivityは主要なライフサイクルのコールバック時に、対応するNativeメソッドを呼ぶ
  • JNIの部分は雛形のテンプレートがあり、それを実装する(っぽい)
  • Nativeでの画面描画はOpenGLを使うのが一般的
  • Nativeからセンサーを使えるようになった

こんな感じでしょうか。で、NativeActivityをextendsして、通常のActivityのようなことができるかを試してみました。


  • Nativeが描画している画面の上にWidget配置→NGでした(やり方が悪いだけかも...)
  • メニューを出す→OKでした
  • ダイアログを出す→OKでした
  • Native側からカメラを制御→方法が見つからず
  • Native側からGPSを使う→方法が見つからず

ということで、OpenGLで画面を描画している上にViewを表示することはできなさそうです。。。
誰か情報持ってたら教えてください!

2011年3月23日水曜日

[Java]戻り値が異なる引数の同じオーバーロード

Javaでは、メソッドのオーバーロードという機能があり、引数の型または数の異なる同じ名前のメソッドを定義することができます。
しかし、引数が同じで、戻り値が異なる。というメソッドの定義はできません。と思っていたんですが、スタティックなメソッドの場合のみ、そうじゃないということを最近知りました。
人の書いたコードレビューって、こういう発見があって楽しいですね!。で、コード例は以下のとおり。

class TestApp {

    public static void main(String[] args) {
        System.out.println("TEST1=" + BaseClass.getInstance());
        System.out.println("TEST2=" + SubClass.getInstance());
    }

    static class BaseClass{
        private static BaseClass instance1 = new BaseClass();
        public static BaseClass getInstance(){return instance1;}
        public String toString() {
            return getClass().getName();
        }
    }

    static class SubClass extends BaseClass{
        private static SubClass instance2 = new SubClass();
        //staticの場合は、引数違いのオーバーロードが可能!
        public static SubClass getInstance(){return instance2;}
        public String toString() {
            return getClass().getName();
        }
    }
}


■結果
TEST1=TestApp$BaseClass
TEST2=TestApp$SubClass

2011年3月18日金曜日

[Android]USBドライバ(その2)

GDD Blog: [Android]USBドライバで備忘を書きましたが、なんとベンダIDを書いておけば、デバッグドライバすべてOK的なことがUsing Hardware Devicesに書かれています。
でも、Windowsのばあい、どこにファイルを置けばいいのかよくわかりません。でgoogleで調べてみるとWindowsでは、
 /ユーザフォルダ/.android/adb_usb.ini
に書いておけばよい的なことが書かれていたのでやってみました。がうまくいかず。
よくよく↑のサイトをよく見ると、「If you're developing on Windows, you need to install a USB driver for adb.」と書かれています、直訳すると「Windows上で開発している場合は、ADBのUSBドライバをインストールする必要があります。」いやー直訳でもはっきりわかりますねぇ。
ということで、OEM USB DriversにOEMのドライバ一覧があったので、備忘代わりにメモ。

2011年3月14日月曜日

[Java]匿名クラスにfinal変数が展開されると

ローカル変数にfinalをつけると、内部で生成している匿名クラスに展開されます。
で、どのように展開されるかというと、匿名クラスのコンストラクターの引数になり、自動生成される同名のメンバ変数に展開されるようです。
とは書いたものの、実際にどうなるかを、classファイルからリバースしてみました。

■変換前のソース
final int hoge1 = 1;
final int hoge2 = 2;
final URL url =new URL("http://localhost/");

btn2.addActionListener(new ActionListener() {
    int hogehoge = 0;
    public void actionPerformed(ActionEvent evt) {
        this.hogehoge = hoge1 + hoge2;
        System.out.print("hogehoge=" + hogehoge);
        System.out.print("hoge1=" + hoge1);

        System.out.print("url=" + url);

        exec();
    }

});


■変換後のソース(一部修正)
/*  51*/        int hoge1 = 1;
/*  52*/        int hoge2 = 2;
/*  53*/        final URL url = new URL("http://localhost/");

/*  55*/        btn2.addActionListener(new ActionListener() {
                    public void actionPerformed(ActionEvent evt)
                    {
/*  58*/                hogehoge = 3;
/*  59*/                System.out.print((new StringBuilder("hogehoge=")).append(hogehoge).toString());
/*  60*/                System.out.print("hoge1=1");

/*  62*/                System.out.print((new StringBuilder("url=")).append(url).toString());

/*  64*/                exec();
                    }
                    int hogehoge;
                    final RefrectionPanel this$0;
                    private final URL val$url;
                    
                    {
/* <-MISALIGNED-> */ /*   1*/                this$0 = RefrectionPanel.this;
/* <-MISALIGNED-> */ /*   1*/                url = url1;
/* <-MISALIGNED-> */ /*  55*/                super();
/* <-MISALIGNED-> */ /*  56*/                hogehoge = 0;
                    }
        }


・・・なんとなく雰囲気はわかるんですが、そのままだとコンパイルエラーです。しかし、コンストラクタでコピー(しようとしている)感じはわかりますね。面白いのは、int型(おそらくプリミティブ)については、値に展開されるところでしょうか。興味深いです。
あ、コレでちょっとしたリバースエンジニアリング対策にもなりますね。

2011年3月8日火曜日

[Java]VMの終了をハンドリングする

たとえばログファイルのクローズなど、システムの終了時に何かをやりたいということが時々あります。
そんなときにに、Runtime#addShutdownHookを使います。引数にはスレッドを渡します。コードはこんな感じ。

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
    public void run() {
        //システム終了時によびだされる
    }
}));

2011年2月27日日曜日

[Java]プライベートクラスをインスタンス化する

そもそもユニットテストを前提とした設計をしていないのが問題なんですが、ユニットテストで、特定のクラスの内部クラスを実行しなければいけない場合に遭遇しました。
メンバ変数に保持していれば、リフレクションで取り出すことができるので、実行も比較的楽なんですが、コレがメソッド内のみで利用されている場合、この内部クラスを外部からインスタンス化する必要があります。
ちょっと面倒ですが、外部からインスタンス化する方法を紹介します。

■インスタンス化するコード
try {
    TargetClass target = new TargetClass();

    //配列の0番目は決めうち
    Class<?> clss = TargetClass.class.getDeclaredClasses()[0];

    //コンストラクタを取り出す。引数はそのアウタークラスになる
    Constructor<?> cons = clss.getDeclaredConstructor(new Class[]{TargetClass.class});

    //コンストラクタを使ってインスタンスを生成する
    cons.setAccessible(true);
    Object inner = cons.newInstance(new Object[]{target});

    //メソッド実行
    System.out.println(inner.toString());

catch (SecurityException e) {
catch (NoSuchMethodException e) {
catch (IllegalArgumentException e) {
catch (InstantiationException e) {
catch (IllegalAccessException e) {
catch (InvocationTargetException e) {
}

第一引数が該当のクラスのアウタークラスになるのがルール。

■ターゲットのクラス(例)

public class TargetClass {

    public void doExec(){

        new TargetInnerClass().subExec();
    }

    //このクラスの単体テストが難しい...
    private class TargetInnerClass{
        public void subExec(){

        }
    }
}

2011年2月20日日曜日

[Android]ADBからintentを送信する

ADBのshellからintentを発行する方法としてamコマンドがあります。以下のような感じ。
結構便利です。

■電話をかける
am start -a android.intent.action.CALL tel:0521112222


■Androidまかせ(ブラウザ起動と地図→スキーまでよろしくやる)
am start -a android.intent.action.VIEW http://www.google.com
am start -a android.intent.action.VIEW geo:0,0?q=Tokyo


■設定
am start -a android.settings.SETTINGS


■特定のアプリケーションを起動
am start -a android.intent.action.MAIN -n com.android.settings/.Settings

2011年2月18日金曜日

[他]シート名を取得する

excelでシート名を取得したい場合があります。シート名を特定のセルに入れたい場合などですね。
で、式はこんな感じ。

=MID(CELL("filename"),FIND("]",CELL("filename"))+1,31)


2012/1/16 追記。
複数のシートがあった時、最後に編集したシート名?になってしまうようです。
とういうことで、
=MID(CELL("filename",$A$1),FIND("]",CELL("filename",$A$1))+1,31)

2011年2月16日水曜日

[Java]ファイルを簡単にコピーする

Javaにはファイルをコピーするメソッドが存在しません。そのためInputStreamとOutputStreamのread/writeで処理しますが、もっと簡単な方法がありました。コードはこんな感じ。

/**
 * ファイルコピーします。
 *
 * @param srcPath コピー元
 * @param destPath コピー先
 */

public static void copy(File srcPath, File destPath) {

    FileChannel srcChannel = null;
    FileChannel destChannel = null;

    try {
        srcChannel = new FileInputStream(srcPath).getChannel();
        destChannel = new FileOutputStream(destPath).getChannel();

        srcChannel.transferTo(0, srcChannel.size(), destChannel);

    } catch (IOException e) {
        e.printStackTrace();

    } finally {
        if (srcChannel != null) {
            try {
                srcChannel.close();
            } catch (IOException e) {
            }
        }
        if (destChannel != null) {
            try {
                destChannel.close();
            } catch (IOException e) {
            }
        }
    }
}

2011年2月12日土曜日

[Android]プロセスは終了しない(基本的に)

Androidのプロセスは基本的に終了しません。最初違和感があったんですが、最近は慣れてきました。
でも余計なリソースを残したくない。という場合、ActivityのライフサイクルのonDestroyあたりでSystem.exit()を呼び出すという方法があります。

意外とすっきりする感じがします。

2011年2月9日水曜日

[Android]ライブラリプロジェクトを使う

androidの開発で、複数のプロジェクトを開発し、なおかつ、共通部分を切り出したい。というときがあります。そういうときに、ライブラリプロジェクトを使います。
ライブラリプロジェクトを使わない方法としては、共通部分をjar化する方法がありますが、結構面倒だったり、デバッグしづらかったりします。

ライブラリプロジェクトは、以下のようにして作成します。


  1. androidプロジェクトを新規作成します

  2. プロジェクトの設定で、「android」のプロパティで「Is Library」にチェックする

ライブラリプロジェクトを使う方法は、以下のようにします。



  1. androidプロジェクトを新規作成します

  2. プロジェクトの設定で「android」のプロパティで、ライブラリの部分の「追加」ボタンでライブラリプロジェクトを選択します

以上。超簡単。でどんなメリットがあるかというと、


  • ソースを共有できる。ソースフォルダを共有します。ビルド対象になります

  • リソースを共有できる。文字列や画像。ライブラリプロジェクト側のR.javaが追加されます

  • aidlを共有できる。R.java同様に、勝手に追加されます

  • nativeライブラリが共有できる。各プロジェクトにコピーする必要がなくなった

リソースに関しては、ライブラリプロジェクトのにあるものと、同名のものを作るとなんと、オーバライドされます。


ソースのビルドは多少時間がかかりますが、反面、コンパイルされることにより、ビルド時にいろいろなことを解決できます。オーバラードされたリソースIDとか。意外と便利。

2011年2月3日木曜日

[Java]オブジェクトをディープコピーする

Object#cloneをオーバライドして実装するのはいちいち手間がかかります。
で、手っ取り早くオブジェクトをディープコピーするために、シリアライズ・デシリアライズします。コードはこんな感じ。

/**
 * ObjectOutputStreamを使ったディープイコピー
 * @param target 対象のオブジェクト
 * @return コピーしたもの
 * @throws Exception
 */

public static Object deepCopy(Object target) throws Exception {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    oos.writeObject(target);

    ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(
            bos.toByteArray()));
    return ois.readObject();
}

2011年1月30日日曜日

[Java]プライベートフィールドを更新する

UnitTestなどでprivateなフィールドをコードを修正せずに値を設定しようとした場合、publicなアクセッサメソッドを呼び出すか、リフレクションを利用するしかありません。
リフレクションを利用する場合、クラスの型情報を指定する必要がありますが、いちいち指定するのが面倒です。というとで、なかったら再起で呼び出すロジックを組んでみました。コードはこんな感じ。


/**
 * プライベート変数に値を設定します。
 *
 * @param obj インスタンス
 * @param fieldName 変数名
 * @param value 値
 */

public static void setPrivateField(Object obj, String fieldName,Object value) {

    setPrivateField(obj.getClass(), obj, fieldName, value);
}

/**
 * プライベートなstatic変数に値を設定します。
 * finalな変数はダメです。
 *
 * @param cls クラス
 * @param fieldName 変数名
 * @param value 値
 */

public static void setPrivateField(Class<?> cls, String fieldName,
        Object value) {

    setPrivateField(cls, null, fieldName, value);
}

/**
 * プライベートなstatic変数に値を設定します。
 * finalな変数はダメです。
 *
 * @param cls クラス
 * @param obj インスタンス
 * @param fieldName 変数名
 * @param value 値
 */

public static void setPrivateField(Class<?> cls, Object obj, String fieldName, Object value) {

    do {
        try {
            Field field = cls.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(obj, value);
            break;
        } catch (NoSuchFieldException e) {
            // 親クラスからの取得にtry
            cls = cls.getSuperclass();
        } catch (Exception e) {
            // 例外
            e.printStackTrace();
            break;
        }
    } while (cls != null);

}

2011年1月26日水曜日

[Java]プライベートフィールドの値を取得する

privateなフィールドをコードを修正せずに取得しようとした場合、publicなアクセッサメソッドを呼び出すか、リフレクションを利用するしかありません。
リフレクションを利用する場合、クラスの型情報を指定する必要がありますが、いちいち指定するのが面倒です。というとで、なかったら再起で呼び出すロジックを組んでみました。コードはこんな感じ。

/**
 * プライベート変数の値を取得します。
 *
 * @param obj インスタンス
 * @param fieldName 変数名
 * @return 値
 */

public static Object getPrivateField(Object obj, String fieldName) {
    Object result = getPrivateField(obj.getClass(), obj, fieldName);
    return result;
}

/**
 * プライベートなstatic変数の値を取得します。
 *
 * @param cls クラス
 * @param fieldName 変数名
 * @return 値
 */

public static Object getPrivateField(Class<?> cls, String fieldName) {
    Object result = getPrivateField(cls, null, fieldName);
    return result;
}

/**
 * プライベート変数の値を取得します。
 *
 * @param cls クラス
 * @param obj インスタンス
 * @param fieldName 変数名
 * @return 値
 */

public static Object getPrivateField(Class<?> cls, Object obj,
        String fieldName) {
    Object result = null;

    do {
        try {
            Field field = cls.getDeclaredField(fieldName);
            field.setAccessible(true);
            result = field.get(obj);
            break;
        } catch (NoSuchFieldException e) {
            //親クラスからの取得にtry
            cls = cls.getSuperclass();
        } catch (Exception e) {
            //例外
            e.printStackTrace();
            break;
        }
    } while (cls != null);

    return result;
}

2011年1月20日木曜日

[Android]USBドライバ

Android端末をADBで接続する際、USB用のドライバが必要になります。
ということで、コレまで使ったことのあるドライバを備忘のためメモ。


;HTC Desire
%SingleAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C87
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C87&MI_01

;HTC EV4
%SingleAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C8D
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C8D&MI_01

;SHARP IS03
%CompositeAdbInterface% = USB_Install, USB\VID_04DD&PID_939C&MI_05
%CompositeAdbInterface% = USB_Install, USB\VID_04DD&PID_939A&MI_01
%CompositeAdbInterface% = USB_Install, USB\VID_04DD&PID_939B&MI_01

;Xperia X10
%SingleAdbInterface% = USB_Install, USB\VID_0FCE&PID_D12E
%CompositeAdbInterface% = USB_Install, USB\VID_0FCE&PID_D12E&MI_01

;HTC Aria
%SingleAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C92
%CompositeAdbInterface% = USB_Install, USB\VID_0BB4&PID_0C92&MI_01

2011年1月14日金曜日

[Java]プライベートメソッドを呼び出す

UnitTestなどでprivateメソッドをコードを修正せずに実行しようとした場合、該当のprivateメソッドを使っているpublicなメソッドを呼び出すか、リフレクションを利用するしかありません。
リフレクションを利用する場合、クラスの型情報を指定する必要がありますが、いちいち指定するのが面倒です。というとで、なかったら再起で呼び出すロジックを組んでみました。コードはこんな感じ。

/**
 * プライベートメソッド(引数なし)を呼び出します。
 *
 * @param obj インスタンス
 * @param name メソッド名
 * @return 戻り値
 * @throws Throwable プライベートメソッドから発生した例外
 */

public static Object invoke(Object obj, String name) throws Throwable {

    Object result = invoke(obj, new Object[0], name, new Class[0]);
    return result;
}

/**
 * プライベートメソッドを呼び出します。
 *
 * @param obj インスタンス
 * @param args 引数
 * @param name メソッド名
 * @param parameterTypes 引数タイプ
 * @return 戻り値
 * @throws Throwable プライベートメソッドから発生した例外
 */

public static Object invoke(Object obj, Object[] args, String name,
        Class<?>[] parameterTypes) throws Throwable {

    Object result = invoke(obj.getClass(), obj, args, name, parameterTypes);
    return result;
}

/**
 * プライベートメソッドを呼び出します。
 *
 * @param cls クラス
 * @param objインスタンス
 * @param args 引数
 * @param name メソッド名
 * @param parameterTypes 引数タイプ
 * @return 戻り値
 * @throws Throwable プライベートメソッドから発生した例外
 */

public static Object invoke(Class<?> cls, Object obj, Object[] args, String name, Class<?>[] parameterTypes) throws Throwable {

    Object result = null;

    do{
        try {
            Method method = cls.getDeclaredMethod(name, parameterTypes);
            method.setAccessible(true);

            result = method.invoke(obj, args);
            break;

        } catch (NoSuchMethodException e) {
            //親クラスの型でリトライ
            cls = cls.getSuperclass();
        } catch (InvocationTargetException e) {
            throw e.getCause();
        } catch (Throwable e) {
            e.printStackTrace();
            break;
        }
    }while(cls != null);

    return result;
}

2011年1月13日木曜日

[Android]SDKをバージョンアップしたら便利になった!

先日、AndroidのSDKを最新(ver8)にしました。ADTも0.99→8.0にしています。
以下のような変化がありました。

■ライブラリプロジェクトにのプロジェクト名に「-」をつけても大丈夫
 以前はライブラリプロジェクトを参照する側がエラーになっていました。

■ライブラリプロジェクトにnativeライブラリを置いておくと参照側のプロジェクトのapkに反映される
 以前はコピーする必要がありました。

■android.jarへのソース添付が簡単に
 以前は指定ができず、変わったやり方で指定する必要がありましたが、今は他のjarと同じ設定方法でできるようになりました。

■ADTのlogcatビュー
 スタックトレースのダブルクリックでコードにジャンプするようになった。以前はgoto problemをメニュー?から選択する必要があった