ラベル Android の投稿を表示しています。 すべての投稿を表示
ラベル Android の投稿を表示しています。 すべての投稿を表示

2017年7月12日水曜日

Xamarinでアプリ開発して良かったこと悪かったこと

業務でXamarin.Formsを使い、iOSとAndroid向けに同じアプリを作るということをしました。

以前はiOSとAndroidの担当が数名ずつで2チームだったのですが、仕様や実装に関する知識が分断しやすく、結果的に実装の都合を互いに押しつけあうなど問題があったのでその解決をXamarinに求めたのです。(その後他にも色々対策はしましたが)

「どうだった」と聞かれがちなので、良かった点と悪かった点を書いておこうと思います。随時追記もするつもりです。

■どんな感じだったか
  • Xamarin.Formsをベースに、たとえばPageの切り替えアニメをiOSとAndroidで同様にするなど、独自のUIフレームワークを作りました。
  • UIが絡まなくてNative機能を使いまくるところはPCL+BindingLibraryな共通ライブラリを作り、UIからはあまりNativeを意識せず実装できるようにしました。
  • UI側でNative機能を使いたいときはDependencyServiceやRendererを使いました。
  • NuGetサーバやCI環境など色々必要になりました。

■良かった点
  • 仕様に関する知識が分断しづらくなりました。
    ある画面がiOSだとこういう仕様でAndroidだと違う仕様というのが激減しました。もちろんXamarin.Formsの中で実装が異なるので挙動が違うというのは良くありましたがたいていはRendererなどで回避・調整可能でした。
  • UIフレームワークやライブラリを使う「だけ」のNative分からない人も開発に参加できるようになりました。人の融通がしやすくなるという意味で良かったです。
  • GitやiOS、Macなど今まで「食わず嫌い」だったものが少し浸透しました。
    SubVersionで止まっている人にGitを使わせたり、AndroidやWindowsしか触ったことのない人に多少なりともiOSやMacを触らせることができました。
    GitはMergeコミットだらけになったり色々アレですが、そこは具体的な悪影響が出るまで我慢です。

■悪かった点
  • 開発環境を人数分揃えるのにかなりコストがかかりました(詳しくはココ)。
    XcodeやAndroid Studioだけなら無料なのに。
  • 実装に関する知識は分断したままでした。
    Nativeを分かる人が独自のUIフレームワークやライブラリを作る担当になり、画面を作る人は別となることが多かったです。結果iOS、Android、C#それぞれで担当部分しか触らないという人はそれなりに残りました。うまく分担できたという気もしますが、もう少し「誰でも触れる」状態が作れればと思います。ただMacやMSDN付きでないとやりづらくコストもより増えてしまうので敬遠されました。
  • 問題の原因を探る階層が増えました。
    例えばUI部品を思うように配置できなかったとき、Nativeならそれらの範囲を調べれば良いのですが、Xamarin.Formsの場合はそちらの挙動も見る必要があったりして手間を感じました。
  • なにかとiOSはズルいです。
    普通に作るとどうしてもiOSアプリの方が安定しています。処理中にホームボタンを押したりしたときもiOSはただサスペンドしている感じですが、Androidは動き続けてヌルポで死ぬとか。画像を読み込む速度もiOSの方が早いです。
    さらには独自ライブラリのほうで依存関係が増えたとしても、iOSの方は簡単に取り込めるのに、Androidはアプリ側でイチイチBinding Libraryを作成してjarを取り込まないとClassNotFoundExceptionになったり。Androidはやりづらさがありました。
  • Mac Agentがアテにならない
    Windows(Visual Studio)からMacへ接続してリモートビルドをかけるための仕組みですが、Visual Studioからのデバッグ実行などは頻繁に切れて失敗します。コマンド(msbuild)からなら割と安定しているのですが。
  • Xamarinの更新がドキドキ
    XcodeやAndroid Studioであれば更新しても問題にあうことはさほど頻繁ではなかったように思いますが、Xamarinは毎回心臓に悪いです。特に今回のXamarin.Android 7.3のようにGCやMonoが変わって実行時に原因不明でクラッシュするとかやめてほしいです。
  • 依存関係が難しい
    例えば今のプロジェクトはFormsの2.3.3.180を使っているのですが、2.3.4に上げるのは難しいかもしれません。
    というのもこれと合わせてXamarin.Android.Support.v4の23.3を使っていて、AndroidのBinding Libraryのほうも同じバージョンで、と揃えた状態で、これまたバージョンを上げるとどうなるか分からないというのがあります。ただモバイル開発は基本的にバージョン上げていかないとシンドイので、近いうちに、せーの、でやることになるのだと思います。

■その他

  • 計測していないのですが、コードを共通化できた部分はさほどないと思います。
    前述の通りBindingLibraryやRenderer、DependencyServiceなども結構書きましたし。

というわけで、Xamarinは(目的と手段とスキルと制限などとコストが折り合いつけば、わりと)いいぞ。

2016年10月4日火曜日

ファーウェイの端末はFLAG_ONGOING_EVENTやsetOngoingしても通知が消せる

最近HUAWEIのhonor 8をメイン端末にしました。

そこで気づいたのですが、愛用しているアラームアプリが通知領域からすぐに消えてしまっています。

アプリを起動すると通知が表示されるのですが、フリックすると消えてしまうのです。

あれ?と思って実際に作って試してみたのですがNotificationにFLAG_ONGOING_EVENTをセットしても消せました。

それならとNotificationCompat.BuilderでsetOngoing(true)してみましたが同様です。

もしかしてAndroid 6.0あたりで変わったのか?とも思いましたが、Galaxyなどでは普通に消せません。

ここで気づいたのですが「すべて消去(ごみ箱ボタン)」では消えません。
通知すべてがsetOngoingしてある場合はごみ箱も表示されません。

というわけで、とりあえずの結論としては「ファーウェイの端末だと手動で通知が消せてしまう」ようです。

なお手元のHuawei MediaPad T1 7.0(4.4.2)で通知を消そうとすると『この通知を消去すると、送信中のアプリケーションで例外が発生します。消去しますか?』という警告ダイアログが表示されました。

honor 8だと表示されないので、例外も出ていないのでしょうか。

2015年11月27日金曜日

build.gradleの依存ライブラリのバージョンが勝手に変更されてしまう

Android Studioでアプリ開発をしています。

アプリのbuild.gradleとライブラリのbuild.gradleでそれぞれdependenciesに

compile 'com.google.android.gms:play-services-maps:8.1.0'

と書いているのですが、気づくとアプリ側だけ

compile 'com.google.android.gms:play-services-maps:8.3.0'

と勝手に書き換わっています。

このままではそれぞれに含まれるバージョンが違うせいか実行しようとすると

AGPBI: {"kind":"simple","text":"com.android.dex.DexException: Multiple dex files define Lcom/google/android/gms/ads/identifier/AdvertisingIdClient$Info;","sources":[{}]}

というエラーが表示されてしまいます。

(adsで問題がでているのは、おそらくライブラリ側でanalyticsを依存に書いていて、それがadsに依存しているから)

これは最新版を使えという思し召しなのでしょうか・・・なぜ片方だけ・・・謎です。

古いアプリを移行してきたので当面はバージョンを変えたくないのですが。

2015年11月7日土曜日

[解決]Playストアで「サーバーからの情報の取得中にエラーが発生しました」が出るときの対策

先日、古いAndroidタブレットを初期化したら使い物にならなくなったことで大変困りました。

これが意外なことで解決したのでご報告します。

やったことはズバリ『「Google Play開発者サービス」を強制的に上書きインストール(更新)すること』です。

「Google Play開発者サービス」のアプリ本体(APK)を取得できさえすれば後は簡単です。

手順は以下の通り。


  1. WebブラウザでAPK Downloader(http://apps.evozi.com/apk-downloader/)を開く
  2. 先頭の入力欄を1回クリックする
  3. ↑で別のタブが開いたり別のページへ遷移したら戻る(ただの広告です)
  4. 再び入力欄をクリックして「com.google.android.gms」と入力する
  5. すぐ下の「Generate Download Link」を押す
  6. 「Click here to download com.google.android.gms now」ボタンが表示されたら押してAPKをダウンロードする
  7. なんとかしてAndroid端末へAPKファイルを転送する
  8. Android端末でAPKファイルを開いてインストールする

5まで実行したときの画面はこんな感じです。

7については、私はBluetoothで転送しました。
メールに添付したり、microSDカードなどを使ったりするのも良いと思います。

ページ内の「QR Code」の「View」を押すと別タブでQRコードが開くので、おそらくそこからダウンロードできるかと思います。Android端末にQRコードリーダが付いている場合これが一番簡単かもしれません。

「Google Play開発者サービス」さえ更新してしまえば、Playストアも普通に使えるようになりました。

お困りの方は、是非試してみてください。

2015年10月28日水曜日

[解決]60秒以上かかる定期処理にWakefulBroadcastReceiverは使えない

Androidで定期的にバックグラウンド処理する場合、従来はPARTIAL_WAKE_LOCKなWakeLockを使う必要がありました。

AlarmManagerでタイマーを仕掛け、タイマーで起こされたBroadcastReceiverがWakeLockでCPUをスリープしないようにしてからServiceを呼んで、Serviceでバックグラウンド処理をし、完了したらBroadcastReceiverが持っているWakeLockを解除してCPUがスリープできるようにする、という流れです。

ここでWakeLockが若干面倒なので処理を簡素化するためにできたのがWakefulBroadcastReceiverです。

公式のトレーニングにも説明が載っています。

WakefulBroadcastReceiverからstartWakefulService()でServiceを起動し、Serviceは処理が済んだらWakefulBroadcastReceiver.completeWakefulIntent()を呼ぶ。
その間はCPUがスリープしないと。

完璧です。

そう思って僕も早速これに置き換えましたが、どうもServiceの処理に時間がかかってしまいます。

もともと2分くらいなのですが、10分とか1時間とかかかっているケースもあります。ただ充電中(開発者オプションで画面付けっぱなし)は2分くらいで済んでいます。

どうも、CPUがスリープしてしまっているように思えます。が、そんなハズはとWakefulBroadcastReceiverのソースを読んでみたところ・・・

wl.acquire(60*1000);

60秒でタイムアウトだそうで。

今回のケースではアウトです。
本当にありがとうございました。

--- 20151029追記 ---

今回はWakefulBroadcastReceiverを取り込んで対処しました。
ソースを自分のプロジェクトにコピペしてpackageとタイムアウト値を変更して使いました。
Apache 2.0なライセンスなので、そこら辺はちゃんとしないといけないですね。

2015年10月27日火曜日

[解決]BOOT_COMPLETEDで起動しない機種があるときはintent-filterに誤りがあるかも

AndroidManifestにこう書いていたら、Zenfone5の電源投入時にonReceiveが呼ばれませんでした。

        <receiver android:name=".BootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </receiver>

カテゴリ指定を取ってこう書いたらonReceiveが呼ばれるようになりました。

        <receiver android:name=".BootReceiver">
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
            </intent-filter>
        </receiver>

公式のトレーニングにもそう書いてありますね。

しかも最初は呼ばれない(enabled="false")ようにして、ユーザが明示的にONしたときにPackageManager経由で有効にする、というのが正しいやり方のようです。

・・・というか、このやり方なら「電源投入時に起動する」という設定を覚えておく必要がないのですね。

2015年7月6日月曜日

[解決]古いAndroidタブレットを初期化したら使い物にならなくなった(追記あり)

(追記20151107)解決した方法はこちら




ガラケーを使っている母親がスマホに興味を持ったようです。

音声で検索とかできるのなら私にも使えるかな、と。

もともとメールは出来るので、まぁいけるかなと試しにタブレットを貸すことにしました。

ちょうど手元にLGのL-06CというAndroid 3.1なタブレットが眠っていたのでそれを渡そうと思い、リセット(初期化)したのが運の尽きでした。

二度とアプリをインストールできなくなってしまったのです。

一番最初にやった初期化はこのようにしました。
  1. 初期化する
  2. Wi-Fiを使えるようにする
  3. 母親用のGoogleアカウントを作成する
  4. 「マーケット」アプリを起動する
  5. 「マイアプリ」へ移動する →何も表示されない
  6. 再起動する
  7. 再び「マーケット」アプリの「マイアプリ」へ移動する →今度は表示された
  8. プリインストールされているアプリを更新しようとする
ここで「サーバーからの情報の取得中にエラーが発生しました。[RPC:S-7:AEC-7]」などと表示されてインストールに失敗します。

エラーメッセージでググって関連ありそうな作業(ストアアプリのデータ消去など)をしてみたり、何度も初期化しながら作業の順番を変えてみたり、使うアカウントを私の普段使いのにしてみたり、とにかく色々試したのですが、今のところ全滅です。

時々エラーメッセージ中の数字が変わるくらいです。

やはりAndroid 3.1はGoogleに見捨てられた子だということなのでしょうか・・・。

(追記20150708)

b-mobileのSIMがあったので挿してリセットからやり直してみましたが結果は変わらず。