2015年11月20日金曜日

9-patch image Validation

作成された(納品された)素材の 9-Patch PNG は、壊れていることが多い。

壊れているとビルドができません。
-----------------------------------------------------------------------------------------------------
/hogeproject/res/drawable-mdpi/btn_on.9.png
Error:No marked region found along edge.
       Found along left edge.
/hogeproject/res/drawable-xxhdpi/btn_on.9.png
Error:No marked region found along edge.
Error:Error: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/android-sdk/build-tools/21.1.2/aapt'' finished with non-zero exit value 42
:beetv:mergeDmyDebugResources FAILED
Error:Execution failed for task ':hogeproject:mergeDmyDebugResources'.

> /hogeproject/res/drawable-xxhdpi/btn_on.9.png: Error: com.android.ide.common.process.ProcessException: org.gradle.process.internal.ExecException: Process 'command '/Applications/android-sdk/build-tools/21.1.2/aapt'' finished with non-zero exit value 42
Information:BUILD FAILED
-----------------------------------------------------------------------------------------------------

ビルドができなくなると開発に支障をきたすので、プロジェクトに取り込む前に検査を行い、エラーがある場合は作成元へフィードバックする。

aapt コマンドで精査することができます。

-----------------------------------------------------------------------------------------------------
$ aapt
...
 aapt c[runch] [-v] -S resource-sources ... -C output-folder ...
   Do PNG preprocessing on one or several resource folders
   and store the results in the output folder.

 aapt s[ingleCrunch] [-v] -i input-file -o outputfile
   Do PNG preprocessing on a single file.

...
-----------------------------------------------------------------------------------------------------

$ aapt c -S res -C /var/tmp
Crunching PNG Files in source dir: res
To destination dir: /var/tmp
ERROR: 9-patch image res/drawable-hdpi/btn_off.9.png malformed.
       No marked region found along edge.
       Found along left edge.
ERROR: 9-patch image res/drawable-hdpi/btn_on.9.png malformed.
       No marked region found along edge.
       Found along left edge.

壊れているファイルはERRORが発生します。

このテキストを作成元へ送りつけましょう...

2015年8月20日木曜日

Androidのメソッド数が65kを超える犯人はPlay Services!!

Androidのメソッド数が65kを超える犯人はPlay Servicesであることは周知であるとおもいますが、その対応の方法

Jake Wharton神 がブログに記載してます!!!

Play Services 5.0 Is A Monolith Abomination - Jake Wharton http://jakewharton.com/play-services-is-a-monolith/

Google Play Services に導入手順が記載されてますね。

Setting Up Google Play Services  |  Google APIs for Android  |  Google Developers https://developers.google.com/android/guides/setup

以下のように全て指定してしまうと大変なメソッド数になるので、
compile 'com.google.android.gms:play-services:7.5.0'
必要な機能だけ指定するといいようです。
compile 'com.google.android.gms:play-services-cast:7.8.0'
compile 'com.google.android.gms:play-services-gcm:7.8.0'
わたしのアプリでは、CastとGCMだけにしています。

2015年7月31日金曜日

FragmentのからActivity/ParentFragmentへのEvent通知

FragmentのからActivity/ParentFragmentへのCallbackにInterfaceの定義とアタッチが面倒くさい!

  • 専用Callback
    • Activity/ParentFragmentでCallback Interface実装
    • FragmentでCallbackの登録
      • onAttachでActivityをCallback Interfaceにキャストして登録
      • getParentFragmenでCallback Interfaceにキャストして登録
        • Fragment側で親を意識しなければならない!!
    • 個々のCallback Interfaceの設計と実装
      • 複数のFragmentの数だけCallbackが必要!!
      • タイプセーフティ?
  • 汎用Callback
    • Activity/ParentFragmentでCallback Interface実装
    • FragmentでCallbackの登録
      • onAttachでActivityをCallback Interfaceにキャストして登録
      • getParentFragmenでCallback Interfaceにキャストして登録
        • Fragment側で親を意識しなければならない!!
    • Callback Interfaceの実装は一つ
      • 汎用化の為、Bundleにオブジェクト格納
        • どのFragmentからのCallbackであるか識別が必要!!
        • Bundleからキー指定してイベント通知内容を取得
  • LocalBroadcastManager
    • Activity/ParentFragmentでIntentFilterを指定してレシーバーを登録
    • FragmentからIntentをブロードキャスト
    • 汎用化の為、IntentのBundleにオブジェクト格納
      • どのFragmentからのCallbackであるか識別が必要!!
      • Bundleからキー指定してイベント通知内容を取得
    • 必ずMainスレッドメッセージの送受信
  • Event Bus (Otto)
    • Activity/ParentFragmentでBusを登録
    • FragmentでBusを登録
    • Activity/ParentFragmentでSubscribeメソッド実装
    • FragmentでProduceメソッドを実装
    • ProduceとSubscribeのインターフェースはPOJOのEventオブジェクト
    • Eventオブジェクトなのでタイプセーフティ
    • デフォルトではMainスレッド以外ではメッセージ送信(Produce)は出来ない。
      • Busインスタンス作成時にThreadEnforcer.ANYを指定すれば、他のスレッドからメッセージ送信できる。
      • しかし、Subscribeメソッドがメッセージ送信時スレッドで実行されてしまいスレッド管理が煩雑
      • runOnUiThread でMainスレッドで実行させる仕組みが必要
    • 何処のクラスからでもメッセージ送信(Produce)で来てしまうので、煩雑化する。
      • DialogFragmentのAlertDialogのOnClickCallbackからActivityに通知できる...
      • 意図しない箇所でSubscribeされる。


    android - LocalBroadcastManager vs using callbacks - Stack Overflow http://stackoverflow.com/questions/13948720/localbroadcastmanager-vs-using-callbacks

    Otto square.github.io/otto/
    Does Otto use weak references to store objects passed to register()?
    https://plus.google.com/101050435280754106754/posts/PGygiXURaTW

    EventBus - torutkの日記 http://d.hatena.ne.jp/torutk/20110407/p1

    2014年10月14日火曜日

    Android Gradle で Nativeライブラリ(.so) を含める方法

    Android Gradle Plugin 0.7.2 からサポートされてた O(≧▽≦)O

    StackOverFlowには、自前タスクを作成する回答ばかりヒットしますが、0.7.2 からサポートされてました!!

    New Build System - Android Tools Project Site http://tools.android.com/tech-docs/new-build-system
    0.7.2

    Note: 0.7.2 requires Java7. This is a mistake. Use 0.7.3 instead.
    • Fix issue with Proguard.
    • Add packagingOptions support in Library projects.
    • Solve issue with local jar when testing library projects.
    • Fix bug with variant.addJavaSourceFoldersToModel
    • Add jniLibs folder to source sets for prebuilt .so files.
    • Lint fixes:
      • fix RTL detector
      • fix HTML report to have valid HTML
    New Samples: ndkJniLib, genFolderApi2

    src/main/jniLibs が標準の配置ディレクトリです。
    変更する場合は、

    sourceSets {
        main {
            jniLibs.srcDirs = ['jniLibs']
        }
    }

    とします。


    以上、覚え書き!

    2013年12月26日木曜日

    ActionBarActivityで finalになったonMenuItemSelectedのソースを追ってみた

    ActionBarActivity#onOptionsItemSelectedのソースを追ってみた。

    android.support.v7.app.ActionBarActivity ActionBarActivityDelegate mImpl; @Override public final boolean onMenuItemSelected(int featureId, android.view.MenuItem item) { if (mImpl.onMenuItemSelected(featureId, item)) { return true; } final ActionBar ab = getSupportActionBar(); if (item.getItemId() == android.R.id.home && ab != null && (ab.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) { return onSupportNavigateUp(); } return false; } 5行目で、ActionBarActivityDelegateのonMenuItemSelectedを呼び出している。
    ActionBarActivityDelegateを実装は幾つかのOSレベル毎に分かれているので、 その1つActionBarActivityDelegateBaseを見てみる。

    android.support.v7.app.ActionBarActivityDelegateBase @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { if (featureId == Window.FEATURE_OPTIONS_PANEL) { item = MenuWrapperFactory.createMenuItemWrapper(item); } return mActivity.superOnMenuItemSelected(featureId, item); } ActionBarActivityのsuperOnMenuItemSelectedを呼んでいる。

    android.support.v7.app.ActionBarActivity boolean superOnMenuItemSelected(int featureId, MenuItem menuItem) { return super.onMenuItemSelected(featureId, menuItem); } ActionBarActivityの親クラスFragmentActivityのonMenuItemSelectedを呼んでいる。

    android.support.v4.app.FragmentActivity /** * Dispatch context and options menu to fragments. */ @Override public boolean onMenuItemSelected(int featureId, MenuItem item) { if (super.onMenuItemSelected(featureId, item)) { return true; } switch (featureId) { case Window.FEATURE_OPTIONS_PANEL: return mFragments.dispatchOptionsItemSelected(item); case Window.FEATURE_CONTEXT_MENU: return mFragments.dispatchContextItemSelected(item); default: return false; } } 6行目の親クラスActivityActivityのonMenuItemSelectedを呼んでいる。
    メニューが処理されなかったら、Fragmentのメニューを呼び出している。
    • Window.FEATURE_OPTIONS_PANEL - ActionBarや昔のメニューパネル
    • Window.FEATURE_CONTEXT_MENU - ロングタップで表示されるコンテキストメニュー


    android.app.Activity public boolean onMenuItemSelected(int featureId, MenuItem item) { CharSequence titleCondensed = item.getTitleCondensed(); switch (featureId) { case Window.FEATURE_OPTIONS_PANEL: // Put event logging here so it gets called even if subclass // doesn't call through to superclass's implmeentation of each // of these methods below if(titleCondensed != null) { EventLog.writeEvent(50000, 0, titleCondensed.toString()); } if (onOptionsItemSelected(item)) { return true; } if (mFragments.dispatchOptionsItemSelected(item)) { return true; } if (item.getItemId() == android.R.id.home && mActionBar != null && (mActionBar.getDisplayOptions() & ActionBar.DISPLAY_HOME_AS_UP) != 0) { if (mParent == null) { return onNavigateUp(); } else { return mParent.onNavigateUpFromChild(this); } } return false; case Window.FEATURE_CONTEXT_MENU: if(titleCondensed != null) { EventLog.writeEvent(50000, 1, titleCondensed.toString()); } if (onContextItemSelected(item)) { return true; } return mFragments.dispatchContextItemSelected(item); default: return false; } } メニューは、12行目でonOptionsItemSelectedを呼び出し。 コンテキストメニューは、32行目でonContextItemSelectedを呼び出し。


    リファレンスのOnMenuItemSelectedの記述
    Default implementation of onMenuItemSelected(int, MenuItem) for activities. This calls through to the new onOptionsItemSelected(MenuItem) method for the FEATURE_OPTIONS_PANEL panel, so that subclasses of Activity don't need to deal with feature codes.

    この通り、onOptionsItemSelectedをオーバーライドしてメニューの処理を実装を行なうのが正しいようだ。

    コンテキストメニューはonContextItemSelectedをオーバーライドして処理を実装を行なうのが正しいようだ。


    Honycombから、Activity#onMenuItemSelectedにはFragmentやActionBarのメニュー処理が行なわれてるためオーバーライドは注意が必要。