その後のその後

iOSエンジニア 堤 修一のブログ github.com/shu223

iOSエンジニアが初めてAndroid開発をやってみた第1日目のメモ

当方フリーランスエンジニアですが、iOS専業でやっております。これまで幾度と無く「ちなみにAndroidの方は・・・?」「すいません、できないんです・・・」と肩身の狭い思いをしてきましたが、ついに今日第一歩目を踏み出しました。


ちなみに現在のスペックですが、

  • iOSアプリ開発歴4年、Android開発歴ゼロ
  • Androidはユーザーとしても経験値ほぼゼロ(先週 Nexus5 購入して WiFi セットアップしただけ)

という感じです。


実況中継的に随時追記する形で書いていきたいと思います。iOS からプログラミングの世界に入り、これから Android もやってみたい、という人も今となっては結構多いんじゃないかと思うので、どなたかの第一歩目の参考になれば幸いです。


※(終了後に追記)あとから読んだ人には何のことかわからないかもしれませんが、本記事は昼ぐらいに出だし(午前中に進めた内容。書籍購入まで)をアップし、その日何かやる度に実況中継的に追記していったものです。思いつくままに手を付けていった順に書いてるので、勉強しやすい順序とかにもなっていないと思います。

Android Studio インストール

何はともあれ開発環境のインストールから。(Android Studioは、iOSでいうXcode みたいなもの)

  • http://developer.android.com/sdk/index.html
  • Setup Wizard 通り
  • 途中で JDK 7.0 のダウンロード&インストールが必要(リンクはウィザード内で提示されるので事前準備は不要)
  • Standard と Custom 選択するところがあるがとりあえず Standard
  • License Agreement の後、そこそこ長いダウンロードとインストールがある

プロジェクト作成&エミュレータで動かしてみる

開発環境を入れたところで「で、どうしようかな。。」と一瞬途方に暮れたかけたのですが、iOSだとどうするかな、と想像してみて、何はともあれプロジェクト作成してエミュレータで空のプロジェクトを動作させてみるのがいいのかなと。

  • バージョンはとりあえず 4.4 (自分の Android がそうだったので)以上
  • テンプレートは違いがわからないので Blank
  • ActivityNameとかLayoutNameなどなど、それぞれがどこに影響するかわからないのでデフォルトのままで


で、上部のバーに Xcode の Build & Run ボタンと同じようなのがあったので、



押してみると、デバイス選択っぽいのが出てきて、無事エミュレータで動作しました!



ただ、ロック画面みたいなところから起動したので、Androidの操作自体がわからなくてちょっと困惑しましたが。。

書籍を入手

最初はWebでググるにも用語自体がわからなかったりするので、近所の書店に行って、参考書籍を購入しました。


つくるべきものは決まっているので、サンプルをチュートリアル的に進めるタイプのものではなく、「テーブルビュー的なのはどう実装するんだろう」という感じで逆引き的に調べられそうかどうか、を判断基準に、書店で目次を確認して選びました。

書籍1:Android StudioではじめるAndroidプログラミング入門

Android StudioではじめるAndroidプログラミング入門
掌田 津耶乃
秀和システム
売り上げランキング: 37,320


入門とありますが目次見るとわりと網羅的な感じがしたので購入しました。今こうやって見るとAmazonではなかなか厳しいレビューついてますね。。

書籍2:Androidアプリ開発逆引きレシピ (PROGRAMMER’S RECiPE)

Androidアプリ開発逆引きレシピ (PROGRAMMER’S RECiPE)
株式会社Re:Kayo-System
翔泳社
売り上げランキング: 94,035


逆引きレシピが欲しかったので最初に手に取りました。出版年月も新しめだし。

書籍3:Androidアプリ開発パーフェクトマスター―Android4/3/2.2完全対応 (Perfect Master)

Androidアプリ開発パーフェクトマスター―Android4/3/2.2完全対応 (Perfect Master)
金城 俊哉
秀和システム
売り上げランキング: 41,222


分厚すぎて絶対外に持ち出せないし、実用的じゃないんじゃないか。。と思いましたが、情報量は多いし、悩んでる時間がもったいないので購入。

UIImageView的なものを置いてみる

画像リソースの実態は、(フォルダ名の一般名称がわからないので自分の現在のプロジェクトで言うと)

MyFirstApplication/app/src/main/res

にあります。


が、画像の追加はプロジェクトウィンドウ内にある「drawable-mdpi」フォルダにドラッグ&ドロップするとのこと。(書籍1からの情報)


UIImageView的なUIコンポーネントは、Widgetsのところに「ImageView」というそのものズバリなものがあったので、それを配置。


画像の入れ方がよくわからなかったのですが(書籍1ではプログラムから入れていた)、ImageView を IB 的なところでダブルクリックしたら src 画像を選ぶダイアログが出てきて、無事 drawable-mdpi に入れたものを選択することができました。



UIViewContentMode的なプロパティと、このレイアウト周りがわからないけど、そのあたりは後回しで。。

ボタンを置いてみる

ボタンを押したらコンソールにログをはく、的なことをやってみます。

  • Widgetsに「Button」というのがあるので、それを配置
  • 右にあるプロパティウィンドウから、「onClick」を探して、「button_onClick」と入力
  • MainActivity.javaを開き、ボタンクリックのイベントハンドラを実装
public void button_onClick(View view) {
    Log.d("MyApp", "view:" + view);
}

(記念すべき初めてのコード!)


ボタンまわりは書籍1を、Log の書き方はググって下記記事を参考にしました。


で、これだとエラーが出たので、import を追記

import android.view.View;
import android.util.Log;

(使用クラスごとに必要なのか・・・!?そんなわけないと思うけど、おいおい学んでいきます。。)



ボタンを押すと、コンソールに次のように出力されました。

12-19 14:21:48.467 1911-1911/com.shu223.myfirstapplication D/MyApp﹕ view:android.widget.Button{9cf6a48 VFED..C. ...P.... 372,921-707,1065 #7f080041 app:id/button}

画面遷移

次は UINavigationController の push / pop 的な画面遷移を・・・と思ったのですが、買った書籍(総ページ数約2000!)を調べてみても見当たらず・・・*1


ググるとすぐに出てきました。

インテントは他のアプリと連携するときに用いるもので、他のアプリ(例えばブラウザアプリ、メールアプリ等)を起動することができます。


同じアプリでも他の画面 (Activity) クラスを起動する場合には、インテントを使用します。

そうなんですね、カルチャーショック。。


それはさておき、

画面の遷移は, 元画面(メイン画面)のアクティビティから次画面(サブ画面)のアクティビティを起動することで実現できる.

とのことなので、

  • メニューから新規アクティビティを作成
    • [File] > [New] > [Activity]
    • SubActivityと命名
  • SubActivity に Button を置き、onClick プロパティに button_onClick と入力
  • SubActivity.java に以下のコード(と必要なimport)を追記
public void button_onClick(View view) {

    Log.d("MyApp", "onClick:" + view);

    Intent intent = new Intent(SubActivity.this, MainActivity.class);
    startActivity(intent);
}
  • MainActivity.java のボタンのイベントハンドラを以下のように修正
public void button_onClick(View view) {

    Log.d("MyApp", "onClick:" + view);

    Intent intent = new Intent(MainActivity.this, SubActivity.class);
    startActivity(intent);
}


上記記事では AndroidManifest.xml へ SubActivity の定義を追加する必要がある、とありますが、自動的に追加されてました。


テーブルビュー的なものを実装する

iOS でいう UITableView は、Android では「リストビュー」と呼ぶそうです。で、「アダプター」なるものが UITableViewDataSource 的役割をするっぽい(いや、それ以前の、単なる配列かも。。)

  • Containers にある「ListView」を配置
  • SubActivity.java の onCreate メソッド内に、以下のコードを追記
ListView listView = (ListView)findViewById(R.id.listView);
ListAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_dropdown_item_1line, DAYS);
listView.setAdapter(adapter);
  • 同じく SubActivity.java に、以下のコードを追記(static な配列を生成)
private static final String[] DAYS = new String[] {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};

以上、「書籍2」の写経です。正直なところ、`R.id.listView` の `R` とか、`android.R.layout.simple_dropdown_item_1line` が何なのかとか全然わかっておりません。。



ちなみに コードから IBOutlet 的に UI オブジェクトにアクセスするにはどうしたらいいのか と思ってましたが、上記のように `findViewById` メソッドで取得するのが定石のようです。iOSでいえば `getViewWithTag` ですね。id は自動補完で出てきますが、IB的な画面の Properties の「id」からも確認できます。

実機で動かしてみる

ここまで忘れてたのですが、せっかく Android 端末を買ったので、実機で動かしてみます。


実機の「デベロッパーモード」を有効にする必要があるとのこと。「書籍3」に全編スクショ付きで載ってたので、Androidの使い方がわからない自分でも簡単でした。

  • [設定] > [端末情報]
  • 「ビルド番号」のところを7回タップ(おもろいw) -> デベロッパーモードになる
  • [設定] > [開発者向けオプション] > [USBデバッグ] を有効にする


あとはエミュレータの代わりに、実機を選択するだけ。


CSVファイルの読み込み

書籍に見当たらなかったのでググりました。


新規クラスファイルの生成方法がわからなくてけっこう困ったのですが、

app/src/main/java の位置で [File] > [New] しないと、メニューに「Java Class」というのが出ないようです。さらに、「com.shu223.myfirstapplication」と書かれているディレクトリ(パッケージ?)の下に置くと、他のクラスから扱えるようになるようです。


あと「プロジェクトの一番上」という表現もよくわからなかったのですが、 app/src/main 直下に assets という名前でディレクトリを作成すればOKでした。

  • assets ディレクトリを作成
    • 直下に csv ファイルを置く
  • CSVParser クラスを作成
package com.shu223.myfirstapplication;

import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

/**
 * Created by shuichi on 14/12/19.
 */
public class CSVParser {

    public static void parse(Context context) {
        // AssetManagerの呼び出し
        AssetManager assetManager = context.getResources().getAssets();
        try {
            // CSVファイルの読み込み
            InputStream is = assetManager.open("params.csv");
            InputStreamReader inputStreamReader = new InputStreamReader(is);
            BufferedReader bufferReader = new BufferedReader(inputStreamReader);
            String line = "";
            while ((line = bufferReader.readLine()) != null) {
                // 各行が","で区切られていて4つの項目があるとする
                StringTokenizer st = new StringTokenizer(line, ",");
                String first = st.nextToken();
                String second = st.nextToken();
                String third = st.nextToken();
                String fourth = st.nextToken();

                Log.d("MyApp", "Parsed:" + first + second + third + fourth);
            }
            bufferReader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  • SubActivity から呼んでみる
CSVParser parser = new CSVParser();
Context context = getApplicationContext();
parser.parse(context);

Context についてはググって下記記事を参考にしました。


実行結果については省略。

ボタンに応じてクリックイベントの処理を分岐させる

MainActivity にボタンを2つ置いて、iOS でいう tag で判別して次に遷移する画面を切り替える、みたいなことをやるには、id プロパティを使用するようです。

  • MainActivity.xml にボタンをもう1つ追加
  • 2つのボタンの id をそれぞれ、「button1」「button2」とする
  • MainActivity.java のイベントハンドラの実装を次のように変更する
public void button_onClick(View view) {

    switch (view.getId()) {
        case R.id.button1:
        {
            Intent intent = new Intent(MainActivity.this, SubActivity.class);
            startActivity(intent);
            break;
        }
        case R.id.button2: {
            Intent intent = new Intent(MainActivity.this, BLEActivity.class);
            startActivity(intent);
            break;
        }
    }
}


参考:クリックイベントに応答する - Android 開発入門

プロジェクトの API LEVEL を変更する

「APIレベル」って初耳でそもそも何なのかわかってないのですが、BLE 関連のコードを書き始めると、巷に情報がよく出ているサンプルコードの多くは `startLeScan`、 `stopLeScan` という BluetoothAdapter クラスのメソッドを使っていて、どうやらこれらは API LEVEL 21 で deprecated になっているらしい。


で、仕方なく推奨されている新しい方を使おうと思ってみたものの、付け焼き刃すぎて、サンプルがないとよくわからない。。


APIレベルってそもそも何なの?と調べてみると、

21とか全然新しすぎる模様。iOS でいえば 8 の API を使ってたようなものか。


で、どこで API レベルが設定されていて、どう変更するのが適切なのかなと調べてみると、build.gradle というファイルを更新する、と書いてあったのですが、

Android Studio の [File] > [Project Structure] > [Flavors] からも変更できました。


ここで「Target Sdk Version」を 19 に変えて、「Sync Project with Gradle Files」ボタン押下、リビルドしました。


結果・・・deprecated なメソッドの警告はなくなりませんでした。。


で、もう一度 [Project Structure] から、[Properties] の 「Comple Sdk Version」 にマニュアルで 19 といれてみたところ、SDKのインストールが始まって、なんやかんやあって、無事「API LEVEL 21 では deprecated だけど、それより下のバージョンでは OKなメソッド」の警告が消えました。。

BLEを利用する

BLE(Bluetooth Low Energy)については参考書籍3冊とも一切記載なし。


が、ググるといろいろと参考記事が見つかりました。


実際にやってみると、deprecated 問題(前項参照)とかいろいろ大変だったのですが、下記手順でいけました。

<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
  • メンバ変数などの追加
private static final long SCAN_PERIOD = 10000;
private BluetoothAdapter mBluetoothAdapter;
private Handler mHandler;
static final int REQUEST_ENABLE_BT = 0;
  • 諸々初期化処理(onCreateにて)
BluetoothManager manager = (BluetoothManager)getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = manager.getAdapter();

if (mBluetoothAdapter == null || !mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

mHandler = new Handler();
  • スキャン開始ボタンのイベントハンドラ
public void onClickScan(View v) {

    Log.d("MyApp", "onClickScan");

    // 10秒後にスキャン停止
    mHandler.postDelayed(new Runnable() {
        @Override
        public void run() {
            mBluetoothAdapter.stopLeScan(mLeScanCallback);
        }
    }, SCAN_PERIOD);

    // スキャン開始
    mBluetoothAdapter.startLeScan(mLeScanCallback);
}
  • ペリフェラルが見つかると呼ばれるコールバック
private BluetoothAdapter.LeScanCallback mLeScanCallback = new BluetoothAdapter.LeScanCallback() {
    @Override
    public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
        // 見つかったペリフェラル情報をログ出力
    }
};


実行してみると、無事部屋にあるペリフェラルデバイスが見つかってました。

12-19 19:38:38.578    2287-2299/com.shu223.myfirstapplication D/BLEActivity&#65109; name=Kontakt, bondStatus=10, address=EE:FB:3B:6C:B8:BE, type2, uuids=
12-19 19:38:38.768    2287-2300/com.shu223.myfirstapplication D/BLEActivity&#65109; name=null, bondStatus=10, address=59:F9:0E:E7:AD:41, type0, uuids=
12-19 19:38:38.958    2287-2299/com.shu223.myfirstapplication D/BLEActivity&#65109; name=null, bondStatus=10, address=59:F9:0E:E7:AD:41, type0, uuids=
12-19 19:38:39.078    2287-2300/com.shu223.myfirstapplication D/BLEActivity&#65109; name=Kontakt, bondStatus=10, address=EE:FB:3B:6C:B8:BE, type2, uuids=
12-19 19:38:39.128    2287-2299/com.shu223.myfirstapplication D/BLEActivity&#65109; name=null, bondStatus=10, address=59:F9:0E:E7:AD:41, type0, uuids=
12-19 19:38:39.318    2287-2300/com.shu223.myfirstapplication D/BLEActivity&#65109; name=null, bondStatus=10, address=59:F9:0E:E7:AD:41, type0, uuids=
12-19 19:38:39.498    2287-2299/com.shu223.myfirstapplication D/BLEActivity&#65109; name=null, bondStatus=10, address=59:F9:0E:E7:AD:41, type0, uuids=

(iOS のセントラルマネージャのように、重複するペリフェラルについてはコールバックをまとめる、ということはやってくれない

アニメーション

PulsingHalo的なのを実装したくて着手していたのですが、所用につき今日はここまで。*2

所感

Android Studio という専用 IDE があるし、エミュレータもまぁ確かに遅いけど全然アリだし、関連書籍は充実してるし、ググれば大量に開発情報が出てくるしで、開発環境としては超快適だと思いました。iOSと比べてどうの、という不満は全くなし


ただ、やはり Android は、API群(SDK)がまるっきり違う上に開発言語や IDE も違うので、思った以上に「覚え直し」感がありました。。ひとつのアプリをプロダクト品質で出すまでにはまだまだ先が遠い印象。。


最近始めて Mac OS X アプリをつくったときは新しい世界が切り開かれる感じがしてワクワクしたのですが、やはり「モバイル端末でのアプリ開発」という似ている分野では自分はあまり新鮮味を感じられないのかなと。(ObjC->Swiftへのコンバートにもあまり興味が無いように)


そんなわけで、1日やってみた後の所感としては「おとなしく iOS の世界に戻ろう」という気持ちになっております。。


*1:あとでわかってみれば、載っていました。が、そもそもインテント、アクティビティという概念を知ってないと目次からは辿りつけない。。

*2:この後の予定としては、「レイアウト」「ボタン等の見た目のカスタマイズ」「デバッグ」などなど予定していました。