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

なにも わからぬ

パソコンとプログラミング関係をメモっていきたい

kotlinで複数回献血クラブのスクレイピングライブラリ&Serializableでハマった話

Kotlin Androidアプリ開発

例のごとく初心者が書いた記事です。突っ込み歓迎します

kotlinで書いた複数回献血クラブ用のスクレイピングライブラリが一応動く形になったので、キリもないしgistに貼って一旦公開してみる。ほんとは献血クラブ非公式クライアントとしてサクッとアプリにして公開したかったけど、Android固有の知識がいろいろ求められてしんどい。自分以外誰得な内容だし習作のテーマ間違った気がする……!!

今のところ

val scraper = Scraper(ID, PASS1, PASS2)

インスタンスを作るとログイン後の1ページ分(最大10回分)の献血データをダウンロードしscraper.recordIndexscraper.bloodDataに書き込まれ、2ページ目以降のデータも取得したければscraper.next()を繰り返す……だけのもの。自分以外使わないだろうけど少しずつ修正していきます。

長いのでリンクのみ
A scraper for www.kenketsu.jp

Serializableやtransientでハマった話

AndroidのActivityをまたいでデータをやりとりするにはintent.putExtra()を使うようだけど、文字列型や整数型などのプリミティブ型以外の自作クラスのインスタンスなどを送る場合は、バイト列などに変換できることを保証するSerializableであることを示さなければならないそう。Serializableはメソッドも何もないマーカークラスというやつで、これを継承しているクラスのみがSerializeできるものとしてActivityをまたぐことができ、インスタンス変数なども含めてSerializableを継承していないクラスが混じっているものをまたがせようとするとエラーが出る。

で、自作クラスには全部Serializableをおまじないのように継承させたが、外部ライブラリであるOkHttpは継承クラスを作ろうとしてもなんかうまく行かず?どーすっぺと適当にぐぐっていたら見つけた@transientを頭に付けてインスタンス変数を宣言すればいい、というのを意味もわからず実行した。

class Scraper(...) : Serializable {
    @transient var client:OkHttpClient

そのときのコードは、Activityをまたぐ前にclientを使ったメソッドを実行し、またいだ後にインスタンス変数の一部を参照するだけだったので問題が露呈しなかったが、作り続けていくうちに当然Activityをまたいだ先でもメソッドを実行させるようになり、そこでclientへのアクセスがあったときにNullPointerExceptionが起こるようになった。困っていろいろググったところ、transientはSerializeの対象外にするという宣言であり、Serializeしてデータを送るときにtransientの宣言されたデータは送られない=nullとなる?というようなことがわかった。

参考:Java直列化メモ(Hishidama's Java Serializable Memo)
【Java】Serializableの基本(シリアライズ・直列化) - TASK NOTES

あーやっちまった全部作り直しかーと思っていたら、static変数はもともとSerializeの対象外であり、kotlinではcompanion objectの中に突っ込むことで実現できるとわかった。

class Scraper(...) : Serializable {
    companion object {
        lateinit var client:OkHttpClient
    }

これでthis.clientScraper.clientに変わったくらいで事なきを得た。HTTPライブラリのインスタンスをクラス変数に持たせるのはどうなのよと思うし、そもそもインターネットアクセスを全部外に出すべきなんじゃ、とも思うのだけど……。