読者です 読者をやめる 読者になる 読者になる

のらぬこの日常を描く

ノージャンルのお役立ち情報やアニメとゲームの話、ソフトウェア開発に関する話などを中心としたブログです。

androidアプリのCI環境(自動ビルドとテスト版自動配信)を全部無料で整える 其の壱

どうも、のらぬこです。

今回の話は、個人開発しているandroidアプリのソース管理(gitホスティングサービス)、自動ビルド、自動テスト配信環境を全部無料で整えたというお話の第一弾(前半)になっております。

この記事では、これらのことをどのように実現したかという観点で、ソース管理の話から自動ビルド環境を構築したところまでの話をします。

利用したサービスは以下の3つです。

  • BitBucket
  • Bitrise
    • モバイルアプリに特化したCIサービスです。androidiOSアプリ、Unityに対応しており、無料プランでも結構使えます。
  • fabric(crashlytics beta配信)
    • リリース済アプリのクラッシュ情報を自動で収集、報告してくれる等のサービスが提供されています。
    • 今回は、その中の一つ、テスト版をチームメンバーに自動配信するようなサービスを利用します。

なお、今回の話は、Bitbucketアカウント登録から、Bitriseを利用した自動ビルド環境構築までの話になります。

さて、僕はandroidの個人開発を始めた当初から、gitホスティングサービスとしてマイクロソフトが提供しているVisual Studio Team Serviceを利用していました。

ちなみに、Visual Studio Team Serviceはソース管理(gitとMS製のバージョン管理システムに対応)と自動ビルド&デプロイ、Issue管理をひとまとめにしたようなWeb上のサービスになります。

5人以上のチームで使用する場合には有償になりますが、メンバーがそれ未満の場合にはサービス利用は基本無料となり、例えば、プライベートgitリポジトリなども数や容量の制限なく自由にいくつでも作成することができる(逆に、パブリックなリポジトリは作成できない)等、ちょっとほかには見られないような特徴も持っています。

また、.net系のアプリやwebサービスはもちろん、nodejsなwebアプリ、mavenやgradle管理のJVM系言語、androidxcodeにも対応したCI環境も用意されており、さらにazureへの自動デプロイもできたりして*1、お金はかけたくないけどいろいろ使ってみたい等、用途によってはとても良いサービスだと思います。

Visual Studio Team Serviceについては、以前紹介記事も書いているので、ご興味ありましたら合わせてごらんください。 noranuk0.hatenablog.com

ももともとはgitリポジトリホスティングサービスとしてしか活用していなかったのですが、開発中Androidアプリの自動ビルドもそのうちやりたいなーとは思っていました。

が、Visual Studio Team Serviceで用意されていたAndroidの自動ビルド環境は割と残念な出来栄えで、自動ビルドには成功しませんでした。

どの辺が残念かというと、環境がかなり古く、例えば用意されているsdkバージョンに未だにandroid-19が使われていました。

しかもおそらくアクセス権限関係?でSDKのアップデートもできない状態で、どうにも使用するには堪えないような代物だったんです。

半ばあきらめていたのですが、gitリポジトリにはBitbucketを使い、リポジトリへのpushをトリガーとした自動ビルドにはbitriseを、ビルド済apkの自動配信にはfabricを使えば、僕のやりたいことは全部無料で実現できそうだと分かり、早速試してみました。

結果、自動ビルド&自動配信まで無事に行うことができるようになりました。

前置きが長くなってしまいましたが、これから手順等の紹介をします。

gitリポジトリの移行

まずは、BitBucketのアカウントを作ります。

bitbucket.org

BitBucketもVSTSと同様に少人数チームであればプライベートリポジトリも割と無制限に作れるようです。

Bitbucketのアカウント作成と他のリポジトリからのソースのインポートについては特に説明の必要ないかと思います。

どうしても何かを見ながらやりたい方はこの辺(↓)でよさげなページを探してください。
bitbucket アカウント作成 - Google 検索

gitはtortoiseGitかSourceTree経由で使っててコマンドとかよく解んないっていう僕みたいな人は、Bitbucketには他のgitホスティングサービスのリポジトリを丸ごとインポートできるUIを持っているので、そいつを使えば移行も簡単です。

ただしその場合、移行後にローカルリポジトリのoriginの向き先を変えるなり、BitBucketからcloneし直すなりしないと後でちょっと残念な気持ちになるかもしれません。

Bitriseで自動ビルド環境を準備する

Bitriseは、iOS, android, unityプロジェクトのCI環境を提供しているモバイルアプリに特化したCIサービスです。

bitrise.io

CIによる自動ビルドも、月に200ビルドまでは無料で利用することができるため、個人利用であれば無料枠内で十分に活用できると思います。

ソースコードは、対応するgitホスティングサービスと連携し、そこから取得ことになります。

対応しているgitホスティングサービスは、GitHub, BitBucket, GitLabの3つです。。

実は、この記事を書いていて気が付いたのですが、任意のリポジトリを指定することもできたようです*2

f:id:noranuk0:20170420001358p:plain

ビルド手順は、workflowと呼ばれるWeb上のツールで設定します。

なお、基本的なテンプレートは最初から用意されており、ソース取得元のgitリポジトリを指定すれば、中身をみてAndroidiOS、Unityの設定を自動生成してくれるため、特にカスタマイズが必要なければそのまますぐに使えます。

f:id:noranuk0:20170420001510p:plain

ただし、ビルド結果やビルド済みファイルのダウンロードURLが記入されたメールの配信などは、デフォルトでは行ってくれません。

ビルド結果とダウンロードURLが書かれたメール通知やSlack通知、今回僕が行ったようなfabricを利用したテスト版配信等をやりたい場合は、多少の設定追加、変更などのカスタマイズが必要です。

なお、カスタマイズする場合も、難しいことをする必要は全然ありません。

例えば、特定のURLからファイルをダウンロードしたり、ビルド結果をslackに通知するためのひな型は最初から用意されており、使いたいコンポネントを選択し、URLやAPIキーなどの情報を入力するだけで使えるようになっています。

f:id:noranuk0:20170423164530p:plain

アプリの署名については、少し注意が必要です。

androidアプリの場合、apkを署名するための鍵が必要になります。

デバッグ用の鍵はbitrise側で用意されているため、特に意識せずともデバッグ用に署名されたapkをビルドしてくれます。しかし、どうも毎回必ず同じ鍵が使用されるわけではないようで、更新版のインストールに失敗してしまうことがありました。

したがって、デバッグビルド用の鍵も自分で用意したほうが良いかもしれません。

独自の(自分で用意した)鍵を使いたい場合も、bitriseが鍵の保存場所を提供してくれているため、別途サーバを用意して鍵をWebからダウンロードできるようにしておく必要も、gitリポジトリに鍵をコミットしておく必要もありません。

僕の場合、ローカル開発環境で使用している鍵と同じものを使いたかったので、bitriseが用意してくれたアップロード場所に、普段開発PCで使用している鍵を登録しています。 qiita.com

無論、リリース向けの鍵についても、同様にbitriseのサーバにアップロードして、リリース版ビルド時にその鍵を参照するように設定しておけば、独自の鍵管理の仕組みを考える必要がなくなります。

ところで、自動ビルドの実行タイミングについてですが、初期設定では全てのブランチへのpush、プルリクをトリガーとして、すべて同じ手順でビルドが実行されるようになっています。

例えば、developへのpush時はdebug版、masterへのpushでリリース版のapkをビルドするような場合には、ワークフローを複数作成し、エディター画面の「trigger pattern」と書かれたリンクから、トリガーの種類と使用するワークフローを関連付けする必要があります。

f:id:noranuk0:20170423173414p:plain

トリガーには、特定ブランチへのpush時、特定ブランチへのプルリク時、特定パターンのタグがpushされた時を指定することができるようになっています。

f:id:noranuk0:20170423173720p:plain

これらの設定内容は、最終的にはymlに落ちますが、ymlでしか書けないような設定は(おそらく)ありません。

全てGUIツールで設定が可能になっています。

自分が使っているymlの設定はこんな感じです。 developへのpushでデバッグ版、masterへのpushでリリース版がビルドされ、ビルド成功時にはfabric betaへの自動配信までやってくれます。

---
format_version: 1.3.1
default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git
trigger_map:
- push_branch: master
  workflow: master
- push_branch: develop
  workflow: develop
workflows:
  master:
    steps:
    - activate-ssh-key@3.1.1:
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone: {}
    - script@1.1.3:
        title: Do anything with Script step
    - install-missing-android-tools@1.0.1: {}
    - gradle-runner:
        inputs:
        - gradle_task: "$GRADLE_TASK_PREFIX$POSTFIX_RELEASE $CRASHLYTICS_UPLOAD_DISTRIBUTION_PREFIX$POSTFIX_RELEASE"
    - deploy-to-bitrise-io@1.2.9:
        inputs:
        - notify_user_groups: none
        - notify_email_list: "$EMAIL_NOTIFICATION"
    envs:
    - KEY: ''
      opts:
        is_expand: true
  develop:
    steps:
    - activate-ssh-key@3.1.1:
        is_always_run: true
        run_if: '{{getenv "SSH_RSA_PRIVATE_KEY" | ne ""}}'
    - git-clone:
        is_always_run: true
    - file-downloader@0.9.1:
        is_always_run: true
        inputs:
        - source: "$BITRISEIO_MYSYNCER_DEBUG_YMEWHJSL_URL"
        - destination: app/signingConfigs/debug.keystore
    - script@1.1.3:
        title: Do anything with Script step
        is_always_run: true
    - install-missing-android-tools@1.0.1:
        is_always_run: true
    - gradle-runner:
        is_always_run: true
        inputs:
        - gradle_task: "$GRADLE_TASK_PREFIX$POSTFIX_DEBUG $CRASHLYTICS_UPLOAD_DISTRIBUTION_PREFIX$POSTFIX_DEBUG"
    - deploy-to-bitrise-io@1.2.9:
        inputs:
        - notify_email_list: "$EMAIL_NOTIFICATION"
app:
  envs:
  - opts:
      is_expand: false
    GRADLE_BUILD_FILE_PATH: build.gradle
  - opts:
      is_expand: false
    GRADLE_TASK_PREFIX: assemble
  - opts:
      is_expand: false
    GRADLEW_PATH: "./gradlew"
  - opts:
      is_expand: true
    CRASHLYTICS_UPLOAD_DISTRIBUTION_PREFIX: crashlyticsUploadDistribution
  - opts:
      is_expand: true
    POSTFIX_DEBUG: Debug
  - opts:
      is_expand: true
    POSTFIX_RELEASE: RELEASE
  - opts:
      is_expand: true
    EMAIL_NOTIFICATION: ********@gmail.com

fabricbeta配信の話まで書こうと思っていたのですが、そろそろ眠くなってきたので続きはまた今度、ということで。

ちょっと中途半端で終わっていますが、お読みいただいてありがとうございました。

PVがあろうがなかろうが、続編も近日中に公開予定です。

*1:ビルド時の制限はある

*2:VSTSnoからBitBucketに移行する必要実はなかった

予想以上の便利さを提供してくれたドラム式洗濯機 ~メリットと注意点を語ってみる~

僕がドラム式洗濯機を選んだ訳

掃除、洗濯、料理、食後の後片付けという家事の中で、最も苦行なのは洗濯だと思っています。

洗濯機に洗濯物を入れてスイッチを入れた時点で、「1時間後に洗濯が終わった洗濯物を干さなければならない」という行動的制制約が課せられます。特に、外出が禁止される(洗濯が終わるタイミングで在宅していなければならない)というのが個人的には一番の心理的負担です。

さらには、干した後も夕方or翌朝に干した洗濯物を取り込むという行動を頭の片隅に残しておく必要があります。

万が一取り込みを忘れた場合、晴れ続きであればいいのですが、そのまま仕事に出かけて昼から雨ったりするととてもとても残念な気持ちになります。

そんな悩みを軽減してくれるかもしれないドラム式洗濯機、前々からほしかったのですが、価格が20万とかするのがネックでなかなか手を出せずにいました。

今年のお正月、三が日の最後の日、お正月特価セールに期待して、ヨドバシAkibaに足を運んでみました。すると、予定通りというかなんというか、家電売り場の各種家電も結構な値引き価格で売られておりました。しかも、ドラム式洗濯機に至っては、お正月特価で通常価格よりも3万~5万ほどのお値引き価格で販売れています。

「迷っている理由が値段なら買え、買う理由が値段ならやめろ」っていう格言もありますが、あるといろいろ捗りそうではあるし、時間的制約から生まれるストレスが20万円で解消されるというのはメリットとしては十分と思うし、今使っている縦型の洗濯機もまとめ洗いするにはちょっと小さいし、小一時間ほどうろうろしつつ悩んだのですが、買い替える決断を致しました。

選ぶ時の注意点

ドラム式洗濯機使用中の友人に、選び方のコツを聞いてみたところ「設置サイズ」「搬入サイズ」「メンテナンス(フィルター掃除)のやりやすさ」かなー、というご神託をいただきました。

ドラム式洗濯機は、現在「日立」「パナ」「東芝」「シャープ」の4社から販売されています。

この中から選んでいくわけですが、まず搬入サイズに関しては、戸建て住まいなので廊下を通過できるサイズであれば無問題。

設置サイズについては、排水溝が奥の壁から65センチの位置にあるのがネックでしたが、最悪高さをかさ上げすればどうにかなると思ってたのであまり気にしていませんでした。ただ、ランドリースペースの既存の収納家具の都合上、幅が60cm以上のものを選んでしまうと収納家具の買い直しをしないとどうにもならない感じだったので、幅60cm以下は割と重要ポイントでした。

メンテナンス性については、どのメーカーも下に排水用ゴミ取り装置のの糸くずカゴ、上に乾燥機排気ダクト用の吸排気フィルタがあるというのは大体一緒だったので、こちらに関してもどれも似たり寄ったりでした。

さて、ドラム式洗濯機なのですが、狭いスペースにも収まるコンパクトなものと、ファミリー向けの標準サイズの2タイプが存在します。

コンパクトサイズのドラム式洗濯機ははパナソニックやシャープから出ていて、幅、奥行きそれぞれ60cmとなっています。

標準サイズのものは、幅、奥行きのいずれかが75cm、もう一方が60cm前後のものが多いです。

ちなみに、スペック上の容量は、コンパクトサイズの場合は洗濯容量6kg前後、標準サイズの物では洗濯容量10kg前後、どちらのサイズも乾燥容量はだいたいその半分、といったところでどのメーカもあまり差はありません。

結局、値段とメーカーの好みと見た目で選びました。

独り住まいなのでコンパクトサイズのものも一応検討しました。

しかし、コンパクトといっても、奥行きが15cmほど小さいだけでしたし、何より乾燥容量が3kgほどしかなく、これでは今の縦型よりも小さくなってしまうため候補からは外しました。

結局、購入したのは、最近いろいろ(悪い意味で)話題の絶えない東芝さんの最新型モデルです。

実物はこんな感じです。

f:id:noranuk0:20170304225213j:plain

操作パネルがなんか未来チックなのがかっこよくて割と気に入っております。

ちなみに、僕が購入したときは、17万3000円+10%ポイント還元ほどでしたが、価格コムなどで探すと、現在の最安価格はもう少し安くなっているようです。

ドラム式洗濯機を買ってよかったこと

洗濯物を放り込んでスイッチを押せばあとは乾燥まで全部やってくれるというのは思っていた以上に心理的に楽です。

別に洗濯マニアではないので、仕上がりがどうだとか、しわがどうだとか、そういうのはよくわかりませんが、とりあえずきれいにはなっています。また、今のところ生乾きだったこともありません。

セミダブルサイズの毛布も洗ってみましたが、洗ってもフカフカのまま、もちろんちゃんと綺麗になりました。

ドラム式洗濯機の注意点

乾燥機用のダクトのエアフィルター、排水用の糸くずかご(排水用の水にまざってる糸くずなどのごみを回収するための部品)ですが、とにかく毎回結構な量のほこりがたまります。

まず、乾燥機用のダクトフィルターには、毎回厚さ数ミリのレベルで綿埃がみっちり付着しています。
乾燥始めて半分くらい時間たったタイミングで、一時停止&フィルタ掃除をしたほうが若干効率が上がるんじゃないかって思うほどです*1

ただし、掃除といっても、古い歯ブラシなどで軽くこすってあげれば、綿埃の塊がごそっと取れるので、めんどくさいということは全くありません。

f:id:noranuk0:20170327215847j:plain

むしろ、乾燥用のダクトが埃で詰まって乾燥しても生乾きだったり、最悪故障したりという事例も検索するとたくさん出てくるので、毎回のフィルタ掃除は超重要と感じます。

ドラム式洗濯機 乾燥できない - Google 検索

次に排水用の糸くずかごなのですが、こちらも結構な糸くずがたまります。やはり毎回掃除したほうがよさそうです。

ただ、縦型洗濯機の槽内側についているような糸くず回収袋に比べると目が粗いです。細かい糸くずなどは水流でそのまま排水溝に流れて行ってしまうんじゃないかっていうくらいには粗いです。

排水溝が詰まった場合、なんだかとてもめんどくさいことになりそうなので、対策をいくつかとっています。

1つ目は、糸くずかごの中にくっつけるゴミ取りフィルターです。 洗濯機内蔵の糸くずかごよりも目が細かいため、小さな糸くずもしっかりキャッチできるようになるはずです。

f:id:noranuk0:20170327215928j:plain

2つ目は、洗濯機の排水パイプと排水溝の間に挟むタイプの糸くずフィルターボックスです。洗濯機の糸くずフィルターで取れなかったごみをここで再回収するために使っています。 紹介した商品は東芝製ですが、排水溝の口径に合わせて作られているため、おそらく他のメーカーの洗濯機でも使えると思います。なお、日立やパナからも類似品が出ているようです。

糸くずフィルターボックスにも量は少ないですが糸くずや髪の毛などのゴミがたまります。2、3回の洗濯で一つまみというくらいです。

細かいゴミなら水流にのってそのまま流れてしまいそうではありますが、まあ、一応気休め程度といったところでしょうか。

まとめ

ドラム式洗濯機、縦型洗濯機と比べると値段も高いし、メンテナンスに若干気を使う必要が出てくることも確かですが、天気の心配と自分のタイムスケジュールを拘束する要因を1つ取り払ってくれるという意味で、費用に見合ったメリットがあると思います。

今後、洗濯のために費やす時間を軽減するための投資と考えれば、確かに多少お高いですが、購入する価値は十分にあると思います。

この記事が、ドラム式洗濯機ほしいなーと思っている方、すでにお使いの方のご参考になれば幸いです。

次は家事の中でも2番目に嫌いな食後の後片付けを楽にしてくれるかもしれない食洗器を導入したいなぁ。と思う今日この頃です。お金があれば。ですが。。。。

お読みいただいてありがとうございました。

*1:そんなめんどくさいことやらないけど

イオシスさんで買った中古タブレットがプチ初期不良、でもその後の対応には好感が持てた

こんにちは。のらぬこです。

先日、中古PC、中古モバイル端末などを取り扱っているイオシスというお店で、Xperia Z4 tabletを買いました。

iosys.co.jp

購入したのはいわゆる海外モデル、LTE+WiFiに対応ているものです。

イオシスさんの中古商品はAランクからCランクまでのグレードがあります。

  • Aランク:
    • ほぼ新品
  • Bランク:
    • 経年劣化による使用感はあるけど目立つ傷や破損などは無い商品
  • Cランク:
    • 経年劣化以上の目立つ傷、ひび割れ破損、付属品欠品などがある商品

参考)商品ランクとは? | 中古パソコンと白ロムとアウトレットのイオシス

僕は使用感とかはあまり気にしないのですが、目立たなくても液晶面に傷があったら嫌だったので、新品個人輸入と値段あまり変わりませんでしたが(それでももろもろ手数料とか考えると1万円近く安い)、Aランクのものを購入しました。

注文後3日程度で届いたと思います。

梱包については「もうちょっと緩衝材多めに入れてもいいんじゃないかな」と個人的には思ったりもしたのですが、商品は、外箱、付属品、本体ともにとても奇麗でした。

取り敢えず、起動&初回セットアップを済ませ、しばらく触ってみたのですが、とあることに気づいてしまいました。

画面通知領域の右上に表示されている「NFC」のアイコンが点滅してる?

どうも、NFCモジュールを起動しようとして失敗して再起動、を繰り返しているように見えます。

試しに、NFC対応BTヘッドホンとか手持ちの携帯電話をNFCでつなごうとしてもうまくいきません。

こういうことがないようにAランク選んだんだけどな(´・ω・`)

取り敢えず、端末再リセットなどあれこれ試しても直る気配がありません。

NFCってぶっちゃけ使わない。でも、電池持ちとかには影響出てそうだなー。

海外モデルって、日本ソニーは修理受付してくれないんだよなー。

ダメだったらまあしょうがないかと思いつつ、一応イオシスさんに対応可能か質問してみました。

以下、送ったメール全文です。

株式会社イオシス 通信販売課 ご担当者様


○○と申します

先日、そちらのショップから
[中古Aランク] Sony Xperia Z4 Tablet SGP771  LTE  [White 32GB 海外版 SIMフリー]
を購入いたしました。

注文番号は ほげほげほにゃらら です。

御送付頂きましたタブレット端末を、早速つかってみたのですが、
端末機能のうち、NFCが正常に動作しない現象が発生しております。

現象としては、

・画面右上のNFCアイコンが点滅を繰り返す。
・設定>その他の設定>NFCのオプションが正常に動作していない
 (NFC機能の有効化、無効化を切り替えるオプションです)
・設定>タブレット情報>機器診断>テストタブ で表示される
 NFCのテストを実行するとテストに失敗した旨が表示されます。
・NFC対応の他の機器とつないでみましたが接続に失敗します。

端末のリセットなど確認してみたのですが、現象は変わらないようです。

ネットなどで、類似の不具合に遭遇されている方がいないかも確認してみましたが、
情報はないようで、初期不良の可能性を考えております。

商品に貼られていた保護フィルムを剥がしてしまったのですが、
初期不良のご確認、ご対応いただくことは可能でしょうか?

箱、その他付属品は購入時のまま保管されており、タブレット本体にも傷、汚れなどはつけておりません。


ご回答いただければ幸いです。

どうぞよろしくお願いいたします。

改めて読み返してみると、なんか面倒臭そうな奴だなー、とか思われた気がします。

中古品に対して、「初期」不良という言い方もなんか変な気もしたのですが、イオシスさんも「中古品の初期不良は~」みたいな書き方をされていたので、そこは気にしないことにしました。

昼にメールを送ったら、夕方前には返信が来まして、お詫びの文面と一緒に「同一商品の在庫確認します。在庫あれば交換、在庫なければ返金対応」という趣旨の返信がありました。

その後18時半ごろに、在庫確認できたから交換対応しますよ、とメールが来ました。

遅くまでご苦労様ですm(__)m

翌日、届いた商品を返送して、その3日後くらいに交換品の発送完了のメールが届きました。

その翌日には交換品を受け取ることができ、交換していただいた商品は全機能問題なく使えております。

初期(?)不良を引き当ててしまってちょっとひと手間掛かることになってしまったのは若干残念ではありますが、ショップさんの対応は丁寧で非常に好感が持てました。

後、配送に、佐川ではなくヤマトを使っていたのもポイント高いです*1

また何かあったら利用したいと思ったショップさんのお話でした。

今回の話は以上となります。

お読みいただいてありがとうございました。

*1:佐川は荷物の扱いが酷いからっていう以外にも、ヤマトの場合、配送先を後からコンビニに変更できるので地味に便利なのです

jetty + jndi(with c3p0)な環境から lightsleepを使って超簡単にDBに繋ぐ - デバッグができるようになるまで

どうも、のらぬこです。

この記事では、タイトルに記載の環境が eclipseデバッグ環境でも mvn jetty:run での環境でも正しく動作するところまでを記載します。

さて、最近、個人的に立ち上げてみたいWebサービスができまして、現在こっそり実装中です。

ただ、いかんせコード書き始めてまだ3日程度なので、使うフレームワークは今後変わるかもしれないし、そもそも途中で飽きるかもしれません。

が、今のところ、言語はJava、ApplicationServer には jetty、RDBpostgreSQL、Webフレームワークにはasta4dというフレームワークを選択しています。

O/R mapperとしてDomaを使おうかとも思ったのですが、最近見かけたlightsleepというO/R mapperを検証がてら使ってみることにしました。

下準備

開発環境

まずは開発環境として以下の環境を準備します。

  • Eclipse 4.5.2
    • すでにneon.2 まで出ていますが、そちらでも問題ないと思います。
  • run-jetty-run 1.3.5(Nightly)
    • Eclipse 内の market place からもダウンロードできますが、現在公開されているバージョンはjetty 9.0.0M3 までしか対応していません。
    • eclipseからデバッグ実行したとき、環境によっては(?)不具合が発生する可能性があるため、GitHub - xzer/run-jetty-run: Official successor of https://code.google.com/p/run-jetty-run/ を参考に、最新のnightly buildを使います。
      • market placeからダウンロードできる1.3.4を使用た場合、デバッガから起動した際に、使用するJettyのバージョンに関わらず、IndexOutOfRangeExceptionが大量発生する謎バグに遭遇することがあります。
    • なお、Eclipse market place を見ると、「Eclipse Jetty 3.9.0」 なるものもありますが、こちらのプラグインはjetty 起動時にc3p0を lib/ext としてjettyに食わせる手段がなさそうなので使えません。

とりあえずひな型WebAppの作成

これから開発を始めるという方は、まずはひな型Webプロジェクトを作成してください。

asta4dを利用する場合の手順は、GitHub - astamuse/asta4d: View first web application framework に手順が掛かれています。超簡単。

pomの設定を追加

下記のような形で書きます。とりあえず、今回追加が必要となる部分のみ抜き出しています。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.4.1212.jre7</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>

        <dependency>
            <groupId>lightsleep</groupId>
            <artifactId>lightsleep</artifactId>
            <version>1.5.1</version>
            <scope>system</scope>
            <systemPath>${project.basedir}/lib/lightsleep-1.5.1.jar</systemPath>
        </dependency>
    </dependencies>
</project>

なお、lightsleepは mvnrepository に登録されてなさそうなので、こちらを参考に、jarをダウンロードして pom.xml に systempath として追加しています。

jndiの設定を書く

jetty側

プロジェクトディレクトリに jetty ディレクトリを作成し、その中に jetty-local.xml という名前で jndiの設定(jetty側)を記載します。 jndi名、jdbc接続文字列等は各自適切に変更してください。

<!-- jetty/jetty-local.xml -->
<Configure id="noranuk0DB" class="org.eclipse.jetty.server.Server">
  <New id="noranuk0MasterDB" class="org.eclipse.jetty.plus.jndi.Resource">
    <Arg></Arg> <!-- score -->
    <Arg>jdbc/noranuk0DB</Arg> <!--  name -->
    <Arg> <!-- value -->
      <New class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <Set name="driverClass">org.postgresql.Driver</Set>
        <Set name="jdbcUrl">jdbc:postgresql://localhost:5432/kagure</Set>
        <Set name="user">admin</Set>
        <Set name="password">admin</Set>
      </New>
    </Arg>
  </New>
</Configure>

webapp側

src/main/webapp/WEB-INF/web.xml の web-app要素内に以下を追記します。res-ref-name は jetty-local.xml で指定した名前と同じものを設定してください。

<resource-ref>
    <res-ref-name>jdbc/noranuk0DB</res-ref-name>
    <res-type>javax.sql.DataSource</res-type>
    <res-auth>Container</res-auth>
</resource-ref>

lightsleepの設定

src/main/resources/lightsleep.property を新規作成し、以下のような感じで設定します。Databaseの欄は環境に合わせて適宜変更してください。

# for PostgreSQL
Logger             = Std$Out$Info
Database           = PostgreSQL
ConnectionSupplier = Jndi
dataSource         = jdbc/noranuk0DB

各環境ごとの設定例などは、Lightsleep/Manual_ja.md at master · MasatoKokubo/Lightsleep · GitHub を参照してください。

run-jetty-runの設定

  • $HOME/.m2/repository/ 以下から下記3つの jarを探してきて、プロジェクトディレクトリ内の適当な場所(jetty/lib/ext 等)にコピーします。
    • c3p0-0.9.5.2.jar
    • mchange-commons-java-0.2.1.jar
    • postgresql-9.4.1212.jre7.jar
      • JDBCドライバは、接続先DBによって適切なものに読み替えてください
  • Eclipse から [Run] - [Debug configurations…] を選択します。
  • 左サイドペインから [Jetty WEbApp]を選択します。
  • Jettyタブを開きます。
    • Jettyのバージョンは 9.3系(もしくは一番新しいバージョン)を選択します。
      • 9.2未満のバージョンを選択した場合、謎バグを踏むという不幸が訪れる可能性があります。
    • 一番下にある、[Show advanced option] を選択します。
    • JNDI support のチェックを入れます
    • Additional Jetty.xml の欄に 上で作成した jetty-local.xml のパスを指定します。
  • Jetty classpath タブを開きます
    • リストを一番下までスクロールさせると[Custom Jetty Classpath]という項目があるのでそれを選択します。
    • 最初にコピーした3つの jar を追加します。

ここまでの作業をしたうえで、デバッグを開始すれば、おそらく正常に実行されるはずです。

DBアクセスのテスト

URLがたたかれた時に動作するコードに、こんな感じのコード埋め込んであげると確認できるかと思います。

Transaction.execute(connection -> {
            Optional<Item> contactOpt = new Sql<>(Item.class)
                .where("id={}", 1)
                .select(connection);
            // 取得したレコード出力するなりなんなり
});

mvn jetty:run 向け pom.xml の設定

そろそろ書くのに疲れてきたので、jetty-maven-pluginの設定部分のみ貼っておきます。

こっち側は、jetty + jndi(with c3p0) が正しく動作するように設定を記載するだけです。

dependencies に c3p0 と jdbc drive を追加してあげないと、jettyが起動時にjndiの設定を行う際、c3p0などが認識できずエラーになってしまうので、ちゃんと書かないとだめです。

<project>
    <build>
        <plugins>
            <plugin>
                <groupId>org.eclipse.jetty</groupId>
                <artifactId>jetty-maven-plugin</artifactId>
                <version>9.4.0.v20161208</version>
                <configuration>
                    <webApp>
                        <contextPath>/</contextPath>
                    </webApp>
                    <httpConnector>
                        <port>8080</port>
                    </httpConnector>
                    <daemon>true</daemon>
                    <jettyXml>${project.basedir}/jetty/jetty-local.xml</jettyXml>
                </configuration>
                <executions>
                    <execution>
                        <id>start-jetty</id>
                        <phase>pre-integration-test</phase>
                        <goals>
                            <goal>start</goal>
                        </goals>
                        <configuration>
                            <scanIntervalSeconds>0</scanIntervalSeconds>
                        </configuration>
                    </execution>
                    <execution>
                        <id>stop-jetty</id>
                        <phase>post-integration-test</phase>
                        <goals>
                            <goal>stop</goal>
                        </goals>
                    </execution>
                </executions>

                <dependencies>
                    <dependency>
                        <groupId>org.eclipse.jetty</groupId>
                        <artifactId>jetty-io</artifactId>
                        <version>9.4.0.v20161208</version>
                    </dependency>

                    <!-- https://mvnrepository.com/artifact/org.postgresql/postgresql -->
                    <dependency>
                        <groupId>org.postgresql</groupId>
                        <artifactId>postgresql</artifactId>
                        <version>9.4.1212.jre7</version>
                    </dependency>

                    <!-- https://mvnrepository.com/artifact/com.mchange/c3p0 -->
                    <dependency>
                        <groupId>com.mchange</groupId>
                        <artifactId>c3p0</artifactId>
                        <version>0.9.5.2</version>
                    </dependency>
                </dependencies>
            </plugin>

            <!-- -->
        </plugins>
    </build>
</project>

今回の話は以上となります。

lightsleep の使用感など、何かネタが出てくればそちらも記事にしたいと思います。

お読みいただいてありがとうございました。

gulp-s3-gzipで、s3上の格納先のパスを指定する方法

こんばんは、のらぬこです。

webサーバからクライアントにコンテンツ圧縮してを送ることができる仕様があります。

今更感半端ないですが、リクエストヘッダに書く accept-encoding : gzip とか、レスポンスヘッダにcontent-encoding : gzip と書いて圧縮したコンテンツを返すとかの話です。

少しでも転送量を減らしたいとき、少しでもページ読み込み速度を上げたい時などに使われるものだと思います。

ところで、s3に格納されたファイルについては、webブラウザが圧縮コンテンツ送ってもいいって要求してきても(つまり、accept-encoding : gzip と書かれていたとしても)、s3はわざわざ圧縮したコンテンツを返してくれません。

その辺は自前でやってくれってことですね。

取り敢えずやってみるだけであれば、以下の記事などが参考になると思います。

d.hatena.ne.jp

こちらの記事を書いた方も言われていますが、毎回毎回こんなことやってたら精神的に死んじゃうので、自動化したいですよね。

例えば、gulp を使う場合、gulp-s3-gzip というモジュールを使うと、ファイルを自動でgzip圧縮したうえで、自動でs3にアップロードできます。

しかも、content-encoding のヘッダ指定などもやってくれるので、deployはコマンド一発で完了です。

www.npmjs.com

npmjs内のドキュメントを読むと、ちょっと困ったことにアップロード先のパス指定のやり方が記載されていません。

やり方は簡単で、npm install して引っ張ってきたソース見るとすぐわかります。

var gulp = require("gulp");
var s3 = require("gulp-s3-gzip");
var gzip = require("gulp-gzip");
var options = { gzippedOnly: true, uploadPath : "/hoge/fuga/" };
 
gulp.src('./dist/**')
.pipe(gzip())
.pipe(s3(aws, options));
 
});

こんな感じで、options に uploadPath : "/hoge/fuga/" を追加すればおっけーです。

なお、gulp-s3-gzipの詳しい説明や、ソースコード全体を閲覧したい場合には、上でも紹介した gulp-s3-gzip のページを参照してください。

ということで、今回のお話は以上となります。

どなたかのお役に立てれば幸いです。

【2016年後半版】個人公開アプリのダウンロード数、収益を少しでも増やすためにやったこととその成果

はじめに

こんにちは。

現在、android 向けに4本のアプリを公開しています(ゲームではなくアプリです)。 そのうち、2014年12月末に公開したアプリが、比較的順調にダウンロード数を伸ばし、先日35万ダウンロードを突破しました。

ちなみに、2017年の1月12日現在の総ダウンロード数は37万5374となっています。

なお、このテーマで記事を書くのは今回で3回目になります。

一応、過去記事のリンクも掲載しておきます。

もしお時間ありましたら、最初の記事からお読みになられると、改善していくための過程などが順に追えるのでより理解しやすいかもしれません。

qiita.com

noranuk0.hatenablog.com

35万ダウンロードされたアプリの紹介

記事内でアプリの紹介をしたことがなかったので、せっかくなので簡単に紹介します。

media.Re.Scan:高速メディアスキャンアプリ

このアプリは、いわゆる「メディアスキャン」という呼ばれるアプリです。

アンドロイド端末に保存された音楽ファイル、動画ファイルは、ファイルシステム上のパス情報とは別に、メタ情報(例えば、アルバムタイトル、作曲者、曲名、動画のサムネイル画像などです)が端末内のデータベースに保存されています。

ちなみに、このデータベースや、その情報を管理している内蔵アプリのことを「メディアストレージ」とよんだりしています。

さて、ファイルシステム上のファイルパス情報と、メディアストレージの情報は、通常はアプリやアンドロイドOSの機能により適切に同期されているのですが、行儀の悪いアプリの影響などで同期が正しくとれなくなってしまうことがあります。

こうなってしまうと、音楽再生アプリや、動画再生アプリなどで、メディアファイルの情報が正しく取得できなくなり、アルバム検索ができないなどの不具合の原因になる場合があります。

これを正しい状態に修正するのが、今回の記事テーマにもなっている、拙作「media.Re.Scan:」というアプリです。

なぜ作ったか

実は、自分が開発を始めた時には、すでに類似のアプリがたくさんありました。

しかし、Android4.4の仕様変更により、既存のアプリはほとんど動作しなくなってしまったこと、Android4.4に対応したメディアスキャンアプリでまともに動作するものが、当時はまだリリースされていませんでした。

自分も必要だったことと、今出せばそこそこのヒットが狙えるんじゃないかと考え、自分で作ってみることにしたのが始まりです。

そんなこんなで

そんなこんなで、記事タイトルにもあるように、ダウンロード数、収益を少しでも増やすために行った各種施策が功を奏し、昨年7月頃から、1日平均1200〜1500ダウンロード、昨年12月の末ごろからは1日1750ダウンロード以上を維持し続けています。

施策と成果の話

というわけで、早速ですが、行ってきた施策とその振り返りの話をします。

ネイティブ広告の話

昨年夏に導入したネイティブ広告ですが(詳細は前記事参照です)、まずはその経過報告です。

ネイティブ広告は、スキャン中の待ち時間に、画面の半分くらいを被うサイズのものを表示させています。

ただ、常に表示させているわけではなく、スキャン時間が長くなりそうな時に一定の確率で表示させています。

同一の期間で、バナー広告とネイティブ広告で、表示回数、収益を比較してみると、

ネイティブ広告
表示回数:10万回 収益:1万1千円

バナー広告(全体)
表示回数:200万回 収益:13万円

程となっています。

数字だけ見ると、ネイティブ広告の方が(表示回数あたりの収益額という見方をすれば)高いのですが、波が大きく、とくにここ最近はあまり収益に貢献してくれていません。 単純に表示確率をあげれば収入が増えるとか、逆に下げたほうが増えるということにはならないと考えています。

とりあえず、両方同時に出すことも画面レイアウト上は可能な作りにしているのですが、規約上、1つの画面に複数の広告を表示できないことになっているので、ネイティブ広告とバナー広告を両方同時に出すことはできません。

CTRの高い方を選択できるような仕組みがあればいいのですが、現状はそういった仕組みはなさそうなので、そこは残念なところです。

アプリ開発者の収益を最大化させるために、その辺の仕組みも用意してくれるか、ネイティブ広告とバナー広告の同時表示がおっけーって規約に明示していただけると嬉しいです。→Google殿

インタースティシャル広告の導入

最近、グーグルから送られてくる「admobがインタースティシャル広告対応したから使ってよ」っていう宣伝メールがうるさいので、試験的に導入してみました。

ネイティブ広告は、画面は占有するけど、ユーザのアクションは必要ない広告です。
一方で、インタースティシャル広告は、クローズボタンで広告を閉じるか、広告内のコンテンツをタップして画面遷移するまで広告が消えない=アプリの操作ができないというタイプの広告のため、ユーザ的にもストレスになりやすいです(少なくとも僕は)。

試験的ということで、現在は、比較的長期間使い続けてくれているユーザさんに対して、ネイティブ広告を表示する条件にマッチした場合、10%くらいの確率でインタースティシャル広告を表示しています。

基本的には、メディアスキャンの待ち時間に表示されていて、広告表示中もバックグラウンドで処理は続行中のため、ユーザさんにとって、広告を閉じるという手間は増えてしまいますが、時間的な不利益はほぼないはずです。

ゲームの場合は、ステージクリア後などに一定の確率で表示させることで、比較的収益に貢献してくれるらしいのですが、アプリの場合にはといいますと、

広告表示比率を絞りすぎた結果、ほとんど表示されておらず、集計可能なデータ量が集まっていない事案発生

広告に対して、ネガティブなイメージを持たれる方が多いのは、日本も海外も一緒のようで、広告嫌い的なレビューがつくことも何度かありました。

そういったレビューがつかないように、表示頻度がかなり少なくなるように設定したのですが、ちょっと少なくしすぎたようです。

約一ヶ月様子を見てみましたが
表示回数:131回 クリック数:2回 収益額:12円

という結果です。

クリック率は高いのですが、データが少なすぎてなんとも言えません。

ただ、1クリックあたり6円程度ならバナー広告と変わらないため、よく言われている「インタースティシャル広告はクリック単価が高い」ということは、少なくともGoogleのインタースティシャル広告に限って言えば、今の所まったくなさそうです。

レビュー訴求

★×5評価ってやっぱり大事です。平均評価4以上が超大事な文化なので、4でもちょっと・・・と思ってしまいます。

個人的には、平均の3以上ならまあいいじゃないのって思うんですけど。。

レビュー訴求はこれまであまりやってこなかったのですが、少しアイデアが出てきたのでやってみることにしました。

どのようなアイデアかというと、スキャン中、ネイティブ広告やインタースティシャル広告を表示する条件にマッチしなかった時に、レビューのお願いを表示してみるというものです。

たいていのアプリは、レビューのお願いをモーダルダイアログで表示するのですが、media. Re.scan: の場合、Activityの上にFrameLayoutを重ね、その上側のレイヤーにコントロールを配置しているだけです。

したがって、レビューのお願いが表示されていても、スキャンの中断などの操作は行うことができるようになっています。

f:id:noranuk0:20170113130845p:plain

結果、1日あたりのレビュー数は増えたような気はしています。

developer console画面で、レビューの平均評価は確認できるのですが、レビュー数のグラフは標準では用意されていないため、感覚値でしかないのですが、レビューが追加されましたというプッシュ通知が来る頻度は、だいたい倍ぐらいに増えたと思っています。

収益について

2016年の収益は?

やっぱり気になる人も多いかと思いますので、さらっと公表してみます。

というか、3年目で、やっと公表できるくらいの数字は出てくるようになりました。

f:id:noranuk0:20170113130334p:plain

結果的には、16万5千円ほどでした。 収益が伸びてきたのは、6月頃からで、今の所月額2万〜2万5千円ほどで頭打ちになっています。
今年もこのアプリだけで収益を増やしていくことができそうか、というと、さすがにそういうことはなさそうです。

とはいえ、10万DLクラスのアプリアイデアも、そうそう出てくるものではないのも悩みどころです。

今年中には月額5万、数年以内に10万くらい稼げたらいいなーっという感じではあるので、何かやってみたいところではあります。

といったところで、今回のお話は以上となります。

皆様にとって何かの参考になれば幸いです。

どうもありがとうございました。

全部無料⇒VSTSとAzureを連携させて、nodejs+expressなアプリの自動デプロイ環境を実現する

こんにちは。のらぬこです。

今年も残り1か月、コミケ以外はもうどうでもいい感じの季節になってきました。

今日は、VisualStudio TeamSerivices内のgitリポジトリに格納されたnodejs+express製のWebアプリを、(今のところは)永久無料でAzureにホスティングしてもらう話をします。

題材として、前回の記事で作成した「nodejs+express製のあぷろだアプリ」をAzureに載せてみたいと思います。

noranuk0.hatenablog.com

gitリポジトリへの登録

まずはプロジェクトをVisualStudio TeamSericesのgitリポジトリにコミットします。 こちらの記事を参考に、VisualStudio TeamSerivcesのアカウントを作り、uploaderプロジェクト全体を、gitリポジトリにpushしてください。

noranuk0.hatenablog.com

なお、node_modulesと、uploadsディレクトリは .gitignoreに追加するなどしてcommit対象から除外してください。

f:id:noranuk0:20161205234631p:plain

Azureアカウントを作成する

すでにアカウントをお持ちの方は Microsoft Azure からダッシュボードに入ってください。

f:id:noranuk0:20161206001913p:plain

アカウントをまだお持ちでない方は、ここからアカウントを作成してください。登録時にクレジットカード要求されたりSMS認証があったりしますが、お金がかかるサービスは一切使いません。

なお、新規登録時に200ドルほどの無料クレジットがもらえますが、こちらも利用しません。

ちなみに新規登録時にもらえる無料クレジットなのですが、一か月ほどで使用期限が切れてしまいます。こちらは各種実験など何か別の用途で使い切ってしまってもよいかと思います。

アプリのデプロイ先を準備する

まずはWebアプリをデプロイするためのインスタンスを作成します。

新規(+ボタン) > Web+モバイ > Web App の順に選択します。 f:id:noranuk0:20161206002859p:plain

表示画面が切り替わるので、まずは、アプリ名、リソースグループ、サブスクリプションを設定します。

デプロイしたアプリには、http://{アプリ名}.azurewebsites.net でアクセスできます。

hoge, huga, wp, myapp等の適当な名前はすでに誰かに使われていて、かつ試しに繋いでみると403だったりするので、自分のわかり易い名前をちゃんとつけたほうが良いかと思います。

次に、料金プランを無料プランに変更します。

App Serviceプラン場所 > 新規作成 > 価格レベル(S1 Standard) f:id:noranuk0:20161206002938p:plain

おすすめプラン一覧が出てくるのですが、この中には無料で使えるプランは含まれていません。

画面右上の「全て表示」を押して使用可能な全てのプランを表示してください。

f:id:noranuk0:20161206003502p:plain

一番下までスクロールさせると、「F1 free」というプランがあるのでそれを選択してください。

f:id:noranuk0:20161206221204p:plain

なお、このプラン、お金はかからないようにはなっていますが、いくつか制限が存在します。

  • 独自ドメインは使えません
  • SSLは使えません
  • CPUやメモリー、ディスクスペース等のスペックはは記載の通りです
  • 自動・手動スケールアウトは出来ません
  • 一定時間あたりのCPU占有率、データ転送量の上限が決まっています
    • 上限を超えた場合、一定時間サービスが停止されます
    • お金を払って制限解除、みたいなことは出来ないようです
  • 他にもあるかもしれません

外部公開してアクセスを稼ぐような用途にはまず向きませんが、個人用途でそんなに負荷もかからないサービスとして利用するのであれば、さほど困ることは無い程度のスペックはあると思います。

AzureをVisualStudio TeamSerivcesと関連付ける

Visual Studio Team SerivicesにコミットされたプロジェクトをAzureにデプロイするには、事前に2つのサービスを関連付けておく必要があります。

ダッシュボードのメニューから、Team services accounts を選択します。

f:id:noranuk0:20161206221619p:plain

ログイン中MicrosoftAccountに紐付いているMSDNアカウントや、VSTSアカウントが表示されるので、紐付けたいアカウントを選択してください。

参考サイト)Setting up a VSTS account so it can deploy to a Web App · projectkudu/kudu Wiki · GitHub

デプロイ元ソースにVSTSのgitリポジトリを設定する

Web App」を選択し、先程作成したアプリインスタンスを選びます。

更に、「アプリのデプロイ」カテゴリー内の「デプロイオプション」を選択します。

f:id:noranuk0:20161206222042p:plain

展開元」の「ソースの選択」を選ぶと選択可能なソース一覧が表示されます。

今回は、「Visual Studio Team Services」を選びます。

プロジェクトとブランチを選んで、OKボタンを押せば、最新コミットが自動でWebサーバに展開されます。

ブラウザから、「http://{アプリ名}.azurewebsites.net」にアクセスすると、VSTSにコミットされているアプリが動作しているのが確認できるかと思います。

また、VSTSリポジトリを更新すると、都度デプロイが走り、公開サれているWebアプリも更新されます。

最後に

ちなみに、ASP.NET等の MS謹製環境だけではなく、JAVAプロジェクトや、PHPなんかも動かすことが出来ます。

更に、ローカル環境のみですが、MySQLも使えるため、おそらくWord Pressも動かすことができるような気がしています。

Javaが動くので、scalaやgroovyやJRubyといったJVMで動く系言語(とその実装)も動かすことができます。

個人開発のタスク管理用に、Redmineクラウドに立てておきたい場合などにも活用できるかもしれません。

今回は、Visual Studio Team Serivcies と Azure を使えば、開発から公開まで完全無料のWebアプリ開発、ホスティング環境が手に入りますよ、というお話でした。

お読みいただいてありがとうございました。