Hatena::ブログ(Diary)

satosystemsの日記 このページをアンテナに追加 RSSフィード

2016-01-03

[] 年末年始の作業

2015年12月25日から2016年1月3日まで、さくらのレンタルサーバで15年ぐらいメンテナンスしている掲示板の刷新を行った。

元の掲示板は Perl で実装してあって、古い掲示板にありがちなヒアドキュメントで HTML を出力する形式になっていて、メンテナンスが非常につらい。もともと Perl は得意だったんだけど、仕事で使わないのでどんどんスキルが落ちていき、今では調べながらじゃないと書きたいことが書けない。

ということで、年末年始は時間があったので Perl をやめて Haskell で再実装した。

まずは、以下。

ずっとさくらのレンタルサーバ上で Haskell を動かす環境を作ろうと思っていたんだけど、これがようやくきちんと完成した。

Haskell が動くようになったら cgi: A library for writing CGI programs で CGI として実装。アプリケーションサーバが動かせればもっとモダンに作れたのだと思うけど、レンタルサーバなので致し方ない。CGI でも Haskell が動くだけよしとする。

Perl で実装した時は、データはすべてファイルに溜め込んでいたけど、Haskell で再実装する際にはレンタルサーバで提供されている MySQL を使用することにした。使用したのは mysql-simple: A mid-level MySQL client library. 。どんなにデータがあってもデータベースに突っ込んでクエリで取り出せるというのは素晴らしい。Perl で実装していた時はある程度の件数でファイルを分割していたので、何をするにしても非常にややこしかった。

CGI が HTML を生成して返すか REST API を用意して Ajax で通信するか迷ったけど、掲示板ユーザの環境を見ると古いブラウザや古いスマートフォンを使用している人はあまりいないので、後者にした。

API のエンドポイントは、独立した実行ファイルを用意するようにしたけど、ひとつにまとめたほうが良かったかもしれない。エンドポイントは 10 数個になってしまい、ひとつひとつの CGI は小さくて良いんだけど見通しがあまり良くない。

フロントエンドは、タブレットやスマートフォンでも使いやすくしたいという狙いがあったので、レスポンシブデザインを取り入れた。React を使えばフロントエンドでも react-haskell: Haskell React bindings を使用して JavaScript ではなく Haskell でコードを書けるんだけど、休み中に完成しなそうなので、慣れている Bootstrap を用いて JavaScript と jQuery を使うことにした。

年末年始で 50 時間ぐらい実装に費やしたけど、完成度は 80% ぐらい。内訳は:

  • レンタルサーバ上に環境を構築する調査および構築: 10 時間
  • MySQL バックエンド: 5 時間
  • CGI エンドポイント: 15 時間
  • HTML5 フロントエンド: 20 時間

という感じだろうか。とにかくフロントエンドに時間がかかる。逆にバックエンドは非常に簡単。ちょっとだけ悩んだのが、データベースの型と Haskell の型。Persistent entity syntax ? yesodweb/persistent Wiki ? GitHub を見れば解決した。CGI フロントエンドは、つまらないハマりをしてしまって時間をロスした。バックエンドは cabal repl で動作確認が簡単に行えるんだけど、エンドポイントはブラウザなり curl なりで叩かないとならないので面倒というのと、エラーが発生した場合エラーログを見れば原因がわかるんだけど、さくらのレンタルサーバの場合、前日のエラーログが翌日 0:06 ぐらいに $HOME/log に配置されるようになっていて、すぐにはエラーログを確認できないことなどがある。ちなみに、つまらないハマりというのは、バックエンドをデバッグ中に putStrLn でデバッグ出力を行ったコードを消し忘れて、その関数をエンドポイントから呼び出してしまうことで、そのデバッグ出力がレスポンスとして扱われてしまい、500 Internal Server Error が発生するというもの。

完成したら GitHub に置く予定。

2015-09-13

[] Qt ライセンス

とあるプログラミングコンテストに参加しようと思って、Qt を調べてみた。主にライセンス。

開発ターゲットは Android と iOS。

  • Qt は商用ライセンス、LGPL、GPL
  • LGPL で Android、iOS の開発は可能
  • ただし iOS はスタティックリンクになるため、アプリが LGPL 感染する(ソースコード公開が必須)
  • モバイル向け商用ライセンスは 50 万以上する

ライセンスフィーが高いな。商用ライセンスは開発者にくっつくので、例えばプロダクト開発で 3 人のチームとなると 3 人分のライセンスを購入する必要がある。また、商用ライセンスは譲渡が可能だけど 6 ヶ月に 1 回という制限がつく。Unity ぐらい(20 万)だと買いやすいんだけど。


参考にしたのは以下サイト。

2015-07-31

[] Context メモ

Android にはいくつかの Context がある。

Activity から参照可能なのは:

  • this : Acrivity
  • getBaseContext() : ContextImpl
  • getApplicationContext() : Applicaiton

となる。

Activity を Context として持ちまわるのは、Activity のリークにつながるので、getApplicationContext() で取得した Application を持ちまわる、というのが慣習になっていると思っていたが、Application だと問題が発生することがある。

具体的に遭遇した問題は:

View view = inflate(context, R.layout.sample, null);
addView(view);

という具合に、inflate を用いて View を生成する際に使用する Context が Application だった場合、生成された View にアプリのテーマが適切に設定されず、EditText の背景が不正になることがあった。

getBaseContext() で取得した ContextImpl を使用した場合は適切なテーマが設定されたため、これがワークアラウンドなんだと思う。

2015-05-17

[] FrameLayout メモ

FrameLayout は z index を簡単に調整することができる便利なレイアウトなんだけど、View が Button だった場合、z index を無視して最上位に表示されるようだ。

これが ImageButton なら期待した通りの z index になるようだ。

2015-05-16

[] Animation メモ

FrameLayout に addView(View) をするタイミングで TranslateAnimatoin でスライドインさせようとした場合に、以下のケースでチラツキが発生したので、その解決法のメモ。

FrameLayout に元からある View を removeView(View) でアニメーションを伴わせ削除しつつ、addView(View) でアニメーションしつつ追加する場合はチラツキは発生しない。

しかし、FrameLayout に View を残したまま新しい View をアニメーションを伴わせつつ追加すると、一瞬アニメーション最終状態で表示され、アニメーション開始されてしまう。いわゆるチラツキ。

これを回避するために、以下の様なコードを書く。

        child.setVisibility(View.GONE);
        animation.setAnimationListener(new Animation.AnimationListener() {
            @Override
            public void onAnimationStart(Animation animation) {
                child.setVisibility(View.VISIBLE);
            }

            @Override
            public void onAnimationEnd(Animation animation) {
            }

            @Override
            public void onAnimationRepeat(Animation animation) {
            }
        });
        child.startAnimation(animation);
        parent.addView(child);

こんな感じ。こちらで見つけた。

android - How to avoid blink of View when using animation? - Stack Overflow

要するに、ちらつく最初は GONE で非表示にさせておき、アニメーションが開始したら VISIBLE に戻す、というもの。

根本的な解決にはなっておらず、かつ実際のアニメーション期間が少し短くなるが、ちらつくよりは良いかと思う。

[] TableLayout メモ

TableLayout に ImageView を配置する場合、android:stretchColumns を設定していると、ImageView を android:scaleType="fitCenter" などと縦横比を維持するように設定していても、TableLayout のストレッチ設定の方が勝ってしまい、画像が横に伸びる。

[] GridView メモ

備忘録。

GridView に表示する View に、Button が存在すると、そのボタンをタップしたかどうかにかかわらず、タッチイベントがボタンに取られてしまい、GridView の OnItemClickListener が通知されなくなる。

[] ViewPager メモ

備忘録。

Android の ViewPager は、単純な使い方ならかなり簡単でかつ便利なんだけど、ちょっと複雑なことをしようとすると、とたんにトラップが牙を剥く。

1.一度表示させた Fragment を取り替えることができない

例えば、3 ページ持つ ViewPager があったとする。

1 ページ目の入力で 2 ページ目に表示させる Fragment を動的に変える、というようなことが難しい。

表示を変えることはできるが、一度表示させた Fragment は ViewPager 内でキャッシュされ、またキャッシュされた Fragment を開放する方法を ViewPager が提供していないためである。

2.スワイプさせて表示した Fragment 内の View#onMeasure(int, int) が呼び出されない

ViewPager は初期状態で、setCurrentItem(int) で設定されたページと左右両隣のページの Fragment を要求し、このタイミングで生成された Fragment 内の View#onMeasure(int, int) は適切に呼び出される。ただし、指でスワイプしたり、後から setCurrentItem(int) などで違う Fragment を表示させる場合において、何故か View#onMeasure(int, int) が呼び出されない。

View#onMeasure(int, int) は、カスタム View を作る際にお世話になるメソッドで、場合によっては必要不可欠だったりするんだけど、ViewPager では機能しないため、対策が必要になる。

最もわかりやすい対策は、初期表示でなら適切にレイアウトされるので、まずすべてのページを初期状態で表示させ、その際使用した View をキャッシュしておき、改めて本番でそれらを使う、というものだ。キャッシュをどのように行うかがキモなんだけど、親 Activity などに持たせるのが賢明なんだと思う。