Android Studio Flavorでやりたいことを切り替えるサンプル

Android
Android Studio1.3.1

Android StudioのFlavor設定で初心者がはまりやすい落とし穴をまじえながら解説します。

なんだかんだ言いながら、Android Studioを使い始めています。
Eclipseから移行すると、作法がいろいろ違うので、とまどいます。
♪とまーどい 学んで 汗を流して いつも何かに傷つきながら

さて、Android Studioで一番やりたかったのは、Gradleを利用して、よくある次のようなことをやりたかったのです。

たとえば、弊社で配布しているSmart動態管理というアプリには、請求書で支払っている顧客とGoogle Playからダウンロードしていただいているお客様がいます。(例なので、実際のアプリの動きとは多少違いますが)
次のように、今まではマニュアルで変更していました。

  1. 請求書を送るアプリは名称を「請求書版」とつけ、請求書サイトに接続する
  2. Google Playからダウンロードするアプリは名称から「請求書版」を外し、Google Playのサイトに接続する

それで、テスト版と、リリース版があり、それだけで4通りのアプリが必要になるわけです。
4通りというのは、次の4通りです。

  1. 請求書版のテストアプリ
  2. 請求書版のリリースアプリ
  3. Google Playのテストアプリ
  4. Google Playのリリースアプリ

で、このあたりがFlavorというものの設定によって、簡単にできます。

まずはFlavorを作っておきます。
時代は進み、build.gradleを編集しなくても、GUIで設定できるのです。
Flavorの作り方はFile→Project Structureで、モジュールに自分が設定したいモジュールが選択されているのを確認して、Flavorsのタブをクリックします。
プラスボタンをクリックして、とりあえず、名前だけを最低限設定します。
android_studio_flavor_new.png
invoice と google_play のフレーバーを作ります。

すると、build.gradleに次の一文ができています。

    productFlavors {
        invoice {
        }
        google_play {
        }
    }

で、肝心の内容の変更です。

いろんな方のサイトを拝見すると、ディレクトリの名前とFlavorの名前を合わせて配置する、とのことでした。
http://iti.hatenablog.jp/entry/2015/06/22/084303

Windows10で開発していますので、エクスプローラーを使って次のようなディレクトリ構成を作ります。

フレーバーを追加する前は、ソースファイルや言語ファイルなどは次のような構成です。

smart_location\src\main\smart\location\…この中にjavaのソースファイルがごちゃっと入っています
smart_location\res\values\strings.xml…もともとの言語ファイル

これに、srcの中にinvoiceというフォルダを作って、次のようにinvoice用の言語ファイルを作ります。
smart_location\src\invoice\res/values/strings.xml

それぞれ、ファイルの内容は次の通りです。

 smart_location\res\values\strings.xml
 これはデフォルトの言語設定
 
 <?xml version="1.0" encoding="utf-8"?>
 <resources>
    <string name="fetching">サーバーと通信中です。</string>
    <string name="app_name">Smart動態管理</string>
    <string name="btn_label">送信</string>
    <string name="back">戻る</string>
    …その他、書ききれないほどの言語設定
 smart_location\src\invoice\res/values/strings.xml
 これは請求書版のみに利用する言語設定なので、とりあえずアプリタイトルを変更するサンプル
 <?xml version="1.0" encoding="utf-8"?>
 <resources> 
    <string name="app_name">Smart動態管理 請求書版</string> 
 </resources>

で、ここまでは別にOKだったのですが、こっから先がつまづきました。
Android Studioのソースツリーを表示するペインに、invoiceディレクトリが出てこないんですよ。

android_studio_flavor2_new.png

上記の赤丸のように、srcのすぐ下にres/values/strings.xmlとなっている。
strings.xmlの中身は、確かにsmart_location\src\invoice\res/values/strings.xmlなのに…
なんで?解せぬ…。(´・ω・`)
と悩むことしきり。
Runしてみると、Stringファイルは確かにinvoice版が表示されているんですけどね。

で、ちょいちょい試していたら、理由がわかりました!

android_studio_flavor3_new.png

上記の赤丸のとこをクリックで、4つのフレーバーが変更できるようになるのですが、フレーバーのinvoiceを選択している場合は、親切にもinvoiceディレクトリを省略してくれるんですね!!

試しにフレーバーをgoogle_playDebugを選択したら、下記の図の上の赤丸部分のようにinvoice.res.values/strings.xmlと表示されるようになりました!

android_studio_flavor4_new.png

よかったよかったo(>▽<)o

Android StrictモードでANRなどをなるべくなくす

Android

Android StrictモードでANRなどをなるべくなくす

StrictModeとは、メインスレッド内で予期せぬディスクアクセスや、ネットワークアクセスなどにより時間がかかる処理などをしている場合、それを教えてくれます。より、サクサク動くアプリを作るための、開発者向けのクラスです。
つまり、これを使って時間がかかっている処理を暴けば、ANRが少なくできるであろう!というわけです。[huh]

http://developer.android.com/reference/android/os/StrictMode.html

これを実装してみます。
単一の画面で調べたい場合は、その画面のActivityのOnCreateに書けばよいのですが、あらゆる画面で調べたい!と欲張りな私は、以前にGoogle Playで配布しないアプリにクラッシュログ機能を実装するで書きました、MyApplication.javaの中で実装します。

 public class MyApplication extends Application {
 
    @Override
    public void onCreate() {
      // The following line triggers the initialization of ACRA
      super.onCreate();
      ACRA.init(this);
      
      Log.d("Application", "MyApplication");    
      
      if (AppConstants.IS_STRICT_ON) {
          StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                  .detectDiskReads()
                  .detectDiskWrites()
                  .detectNetwork()   // or .detectAll() for all detectable problems
                  .penaltyLog()
                  .build());
          StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                  .detectLeakedSqlLiteObjects()
                  .detectLeakedClosableObjects()
                  .penaltyLog()
                  .penaltyDeath()
                  .build());
      }
 
      
    }
    
 }

アプリを実行します。
すると、とある処理で、アプリが落ち、LogCatにエラーが表示されます。

 06-25 13:10:43.585: E/StrictMode(21309): Finalizing a Cursor that has not been deactivated or closed. 
                           database = /data/data/hogehoge.appli/databases/hoge, table = null, query = select * from LocationTable
 06-25 13:10:43.585: E/StrictMode(21309): android.database.sqlite.DatabaseObjectNotClosedException: 
                  Application did not close the cursor or database object that was opened here
 06-25 13:10:43.585: E/StrictMode(21309): 	at android.database.sqlite.SQLiteCursor.<init>(SQLiteCursor.java:98)
 06-25 13:10:43.585: E/StrictMode(21309): 	at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:50)
 06-25 13:10:43.585: E/StrictMode(21309): 	at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1322)
 06-25 13:10:43.585: E/StrictMode(21309): 	at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1261)
 06-25 13:10:43.585: E/StrictMode(21309): 	at hogehoge.appli.helper.SqliteDataBaseHelper.getAllData(SqliteDataBaseHelper.java:163)

なぬっ
SQLiteのSELECTで使うカーソルが、閉じ忘れだそうです。

 Cursor c = db.rawQuery("select * from " + SqliteDataBaseHelper.DB_TABLE, null);
 //この後 c.close(); を実装しなければならなかったのに忘れていた

ふぅ~
見つかってよかった[smile]
と、このように便利なツールですね!!

ちなみに、

http://developer.android.com/reference/android/os/StrictMode.html

を読んで勉強になったのが、Androidのファイルシステムは通常フラッシュメモリなので、I/Oが早いはずなのですが、他のプロセスと並行するためのメモリ割り当ては大変少ないので、他のプロセスと同時に書き込まれる場合はとっても遅くなる場合があるらしいです。

Android Spinnerにデフォルトを設定

Android

Android Spinnerにデフォルトを設定

スピナーにデフォルトの値を設定する方法です。

 prefectureSpinner = (Spinner) findViewById(R.id.prefecture);  
 
 if(tempo_prefecture != null){
 
     ArrayAdapter myAdap = (ArrayAdapter) prefectureSpinner.getAdapter();
     int spinnerPosition = myAdap.getPosition(tempo_prefecture);
     prefectureSpinner.setSelection(spinnerPosition);
   		    
 } 

下記を参考にさせて頂きました。
http://stackoverflow.com/questions/2390102/how-to-set-selected-item-of-spinner-by-value-not-by-position

Android Spinnerで、選択された順番を取得する

Android

Android Spinnerで、選択された順番を取得する

SpinnerとはHTMLで言うリストみたいなものです。
valueを取得するには、↓

 String item = (String) spinner.getSelectedItem();

のようにすればよいのですが、keyを取得するのは難しいです。
苦肉の策で、リストの順番だけは、下記のように取得できるので、これを使うことにしました。

 String item = (String) spinner.getSelectedItemId();