Unyablog.

のにれんのブログ

画像取得ライブラリの Glide 使ってみた

Android で画像を表示する際、 URL からいい感じにキャッシュしていい感じに表示してくれるライブラリはいくつかあります。

有名所だと

  • Picasso
    • Square のライブラリ
  • Glide
    • 元BumpTech のライブラリ (Google のプロダクトでも使われているらしい)
  • Fresco*1

日本だとなんとなく Picasso をよく見かける気がする。

元々 PhotoLinkViewer では Picasso を用いていて、アニメーション GIF は webView で表示していたのですが、 Glide がアニメーション GIF に対応しているとのことで Glide を使ってみました。

使い方の説明は以下に詳しいです。

上に書いてあるとおりカスタムがしやすい Picasso と言った感じ*2で、インターフェースもそっくりなので適当に Picasso を Glide に置き換えるだけで動きます。すごい。

move to glide from picasso · nonylene/PhotoLinkViewer-Core@a11fe73 · GitHub

OkHttp や Volley にも対応していて、さらに GlideModule を使えば自分の OkHttpClient も使えて便利。

Integration Libraries · bumptech/glide Wiki · GitHub

アニメーションGIFについて

さて、目的はアニメーションGIFな訳ですが一筋縄には行きませんでした。

軽いアニメーションGIF(500*500程度) では軽快に動くのですが、 1980 * 1080 ぐらいになるとよくOOMで落ちてしまいます。

.asBitmap() を指定すると細かい画像の縮小ロードの設定ができるのですが、GIF だとできないので BitmapOptions.inSampleSize レベルではあまり縮小してくれない。*3

ttps://www.reddit.com/r/androiddev/comments/2tpwub/glide_35_released/co1hmsx*4

そもそもまだ縮小してデコードするのに完全に対応していないらしい。

OOM loading huge gifs · Issue #83 · bumptech/glide · GitHub

メモリキャッシュをスキップしたり、 AndoridManifest.xmlandroid:largeHeap="true" を設定すると落ちにくくはなったのですが、そこまでしても結局GIFの拡大・縮小操作やアニメーション自体が非常に重くなってしまいました。

その後結局諦めて webView で表示するようにしました。 webView だと 3000px ぐらいでも落ちることがなく、比較的滑らかです。

小さい GIF の表示にでは簡単に GIF を入れることができて非常に便利そうなので、また機会があれば使おうという感じでした。*5

その他

  • Fresco という Facebook の画像ライブラリもアニメーションGIFに対応していて多機能そうだったのですが、ダウンサンプリングにはまだあまり対応しておらず、コードも大きい等いろいろ面倒そうだったので使っていません。
  • Ion というライブラリも対応していますが、これはもうちょっと汎用的ななんでもライブラリっぽい。

*1:京都には同名のスーパーがある

*2:内部のコードは全然違いますが

*3:inSampleSize が小さい方の辺が優先されるため、横に大きい画像だと縦に表示した際に縦が 3000px ぐらいになるまで inSampleSize が 1 のまま

*4:reddit のNG避け

*5:戻すのめんどくさくて Picasso 使っていた場所はそのまま Glide にしてますが

SQLクエリチューニングメモ

  • table: "table_name"
A B
"a" 1
"a" 2
"a" 4
"b" 1
"b" 4
"c" 1
"c" 3

のようなデータが 100k レコードほど存在するデータベースにおいて、指定のB (b_list) が存在するAランダムに一つ抽出 する。

例:

  • blist: [1] -> mathced A: ["a", "b", "c"] -> ランダムに取り出し -> "b"
  • blist: [1, 2] -> mathced A: ["a"] -> ランダムに取り出し -> "a"
  • blist: [1, 4] -> matched A: ["a", "b"] -> ランダムに取り出し -> "b"

このとき、

select A from table_name
    where B in ({ b_list })
    group by A
    having count(distinct B) > { b_list_length - 1 }
    order by random()
    limit 1
;

としていたが、1.5 秒ほどかかって困っていた。

order by random() があまり良くなく、 count して自前でランダムしたほうが良いという話を見る *1 が、そもそも limit 1 を消し、得られた結果を count しても同程度に遅いのであんまり関係無さそう。

count(distinct B) が悪いのではということで、はじめから B でも group by して count() はグループ化された A の個数を数えるだけにした。

そして、

select A from (
    select A from table_name
        where B in ({b_list})
        group by A, B
    )
    group by A
    having count() > { b_list_length - 1 }
    order by random()
    limit 1
;

とすると 0.8 秒ほどになり、そこから A,B 間に index を貼ることで 0.6 秒ほどになった。

蛇足

その後 group by は暗黙のソートがあるので exists を使おう!という数年前の記事を見かけた。

実際にやってみたら 0.8秒と遅くなってしまったので、 group by に戻した。

参考になったサイト

Use Subqueries to Count Distinct 50X Faster

norifyDataSetChanged でアニメーションする in RecyclerView

stackoverflow.com

  • RecyclerView.Adapter#getItemId()ユニークな ID を返す
  • RecyclerView.Adapter#setHasStableIds(true) を行う

これだけで RecyclerView.Adapter#notifyDataSetChanged() のみでもいい感じにアニメーションしてくれます。

パフォーマンスも良くなります。

ttps://www.reddit.com/r/androiddev/comments/2lr8bf/what_does_recyclerview_sethasstableids_do_and_why/*1

StackOverflow では supportsPredictiveItemAnimations()true を返すようにする必要があると書いてありますが、 Support Library 23.4.0 時点の LinearLayoutManager では必要ありませんでした。

実演

getItemId()ユニークなID(今回はデータの primary key) を返し、全て notifyDataSetChanged() で変更を伝えています。

毎回 notifyDataInserted() 等を呼ぶよりも簡単にできて便利。

*1:reddit リンク入れると投稿失敗するみたいなので h 抜いています

Haskell入門 14日目

十四日目

VBoxHeadlessTray を入れてみた。これだと一々 VM 起動してること気にしなくてよさそう。

SICP 読まないとなあとか思ってるこの頃。

Haskell, monad ぐらいまでやろうかなあとか思って目次見たけど一番最後だった。

すごいHaskellたのしく学ぼう!

すごいHaskellたのしく学ぼう!

第8章

入出力。結構難しいとか聞いたことある。

8.1

  • いい話

8.2

  • ついに ghci 以外から呼び出すようになるのか
  • コンパイルだ。。。!
    • まあ ghci からロードしてる時も呼び出してるのだけど
  • 返り値はあってアクション
  • C みたいに main がデフォで行われるみたいな感じかな

8.3

  • 急に命令形っぽくなってきた
    • main 自体がIOアクションを返す
  • getLine はIOアクションで、String を返す
    • printStrLn はIOアクション自体で、<- はそこから取ってくるみたいな感じ仮名
      • 見た感じリスト内包のアレとは違いそう。
  • do 構文自体はIOアクションを返すんかな
  • do の中で = 使ったら怒られたけどそういうもんなのか
    • let 使ったらできた。
  • haskell, 地味にインデント必要なのが難しいと思う*1
foo <-  putStrLn "hello"
putStrLn $ show $ foo == ()

としたら True と出てなるほどって感じ。

  • ghci でやっても do 内でやれって怒られた。

    • もしやと思って let 付けたら通った。
  • null 初めて見た気がする

    • Foldable を引数に取るので isEmpty みたいなもんっぽい
  • return もIOアクションなのかー

    • do 節で終わりに何もしない時とかに使う

8.4

  • それっぽい入出力群
  • 入力を反転して返すの、when 使えば
when (not $ null line) do putStrLn $ reverse line

に省略できて便利

  • forMmapM はIOアクションの結果を入れてくれる

感想

  • 一気に実用的になった感じで楽しかった。
  • なるほどいろいろ考えられてるなぁという感じ
  • これがよく聞くIOモナドかと思いながら読んでた。

*1:まあpythonもそうだけど

Haskell入門 13日目

十三日目

時間が開いてしまってやることを忘れたのでまずここのログを読みなおした。メモ残すこと大事だなって思いました()

WindowsHaskell しようかと思って Stack いれたけど cmd 使うこと考えると嫌になってすぐアンインストールしました*1

7.9

  • 型クラス懐かしい
    • 要するにjavaインターフェースみたいなもんでしょっていうjava変換おじさんになってる。
  • 自動でやってくれるけど今回はちゃんとやってみますねっていう感じか
    • Serializable 的な hogehoge
  • 循環的な定義もできるの良い

  • 試しに Show を付けなかったらghci で Red だけするとエラーになった。これ前もやったかもしれない。

  • サブクラスは制約っぽい

  • Maybe は直接できないので二重にやるなるほど

  • このへん Haskell 勝手にやってくれるの良いですね。

7.10

  • if の中になんでも入れれるの便利で java 使ってると羨ましくなる。
型クラス制約付きの引数を具体型によってパターンマッチングはできるのか?
hoge :: (Num a) => a -> String

といった関数がある時、Int とそれ以外で挙動を変えたい場合はどうすればいいのか?と思って調べたメモ。

データ型の直和型や列挙型をパターンマッチングするのは簡単にできるのですが...

試しに

hoge :: (Num a) => a -> String
hoge (x::Int) = "aaa"
hoge x = "hoge"

などと書くと Perhaps you intended to use ScopedTypeVariables と言われたのでそうしてみたけど関係なさそう。

しばらく考えたらこういう案件こそ型クラスを使うべきなんだなあと気付きました。

  • 最悪っぽいの

http://stackoverflow.com/questions/4131552/haskell-check-if-integer-or-check-type-of-variable

にある二番目の解答。

import Data.Typeable
isInteger :: (Typeable a) => a -> Bool
isInteger n = typeOf n == typeOf 1

最悪っぽい。

  • yesnoIf が簡単に実装できるのは便利だなあという感じ。

7.10

  • instance Functor [] where と書くことになるのは、class Functor
class Functor f where
   fmap :: (a -> b) -> f a -> f b

と宣言されていて、これによって f 自体は 具体型ではなく型コンストラクタになる ためである。

そのため、

class NonyFunctor f where
    fmap :: (a -> b) -> f a -> f b

instance Functor Int where

として具体型を入れると

The first argument of ‘Functor’ should have kind ‘* -> *’,
  but ‘Int’ has kind ‘*’
In the instance declaration for ‘Functor Int’

と怒られることになる。

  • fmapmap が同じなの、なるほどという感じ

  • 要するに、fmap は型クラスの中にある値に直接関数をどうこうしたいときに使うものみたいな感じですね

  • Either a は型コンストラクタ

    • こういうのを見ると左にエラー入れるってのもなるほど感ある
    • Either いみわからんとか言ってたけど tuple とはまた別の物ですね。片方しか入れられないし。

7.11

  • :k 便利

:k Num とすると Num :: * -> GHC.Prim.Constraint となった。

これは型クラス制約のことで、 Num a は型クラス制約となることの現れ。

:k FunctorFunctor :: (* -> *) -> GHC.Prim.Constraint となり、型コンストラクタを引数に取り型クラス制約になるということ。

感想

  • またまた久しぶりになってしまった...
  • 型クラス制約と型コンストラクタがごちゃごちゃになってたので 7.11 読んでよかった。
    • 段々難しくなってきた気もするけどしばらくブランク空いたからかもしれない

*1:今は Arch LinuxVM でやってます

Android Studioでのビルドをクラウドで行い開発を高速化する

こんにちは。学校も始まり最近は健康な時間に起きています*1

今回は 手元の Android Studio からビルドのみをクラウドで行う ことによって快適な生活を手に入れる話です。

概要

  • ビルドを移譲できる Android Sutudio のプラグインを書きました
  • これを使ってクラウドのリッチなマシンでビルドを行い高速化
  • ビルドのみが移譲されるので操作感は変わらない
  • 実装は割りと無理矢理

*1:ちなみに現在風邪を引いて寝込んでいます

続きを読む

Raspberry pi に GitWeb を入れた

昔 Raspberry Pi に GitLab を入れた記事を書きました。

nonylene.hatenablog.jp

この時、重い重い言いながら運用していました*1が、ニヶ月ほど前にSDカードが壊れる事件が起きて、再構築した時に入れるのがめんどくさくて*2入れてませんでした。そもそもSDカードが壊れたのが GitLab をアップデートして reconfigure 中に突然再起動した後だったのでちょっと怖いというのもあります((

しかし4月になり実験用のレポジトリを作る必要が出てきたため、今回は GitLab をやめて GitWeb を使うことにしました。

*1:Slack に投稿できたりして便利

*2:まあ Ansible コマンド打つだけなんですけど...

続きを読む