2010年11月29日月曜日

ABC2011 Winter

興味があればどうぞ。

http://www.android-group.jp/abc2011w/

2010年11月20日土曜日

Android™技術者認定試験制度

大学院の講義でもやっているので、Androidの技術者認定試験の受験を推奨してます。
http://www.oesf.jp/modules/training/index.php?content_id=2
でも就職とか、社会に出てから役に立つのかな?
まだ、認知度低いし。
学割もあるみたいだけど、まだ未定。

2010年10月6日水曜日

QualcommがAndroid用AR SDK公開

QualcommがAndroid用のAR SDKを公開しています。
https://ar.qualcomm.com/qdevnet/sdk
時間があればいじってみよう。

2010年9月9日木曜日

ちょっと便利なメディアコンバータ

 http://media-convert.com/ja/

2010年8月17日火曜日

Google Developer Day 2010

申し込みまだ間に合います!(但し、参加するためには試験をパスしないといけませんが。。。)
腕に覚えがあれば是非挑戦してみてください。

と書きつつ、自分は問題解く時間が作れず困ってますが(笑)
興味のある人は挑戦してみてください。

Google Developer Day2010

2010年7月20日火曜日

本の紹介

お久しぶりです。

今日は本の紹介をします。

大規模サービス技術入門という本を最近読みました。
この本は、はてなのインターンシップで学生向けに行っている講義+演習を本にしたものです。学生向けというだけあって、システムの設計・運用上の概要(の例)がわかりやすく説明されています。
決して深い所まで議論している本ではありませんが、サーバ設計の考え方からAPの実装まで説明されていて、「システム」について学ぶ上で良い本だと思います。


システムの中で、APサーバやDBサーバを多重化・冗長化することではどういった効果が見込めるのか?
システムの負荷を分析して、サーバのスケールアップ/スケールアウトを考える上でのポイントは?どんな時にメモリ増設が有効で、どんな時にサーバ多重化が有効か?
何億ものドキュメントから全文検索をする際の実装上のポイントは?(どういったポリシーで実装すればユーザ満足度につながるか?)そのためのアルゴリズムは?
などなど。



講義で学んだ知識が、システム開発・運用の現場でどう役に立つのか(自分の知識が実際のシステムの中のどの部分と結びつくのか)を知るための良い読み物だと思います。

以下もくじ。

  • 第1回 大規模Webサービスの開発オリエンテーション―全体像を把握する
    • Lesson 0 本書の源 ―本書で説明すること,しないこと
    • Lesson 1 大規模なサービスと小規模なサービス
    • Lesson 2 成長し続けるサービスと,大規模化の壁
    • Lesson 3 サービス開発の現場
  • 第2回 大規模データ処理入門 ―メモリとディスク,Webアプリケーションと負荷
    • Lesson 4 はてなブックマークのデータ規模 ―データが大きいと処理に時間がかかる
    • Lesson 5 大規模データ処理の難所 ―メモリとディスク
    • Lesson 6 スケーリングの要所
    • Lesson 7 大規模データを扱うための基礎知識
  • 第3回 OSのキャッシュと分散 ―大きなデータを効率良く扱うしくみ
    • Lesson 8 OSのキャッシュ機構
    • Lesson 9 I/O負荷の軽減策
    • Lesson 10 局所性を活かす分散
  • 第4回 DBのスケールアウト戦略 ―分散を考慮したMySQLの運用
    • Lesson 11 インデックスを正しく運用する ―分散を考慮したMySQL運用の大前提
    • Lesson 12 MySQLの分散 ―スケーリング前提のシステム設計
    • Lesson 13 MySQLのスケールアウトとパーティショニング
  • 第5回 大規模データ処理[実践]入門 ―アプリケーション開発の勘所
    • Lesson 14 用途特化型インデクシング ―大規模データを捌く
    • Lesson 15 理論と実践の両側から取り組む
  • 第6回 [課題]圧縮プログラミング ―データサイズ,I/O高速化との関係を意識する
    • Lesson 16 [課題]整数データをコンパクトに持つ
    • Lesson 17 VB Codeと速度感覚
    • Lesson 18 課題の詳細と回答例
  • 第7回 アルゴリズムの実用化 ―身近な例で見る理論・研究の実践投入
    • Lesson 19 アルゴリズムと評価
    • Lesson 20 はてなダイアリーのキーワードリンク
    • Lesson 21 はてなブックマークの記事カテゴライズ
  • 第8回 [課題]はてなキーワードリンクの実装 ―応用への道筋を知る
    • Lesson 22 [課題]はてなキーワードリンクを作る
    • Lesson 23 回答例と考え方
  • 第9回 全文検索技術に挑戦 ―大規模データ処理のノウハウ満載
    • Lesson 24 全文検索技術の応用範囲
    • Lesson 25 検索システムのアーキテクチャ
    • Lesson 26 検索エンジンの内部構造
  • 第10回 [課題]全文検索エンジンの作成 ―基本部分,作り込み,速度と精度の追求
    • Lesson 27 [課題]はてなブックマーク全文検索を作る
    • Lesson 28 回答例と考え方
  • 第11回 大規模データ処理を支えるサーバ/インフラ入門 ―Webサービスのバックエンド
    • Lesson 29 エンタープライズ vs. Webサービス
    • Lesson 30 クラウドvs.自前インフラ
  • 第12回 スケーラビリティの確保に必要な考え方 ―規模の増大とシステムの拡張
    • Lesson 31 レイヤとスケーラビリティ
    • Lesson 32 負荷の把握,チューニング
  • 第13回 冗長性の確保,システムの安定化 ―ほぼ100%の稼動率を実現するしくみ
    • Lesson 33 冗長性の確保
    • Lesson 34 システムの安定化
    • Lesson 35 システムの安定化対策
  • 第14回 効率向上作戦 ―ハードウェアのリソースの使用率を上げる
    • Lesson 36 仮想化技術
    • Lesson 37 ハードウェアと効率向上 ―低コストを実現する要素技術
  • 第15回 Webサービスとネットワーク ―ネットワークで見えてくるサービスの成長
    • Lesson 38 ネットワークの分岐点
    • Lesson 39 さらなる上限へ
  • 特別編 いまどきのWebサービス構築に求められる実践技術 ―大規模サービスに対応するために
    • Special Lesson 1 ジョブキューシステム ―TheSchwartz、 Gearman
    • Special Lesson 2 ストレージの選択 ―RDBMSかkey-valueストアか
    • Special Lesson 3 キャッシュシステム ―Squid,Varnish
    • Special Lesson 4 計算クラスタ ―Hadoop

Nexus One販売終了

Nexus Oneが手に入らなくなるっぽい。
http://japan.cnet.com/news/service/story/0,3800104747,20417034,00.htm
もう2台ほど買っておこうかな。

SunSPOTとか面白いものはどんどん手に入らなくなってきてしまう。

2010年7月14日水曜日

App Inventor for Android

App Inventor for Androidなんてものが、どんどん出てきたら、もうコーディングなんてする必要ないかな。
確かに簡単なものだったらできそう。
XMLで書けるauのケータイカスタムキットなんてものあったけど、まったく流行ってなさそうだし、やっぱコーディングできないとね。

2010年6月18日金曜日

忘れがちなC++での関数ポインタ

クラス内部で状態によって呼ぶ出す関数を変えるときには関数ポインタを使うと便利ですが、C++では書き方が若干面倒なので、その記録。
#include <cstdlib>
#include <iostream>

class FPSample {
public:

    void function_switch(int sw) {
        switch (sw) {
            case 1: m_pFunctionSample = &FPSample::function1;
                break;
            case 2: m_pFunctionSample = &FPSample::function2;
                break;
            default:
                std::cout << "default" << std::endl;
        }
    }

    bool function(int x) {
        return (this->*m_pFunctionSample)(x);
        // return (*mFunctionSample)(x); // Error
    }
private:
    typedef bool (FPSample::*FUNCTION_SAMPLE)(int);
    FUNCTION_SAMPLE m_pFunctionSample;

    bool function1(int x) {
        std::cout << "function1 : " << x << std::endl;
        return true;
    }

    bool function2(int x) {
        std::cout << "function2 : " << x << std::endl;
        return false;
    }
};

int main(int argc, char** argv) {
    FPSample mFPSample;
    int sw;
    int x;

    x = 10;
    
    sw = 1;
    mFPSample.function_switch(sw);
    mFPSample.function(x);

    sw = 2;
    mFPSample.function_switch(sw);
    mFPSample.function(x);

    return 0;
}

2010年6月14日月曜日

たまには使えるアプリ

Wifiの受信レベルを測定し、それをグラフにするアプリです。



















package jp.ac.uu.is.degas.android.wifi;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class WifiViewActicvity extends Activity {

    private static final int MENU_START = 0;
    private static final int MENU_PAUSE = 1;
    private static final int MENU_QUIT = 2;
    private MySurfaceView mySurfaceView;
    private WifiManager mWifiManager;
    private boolean run;
    private static final float MIN_LEVEL = -120;
    private static final float MAX_LEVEL = 0;
    private static final int TIME_BUFFDER_SIZE = 50;
    private HashMap apMap;
    private long cnt;

    private int[] lineColor = { Color.BLUE, Color.CYAN, Color.GREEN,
            Color.MAGENTA, Color.RED, Color.YELLOW, Color.WHITE };
    private int indexOfColor;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mySurfaceView = new MySurfaceView(getApplication(), 0,
                TIME_BUFFDER_SIZE, MIN_LEVEL, MAX_LEVEL, TIME_BUFFDER_SIZE,
                (int) ((MAX_LEVEL - MIN_LEVEL) / 10));
        setContentView(mySurfaceView);
        apMap = new HashMap();
        run = false;
        cnt = 0;
        indexOfColor = 0;
    }

    private void startScan() {
        run = true;
        mWifiManager = (WifiManager) getSystemService(WIFI_SERVICE);
        registerReceiver(mBroadcastReceiver, new IntentFilter(
                WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
        mWifiManager.startScan();
    }

    private void pauseScan() {
        run = false;
    }

    private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            List scanResults = mWifiManager.getScanResults();
            for (ScanResult result : scanResults) {
                String bssid = result.BSSID;
                int level = result.level;
                AccessPoint ap = apMap.get(bssid);
                if (ap == null) {
                    ap = new AccessPoint(TIME_BUFFDER_SIZE + 1);
                    ap.setColor(lineColor[indexOfColor]);
                    indexOfColor++;
                    indexOfColor %= lineColor.length;
                }
                ap.setLevel(cnt, Float.valueOf(level));
                ap.setUpdate(true);
                apMap.put(bssid, ap);
            }
            for (Map.Entry e : apMap.entrySet()) {
                AccessPoint ap = e.getValue();
                if (!ap.isUpdate()) {
                    ap.setLevel(cnt, null);
                }
                ap.setUpdate(false);
                apMap.put(e.getKey(), ap);
            }
            if (run) {
                mySurfaceView.setData(cnt, apMap);
                cnt++;
                startScan();
            }
        }
    };

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        menu.add(0, MENU_START, 0, "START").setIcon(
                android.R.drawable.ic_media_play);
        menu.add(0, MENU_PAUSE, 0, "PAUSE").setIcon(
                android.R.drawable.ic_media_pause);
        menu.add(0, MENU_QUIT, 0, "QUIT").setIcon(
                android.R.drawable.ic_menu_close_clear_cancel);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case MENU_START:

            startScan();
            break;
        case MENU_PAUSE:
            pauseScan();
            break;
        case MENU_QUIT:
            finish();
            break;
        }
        return super.onContextItemSelected(item);
    }
}

class AccessPoint {
    private Float[] data;
    private boolean update;
    private int color;

    AccessPoint(int size) {
        data = new Float[size];
    }

    void setLevel(long cnt, Float level) {
        data[(int) (cnt % data.length)] = level;
    }

    void setUpdate(boolean update) {
        this.update = update;
    }

    boolean isUpdate() {
        return update;
    }

    void setColor(int color) {
        this.color = color;
    }

    int getColor() {
        return color;
    }

    Float[] getData() {
        return data;
    }
}

class MySurfaceView extends SurfaceView implements SurfaceHolder.Callback,
        Runnable {
    private SurfaceHolder mSurfaceHolder;
    private Thread thread;
    private Canvas canvas;
    private MyGraph myGraph;
    private float minX, maxX, minY, maxY;
    private int numDivX, numDivY;
    private long cnt;
    private HashMap apMap = new HashMap();

    MySurfaceView(Context context, float minX, float maxX, float minY,
            float maxY, int numDivX, int numDivY) {
        super(context);
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);
        myGraph = new MyGraph();
        this.minX = minX;
        this.maxX = maxX;
        this.minY = minY;
        this.maxY = maxY;
        this.numDivX = numDivX;
        this.numDivY = numDivY;
    }

    public void surfaceChanged(SurfaceHolder holder, int format, int width,
            int height) {
    }

    public void surfaceCreated(SurfaceHolder holder) {
        thread = new Thread(this);
        thread.start();
    }

    public void surfaceDestroyed(SurfaceHolder holder) {
        thread = null;
    }

    public void run() {

        while (thread != null) {
            canvas = mSurfaceHolder.lockCanvas();
            myGraph.drawRule(canvas);

            String[] bssidName = new String[apMap.size()];
            int[] bssidColor = new int[apMap.size()];
            int num = 0;
            for (Map.Entry e : apMap.entrySet()) {
                AccessPoint ap = e.getValue();
                Float[] originalData = ap.getData();
                int size = originalData.length;
                Float[] data = new Float[2 * size];
                for (int i = 0; i < size; i++) {
                    data[2 * i] = Float.valueOf(i);
                    data[2 * i + 1] = (cnt < size) ? originalData[i]
                            : originalData[(int) ((i + cnt + 1) % size)];
                }
                myGraph.drawLines(canvas, data, ap.getColor());
                bssidName[num] = e.getKey();
                bssidColor[num] = ap.getColor();
                num++;
            }
            if (apMap.size() > 0) {
                myGraph.drawLegend(canvas, bssidName, bssidColor);
            }
            mSurfaceHolder.unlockCanvasAndPost(canvas);
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
            }
        }
    }

    @Override
    protected void onSizeChanged(int width, int heigh, int old_w, int old_h) {
        int size = (width < heigh) ? width : heigh;
        myGraph.setDarwArea(size, size, minX, maxX, minY, maxY, numDivX,
                numDivY, false, true);
    }

    void setData(long cnt, HashMap apMap) {
        this.cnt = cnt;
        this.apMap = apMap;
    }
}

class MyGraph {
    private static final float TOP_MARGIN_RATIO = 0.05f; // 描画エリアサイズに対する上マージンの比
    private static final float BOTTOM_MARGIN_RATIO = 0.05f; // 描画エリアサイズに対する下マージンの比
    private static final float LEFT_MARGIN_RATIO = 0.05f; // 描画エリアサイズに対する左マージンの比
    private static final float RIGHT_MARGIN_RATIO = 0.05f; // 描画エリアサイズに対する右マージンの比
    private boolean labelFlagX; // X軸ラベル表示フラグ
    private boolean labelFlagY; // Y軸ラベル表示フラグ
    private Paint paintLabel = new Paint();
    private float bodyW; // ラベルを含むグラフエリア
    private float bodyH; // ラベルを含むグラフエリア
    private float graphW; // ラベルを含まないグラフエリア
    private float graphH; // ラベルを含まないグラフエリア
    private float graphOriginX; // ラベルを含まないグラフエリアの原点
    private float graphOriginY; // ラベルを含まないグラフエリアの原点
    private float minX, maxX, minY, maxY;
    private int numDivX, numDivY;

    private float legendOriginX; // 凡例原点
    private float legendOriginY; // 凡例原点
    private Paint legendLabel = new Paint();

    void setDarwArea(float drawW, float drawH, float xMin, float xMax,
            float yMin, float yMax, int xDivNum, int yDivNum,
            boolean labelXFlag, boolean labelYFlag) {
        this.minX = xMin;
        this.maxX = xMax;
        this.minY = yMin;
        this.maxY = yMax;
        this.numDivX = xDivNum;
        this.numDivY = yDivNum;
        this.labelFlagX = labelXFlag;
        this.labelFlagY = labelYFlag;
        // ラベルを含むグラフエリア
        bodyW = (1 - LEFT_MARGIN_RATIO - RIGHT_MARGIN_RATIO) * drawW;
        bodyH = (1 - TOP_MARGIN_RATIO - BOTTOM_MARGIN_RATIO) * drawH;
        // ラベルを含まないグラフエリア
        FontMetrics fontMetrics = paintLabel.getFontMetrics();
        graphH = bodyH;
        if (labelXFlag) {
            graphH += (fontMetrics.top + fontMetrics.bottom);
        }
        graphW = bodyW;
        if (labelYFlag) {
            float maxTextWidth = 0;
            for (int i = 0; i < yDivNum + 1; i++) {
                String text = String
                        .valueOf(yMin + i * (yMax - yMin) / yDivNum);
                float baseX = paintLabel.measureText(text); // テキスト幅
                maxTextWidth = (baseX > maxTextWidth) ? baseX : maxTextWidth;
            }
            graphW -= maxTextWidth;
        }
        // ラベルを含まないグラフエリアの原点
        graphOriginX = LEFT_MARGIN_RATIO * drawW + (bodyW - graphW);
        graphOriginY = TOP_MARGIN_RATIO * drawH;
        // 凡例原点
        legendOriginX = graphOriginX + 0.05f * graphW;
        legendOriginY = graphOriginY + 0.05f * graphH;
    }

    /* グラフの罫線、軸描画 */
    void drawRule(Canvas canvas) {
        // 背景色
        canvas.drawColor(Color.WHITE);
        // 罫線
        Paint paintRule = new Paint();
        paintRule.setStyle(Paint.Style.FILL);
        paintRule.setColor(Color.BLACK);
        for (int i = 0; i < numDivX + 1; i++) {
            canvas.drawLine(graphOriginX + i * (graphW / numDivX),
                    graphOriginY, graphOriginX + i * (graphW / numDivX),
                    graphOriginY + graphH, paintRule);
        }
        for (int i = 0; i < numDivY + 1; i++) {
            canvas.drawLine(graphOriginX,
                    graphOriginY + i * (graphH / numDivY), graphOriginX
                            + graphW, graphOriginY + i * (graphH / numDivY),
                    paintRule);
        }
        // 軸ラベル
        paintLabel.setColor(Color.GRAY);
        FontMetrics fontMetrics = paintLabel.getFontMetrics();
        if (labelFlagX) {
            for (int i = 0; i < numDivX + 1; i++) {
                String text = String
                        .valueOf(minX + i * (maxX - minX) / numDivX);
                float baseX = paintRule.measureText(text) / 2; // テキスト幅の半分
                float baseY = -fontMetrics.top; // baselineからtopまでの高さ
                canvas.drawText(text, graphOriginX + i * (graphW / numDivX)
                        - baseX, graphOriginY + graphH + baseY, paintLabel);
            }
        }
        if (labelFlagY) {
            for (int i = 0; i < numDivY + 1; i++) {
                String text = String
                        .valueOf(minY + i * (maxY - minY) / numDivY);
                float baseX = paintLabel.measureText(text); // テキスト幅
                float baseY = -fontMetrics.top / 2; // baselineからtopまでの高さの半分
                canvas.drawText(text, graphOriginX - baseX, graphOriginY
                        + graphH - i * (graphH / numDivY) + baseY, paintLabel);
            }
        }
    }

    void drawLines(Canvas canvas, Float[] data, int color) {
        // 境界チェック
        for (int i = 0; i < data.length / 2; i++) {
            if (data[2 * i] != null && data[2 * i + 1] != null) {
                data[2 * i] = (data[2 * i] < minX) ? minX : data[2 * i];
                data[2 * i] = (data[2 * i] > maxX) ? maxX : data[2 * i];
                data[2 * i + 1] = (data[2 * i + 1] < minY) ? minY
                        : data[2 * i + 1];
                data[2 * i + 1] = (data[2 * i + 1] > maxY) ? maxY
                        : data[2 * i + 1];
            }
        }
        float[] pts = new float[4 * (data.length / 2 - 1)];
        // 描画座標へ変換
        float pxlParUnitX = graphW / (maxX - minX); // X軸の単位辺りの画素値数
        float pxlParUnitY = graphH / (maxY - minY); // Y軸の単位辺りの画素値数
        for (int i = 0; i < data.length / 2 - 1; i++) {
            if (data[2 * i] != null && data[2 * i + 1] != null
                    && data[2 * i + 2] != null && data[2 * i + 3] != null) {
                float x1 = data[2 * i];
                float y1 = data[2 * i + 1];
                float x2 = data[2 * i + 2];
                float y2 = data[2 * i + 3];
                pts[4 * i] = (x1 - minX) * pxlParUnitX + graphOriginX;
                pts[4 * i + 1] = graphH - (y1 - minY) * pxlParUnitY
                        + graphOriginY;
                pts[4 * i + 2] = (x2 - minX) * pxlParUnitX + graphOriginX;
                pts[4 * i + 3] = graphH - (y2 - minY) * pxlParUnitY
                        + graphOriginY;
            }
        }
        Paint paint = new Paint();
        paint.setColor(color); // 線の色
        paint.setStrokeWidth(2); // 線の太さ
        canvas.drawLines(pts, paint);
    }

    void drawLegend(Canvas canvas, String[] name, int[] color) {
        Paint paint = new Paint();
        float lineLength = 0.05f * graphW;
        FontMetrics fontMetrics = legendLabel.getFontMetrics();
        float width;
        float textW = 0;
        for (int i = 0; i < name.length; i++) {
            width = legendLabel.measureText(name[i]);
            textW = (width > textW) ? width : textW;
        }
        float textH = -fontMetrics.top + fontMetrics.bottom;

        float legendH = name.length * textH + fontMetrics.bottom;
        float legendW = textW + lineLength;

        paint.setColor(Color.WHITE);
        paint.setStyle(Paint.Style.FILL);
        canvas.drawRect(legendOriginX, legendOriginY, legendOriginX + legendW,
                legendOriginY + legendH, paint);

        paint.setColor(Color.BLACK);
        paint.setStyle(Paint.Style.STROKE);
        canvas.drawRect(legendOriginX, legendOriginY, legendOriginX + legendW,
                legendOriginY + legendH, paint);
        float x, y;
        x = legendOriginX;
        y = legendOriginY + textH;
        for (int i = 0; i < name.length; i++) {
            paint.setColor(color[i]);
            paint.setStrokeWidth(2);
            canvas.drawLine(x, y + fontMetrics.top / 2, x + lineLength, y
                    + fontMetrics.top / 2, paint);
            paint.setColor(Color.BLACK);
            canvas.drawText(name[i], x + lineLength, y, legendLabel);
            y += textH;
        }
    }
}


2010年6月13日日曜日

Androidで画面の縦横が切り替わった時にActivityが再起動されない方法

AndroidのActivityのlifecycleは
http://developer.android.com/guide/topics/fundamentals.html
の説明にあるようにonCreateから始まってonDestroyで終わります。
Activityが起動すると、
onCreate -> onStart -> onResume
の順序で、Activityが動作します。
終了時には、
onPause -> onStop -> onDestroy
の順序でActivityが終わります。
途中、割り込みなどで、推移します。

画面の縦横が切り替わった時には
onCreate -> onStart -> onResume -> onPause -> onStop -> onDestroy -> onCreate -> onStart -> onResume
の順序で呼ばれます。
これは、画面の縦横変化によって、画面を再構成する必要があるからです。

だけど、Activityによっては、これでは困る場合があります。
そんなときは、AndroidManifest.xmlの対応するActivityに
android:configChanges="orientation"
を加えましょう。
すると、画面の縦横が切り替わってもonPause以降は呼ばれません。
その代わりにonConfigurationChangedが呼ばれます。

2010年6月9日水曜日

AndroidでBluetooth~Discoveryまで~


package jp.ac.hoge.android.bluetooth;

import java.util.Set;

import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.widget.TextView;

public class BluetoothTestActivity extends Activity {
private static final int REQUEST_ENABLE_BT = 1;
private static final int REQUEST_STATE_CHANGE_BT = 2;
private TextView tv_result = null;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothReceiver mBluetoothReceiver = null;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

tv_result = (TextView) findViewById(R.id.tv_result);
tv_result.setText("");

checkBluetooth();
}

private void checkBluetooth() {
tv_result.append("Step 1:Bluetoothの利用可能状態\n");
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
tv_result.append("Bluetoothはサポートされてません\n");
} else {
tv_result.append("Bluetoothはサポートされています\n");
if (mBluetoothAdapter.isEnabled()) {
tv_result.append("Bluetoothは利用可能です\n");
getLocalInformation();
} else {
Intent enableBTIntent = new Intent(
BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBTIntent, REQUEST_ENABLE_BT);
Intent stateChangedBTIntent = new Intent(
BluetoothAdapter.ACTION_STATE_CHANGED);
startActivityForResult(stateChangedBTIntent,
REQUEST_STATE_CHANGE_BT);
}
}
}

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == RESULT_OK) {
tv_result.append("Bluetoothが利用可能になりました\n");
getLocalInformation();
} else if (resultCode == RESULT_CANCELED) {
tv_result.append("Bluetoothは利用不可です\n");
}
} else if (requestCode == REQUEST_STATE_CHANGE_BT) {
switch (resultCode) {
case BluetoothAdapter.STATE_TURNING_ON:
tv_result.append("STATE_TURNING_ON\n");
break;
case BluetoothAdapter.STATE_ON:
tv_result.append("STATE_ON\n");
break;
case BluetoothAdapter.STATE_TURNING_OFF:
tv_result.append("STATE_TURNING_OFF\n");
break;
case BluetoothAdapter.STATE_OFF:
tv_result.append("STATE_OFF\n");
break;
}
}
}

private void getLocalInformation() {
tv_result.append("\nStep 2:自機Bluetoothの調査\n");

tv_result.append(mBluetoothAdapter.getName() + ":"
+ mBluetoothAdapter.getAddress() + "\n");
switch (mBluetoothAdapter.getScanMode()) {
case BluetoothAdapter.SCAN_MODE_CONNECTABLE:
tv_result.append("SCAN_MODE_CONNECTABLE:");
break;
case BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE:
tv_result.append("SCAN_MODE_CONNECTABLE_DISCOVERABLE:");
break;
case BluetoothAdapter.SCAN_MODE_NONE:
tv_result.append("SCAN_MODE_NONE:");
break;
}
switch (mBluetoothAdapter.getState()) {
case BluetoothAdapter.STATE_OFF:
tv_result.append("STATE_OFF\n");
break;
case BluetoothAdapter.STATE_ON:
tv_result.append("STATE_ON\n");
break;
case BluetoothAdapter.STATE_TURNING_OFF:
tv_result.append("STATE_TURNING_OFF\n");
break;
case BluetoothAdapter.STATE_TURNING_ON:
tv_result.append("STATE_TURNING_ON\n");
break;
}
findPairedDevices();
}

private void findPairedDevices() {
tv_result.append("\nStep 3:登録済みのBluetoothの調査\n");

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter
.getBondedDevices();
if (pairedDevices.size() > 0) {
for (BluetoothDevice device : pairedDevices) {
tv_result.append(device.getName() + ":" + device.getAddress()
+ ":" + device.getBluetoothClass() + "\n");
}
} else {
tv_result.append("登録されているBluetoothデバイスはありません\n");
}
discoverDevices();
}

private void discoverDevices() {
tv_result.append("\nStep 4:Bluetoothデバイスの探索\n");

mBluetoothReceiver = new BluetoothReceiver();
registerReceiver(mBluetoothReceiver, new IntentFilter(
BluetoothDevice.ACTION_FOUND));
mBluetoothAdapter.startDiscovery();
tv_result.append("探索開始\n");
}

class BluetoothReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
tv_result.append("Bluetoothデバイスを発見\n");
String action = intent.getAction();
if (BluetoothDevice.ACTION_FOUND.equals(action)) {
BluetoothDevice device = intent
.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
tv_result.append(device.getName() + ":" + device.getAddress()
+ ":" + device.getBluetoothClass() + "\n");
}
}
}
}

AndroidManifest.xml
<uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>

BLUETOOTH_ADMINはstartDiscovery()に必要です。

2010年6月7日月曜日

Nexus One搭載センサー 

  • TYPE_ACCELEROMETER (加速度センサー)○
  • TYPE_GYROSCOPE (ジャイロセンサー)×
  • TYPE_LIGHT(光センサー)○
  • TYPE_MAGNETIC_FIELD(磁気センサー)○
  • TYPE_ORIENTATION(方位センサー)○
  • TYPE_PRESSURE(圧力センサー)×
  • TYPE_PROXIMITY(近接センサー)○
  • TYPE_TEMPERATURE(温度センサー)×

AndroidのHttpのPostでもはまる

AndroidからHttpでPostするときのアプリです。

package jp.ac.hoge.android.httppost;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

public class HttpPostActivity extends Activity {
private static final String URI = "http://hoge/http_post_test.php";

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

Button btn_sbmit = (Button) findViewById(R.id.btn_sbmit);
btn_sbmit.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// TODO 自動生成されたメソッド・スタブ
EditText et_string = (EditText) findViewById(R.id.et_string);
String value = et_string.getText().toString();
HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost(URI);
List<NameValuePair> nameValuePair = new ArrayList<NameValuePair>(
1);
nameValuePair.add(new BasicNameValuePair("str", value));

try {
httppost.setEntity(new UrlEncodedFormEntity(nameValuePair));
HttpResponse response = httpclient.execute(httppost);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
response.getEntity().writeTo(byteArrayOutputStream);
TextView tv_result = (TextView) findViewById(R.id.tv_result);
tv_result.setText(byteArrayOutputStream.toString());

} catch (UnsupportedEncodingException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
} catch (IOException e) {
// TODO 自動生成された catch ブロック
e.printStackTrace();
}
}
});
}
}

httpclient.execute(httppost)でこけますが、LogにINTERNETのpermissionがないよと言われるので、AndroidManifest.xmlに

<uses-permission android:name="android.permission.INTERNET"></uses-permission>

を付け加えます。


サーバアプリはPOSTで投げた文字列をただ2回出力するだけです。

2010年6月6日日曜日

AndroidのWifiのscanStartではまる

AndroidでWifiをScanするプログラムです。

package jp.ac.hoge.android.wifi;

import java.util.List;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

public class WifiActivity extends Activity implements OnClickListener {
private TextView tv_search_result;
private WifiManager wifi_mng;
private WifiReceiver wifi_rec;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button btn_start_search = (Button) findViewById(R.id.btn_start_search);
btn_start_search.setOnClickListener(this);
}

public void onClick(View v) {
// TODO 自動生成されたメソッド・スタブ
if (v.getId() == R.id.btn_start_search) {
tv_search_result = (TextView) findViewById(R.id.tv_search_result);
wifi_mng = (WifiManager) getSystemService(Context.WIFI_SERVICE);
wifi_rec = new WifiReceiver();
registerReceiver(wifi_rec, new IntentFilter(
WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
wifi_mng.startScan();
tv_search_result.setText("スキャン開始\n");
}
}

class WifiReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
// TODO 自動生成されたメソッド・スタブ
StringBuffer sb = new StringBuffer();
List<ScanResult> result_list = wifi_mng.getScanResults();
for (int i = 0; i < result_list.size(); i++) {
sb.append(new Integer(i + 1).toString() + ".");
sb.append((result_list.get(i)).toString());
sb.append("\n");
}
tv_search_result.setText(sb);
}
}
}

このままだとstartScanで落ちてしまいます(もちろんエミュレータでは動かないのので実機Nexous Oneで)。
なので、AndroidManifext.xmlに

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"></uses-permission>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"></uses-permission>

の権限を追加します(BREWみたい)。Bluetoothも同じような権限があるみたいだけどまだ未調査。



研究室のデスク周りで6つAPが見つかりました。
SSIDにはアクセスポイント名、BSSIDにはMACアドレス、capabilitiesには暗号化などの方式が表示されます。

inSSIDerみたいなのもすぐできそう。すでにあるかも。

2010年5月21日金曜日

Android 2.2

Android 2.2が出ております。
http://developer.android.com/index.html
噂によると相当速くなっているらしい。

2010年5月20日木曜日

android端末の画面キャプチャー


Android機でアプリ作ってて画面の絵が欲しいとき
SDK\tools\ddms.bat
でDalvik Debug Monitorを立ち上げ「Device」->「Screen capture」で実機のキャプチャーがとれてちょっとハッピー。
ヒープサイズとかも見れます。

2010年3月28日日曜日

お勧め記事

軽く立ち読みしただけなのですが、日経ソフトウェアの「特集1 要件定義からテストまでJavaでアプリケーションを作り上げよう! 」は中々良さそうなのでお勧めです。

2010年3月24日水曜日

たまにはSunSPOT備忘録

  • RadiostreamプロトコルとRadiogramプロトコルの違い
Radiostream:到達保障あり、ユニキャスト可能、ブロードキャスト不可能
Radiogram:到達保障なし、ユニキャスト可能、ブロードキャスト可能

  • RadiogramクラスとDatagramインターフェースの違い
RadiogramはDatagramの実装
read/write/receive/sendだけならDatagramでOK(RadiogramでもOK)
getRssi()なんか使いたければRadiogram

2010年3月3日水曜日

Nexus One

またしても、こんなもの買ってしまいました。

2010年1月26日火曜日

NetBeansでPHP

そろそろ修士論文発表会の頃でしょうか??
発表者は頑張ってくださいね。

今更PHPに興味もないんですが、いつの間にやらNetBeansにPHPがサポートされていたのでメモ。
PHPを使ったWebアプリをIDEで開発できると大分幸せかもしれません。
ちょっとしか試していませんが、一応補間入力・Webサーバへのアップはできるようでした。
(外部サーバの場合FTPサーバが上がっていることが必要です。)

注意としては、httpサーバの上げ下げは手動です。

一応スナップショット。こんな感じで(笑)
設定


プロジェクト作成




2010年1月17日日曜日

JUnitで単体テスト

プログラムを書いた時にテストを行うことは非常に重要です。
プログラムのテストと言っても、
単体テスト => 結合テスト => システムテスト
と、段階的に行うのが一般的なようです。

各テストをどのような観点で行うかはさておき、Javaで書いたプログラムの単体テストを支援するフレームワークとしてJUnitがあります。

NetBeansには標準でJUnitが入っていますので、ちょっと使ってみたいと思います。
(利用環境はNetBeans6.8ですが、他のVersionでも多分一緒)

1.テスト対象のクラスの作成
適当な名前でプロジェクトを作成して、以下のコードを作成します。(パッケージ等は適当に。)

今回のテスト対象クラスFactorialClass。

package jp.taka.factorial;

/**
* 階乗計算をするクラスです。。。
* @author taka
*/
public class FactorialClass {
/**
* ループを使って階乗を計算します。
* @param x
* @return x!
*/
public static int factorial(int x){
int fact = 1;
for(int i=x; i>=1;i--){
fact *= i;
}
return fact;
}

/**
* 再帰を使って階乗を計算します。
* @param x
* @return x!
*/
public static int factorial2(int x){
if(x > 1)
return x*factorial2(x-1);
else
return 1;
}
}


一応Mainも。。

package jp.taka.factorial;

public class Main {
public static void main(String[] args) {
int f1 = FactorialClass.factorial(4);
int f2 = FactorialClass.factorial2(4);

System.out.println(f1);
System.out.println(f2);
}
}


ここまでで、NetBeansのプロジェクトエクスプロラー(?)はこんな感じになると思います。


2.テストコード作成
ここからがJUnitの出番です。(今回はJUnit4を使います)

プロジェクトを右クリックして新規->その他を選択すると、ウィンドウが一つ開くので、下図のように選択し、次へをクリックします。


次の画面はこんな感じで、完了をクリック。


ここまでくると、NetBeansがテストクラスのひな形を作成してくれます。
それを下のようにいじります。
ここでのポイントは、
・assertEquals():第一引数と第二引数が一致しているか判定
・@Testアノテーション:メソッドがテストメソッドであることを明示する


package jp.taka.factorial;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;

/**
*
* @author taka
*/
public class FactorialClassTest {

public FactorialClassTest() {
}

@BeforeClass
public static void setUpClass() throws Exception {
}

@AfterClass
public static void tearDownClass() throws Exception {
}

/**
* FactorialClass#factorial(int x)のテスト
*/
@Test
public void testFactorial() {
System.out.println("chk factorial");
int x = 4;
int expResult = 4*3*2*1;
int result = FactorialClass.factorial(x);
assertEquals(expResult,result );
}

/**
* FactorialClass#factorial2(int x)のテスト
*/
@Test
public void testFactorial2() {
System.out.println("chk factorial 2");
int x = 4;
int expResult = 4*3*2*1;
int result = FactorialClass.factorial2(x);
assertEquals(expResult,result );
}
}


3.テストをしてみよう
プロジェクト名を右クリックして、テストをクリックします(下図)


@Testアノテーションがついたテストメソッドが実行されます。
以下のような結果が出れば成功です。


ちなみに、テスト結果NGのモノがあった場合は、下図。


4.まとめ
今回は、NetBeansでJUnitを使って簡単なテストを行いました。
今回使ったのはassertEqualsだけですが、他にも色々ありますので、興味のある方は是非。

また、JUnit以外にも、CUnit(C言語用),CppUnit(C++言語用)などなどxUnitシリーズは沢山ありますので、研究でも役立つ・・・かも?

2010年1月10日日曜日

Bluetooth 4.0

低消費電力向けのBluetooth 4.0のプレスリリースが出てました。
最大28octetsしか送れず1Mbpsしかでないけど、低消費電力は魅力。
たまにしかデータ送らないセンサーとかには良いかも。
advanced sniff-subratingというのが使われるみただけど、RSSIとるためにガリガリコネクトしたらやっぱり電力食っちゃうよね。