CentOS7+PHP7+memcached
はじめに
構築済みのサーバーでmemcachedの書き込みが遅い、という問題に出くわしたのでそれの解決方法をまとめます。
想定環境
- CentOS7
- PHP7
- memcached
- libmemcached 1.0.16 <- これが原因
簡単な解説
問題はmemcached本体ではなくPHPが使うlibmemcachedがバグっていたからでした。 qiita.com この問題はlibmemcachedのバージョンアップで解決されます。
ただし、自分でコンパイルしてインストールはしたくありません。 いえ、僕はコンパイル好きなのでしたいのですが、諸事情で許されません。 なのでパッケージのインストールのみで対応します。
解決方法
CentOS標準のパッケージでは現在(2019年3月25日)でもlibmemcachedは1.0.16です。 しかしCentOSにはremiリポジトリというものがあり、PHPに関するパッケージが一通り準備されています。またパッケージだけでなくPHP本体も用意されており、CentOSの標準リポジトリとは別のPHPをインストール可能です。 この別のPHPは/usr/local以下にはインストールされず、/opt以下にインストールされます。同様に関連するパッケージも/opt以下にインストールされるのでPATHさえ通してしまえば使い方は標準のPHPと同じです。
今回の解決方法では別のPHPのインストールから行います。 その前に既存のPHPをアンインストールします。
$ sudo yum remove php
あとはインストールするだけ。 *は好きなマイナーバージョンを指定してください
$ sudo yum install php7* php7*-php-pecl-memcached
KoinとMockKでAndroidTestを簡単に(Android 9以上)
はじめに
前回Koinについていろいろ調べながら使い方をしらべていたのですがMockitoをAndroidTestで使おうとすると はまりどころが多くて嫌になりました。テストの重要性が上がるにつれ実情に合わなくなってきたのでしょうか。
そんなときAndroidテスト全書をぱらぱらと眺めていたら、Kotlin向けにMockKというライブラリがあるよ、という文字が目に入りました。
さらにうれしいのがAndroidのサポート状況です。
Android9上でのAndroidTestに関してはPowerMockのようなライブラリを必要とすることなくPrivateメソッドなどのテストを行えるのです。
これはとてもうれしい。
テスト方法
Mockitoに比べてテストを書く上での決まり事が少なくい印象です。 例えばRunWithにMockitoJUnitTestRunnerを指定しなくてよくなっています。 またAnnotationを使ったりするためにMockitoAnnotations.initMocks(this)が必要でしたが不要になっています。
KoinとMockKを使ってテストを書いた場合は以下になります。
@RunWith(AndroidJUnit4ClassRunner::class) class MyFragmentTest : KoinTest { fun MyFragment起動時にPresenterのonViewCreatedとonResumeが呼ばれる() { loadKoinModules(listOf(module(override = true) { factory<MytContract.Presenter>(override = true) { (view: MyContract.View) -> spyk(MyPresenter(view, get())) } single<MyContract.Repository>(override = true) { spyk(MyRepository()) } })) val scenario = launchFragment<MyFragment>() scenario.onFragment { val mockPresenter = it.presenter verifySequence { mockPresenter.onCreateView() mockPresenter.onViewCreated() mockPresenter.view mockPresenter.onResume() } } } @Test fun MyFragment起動時にPresenterがrequestを呼ぶ() { loadKoinModules(listOf(module(override = true) { factory<MyContract.Presenter>(override = true) { (view: MyContract.View) -> spyk(MyPresenter(view, get()), recordPrivateCalls = true) } single<MyContract.Repository>(override = true) { spyk(MyRepository(), recordPrivateCalls = true) } })) val scenario = launchFragment<MyFragment>() scenario.onFragment { val mockPresenter = it.presenter verify(exactly = 1) { mockPresenter["requestFirstPageIfNotRequested"]() mockPresenter["requestFirstPage"]() mockPresenter["request"](1) } } } }
特徴的なところをいくつか紹介します。
spyk
今回はmockk(mockitoにおけるmock)は使わずspykのみを使っています。
今回の例ではテストを2つ書いています。
1つ目はpublicメソッドのみ
2つ目はprivateメソッドのみです。
publicメソッドのみ
publicメソッドのみを使ったテストは難しいことはありません。
今回の場合はFragmentがResumeされるまでの動作を検証します。
FragmentがResumeされるまでにPresenterは
1. onCreateView
2. onViewCreated
3. view(プロパティの呼び出し)
4. onResume
を呼び出します。
この順番で呼ばれることを検証したい場合、MockKではverifySequenceを使用します。
verifySequenceを使ったテストの抜粋です。
verifySequence { mockPresenter.onCreateView() mockPresenter.onViewCreated() mockPresenter.view mockPresenter.onResume() }
verifySequenceの特徴は呼ばれるメソッドが抜けもれなく書かれている必要があることと順番にも依存することです。 たとえばmockPresenter.onViewCreated()が書かれていない以下のようなテストでは失敗します。
verifySequence { mockPresenter.onCreateView() mockPresenter.view mockPresenter.onResume() }
同様にメソッドに抜けもれがなかったとしても順番が間違っていても失敗します。 たとえばonCreateViewとonViewCreatedの順番が入れ替わっている以下のようなテストでは失敗します。
verifySequence { mockPresenter.onViewCreated() mockPresenter.onCreateView() mockPresenter.view mockPresenter.onResume() }
privateメソッドも含んだテスト
privateメソッドをテストする場合はspykメソッドの引数にrecordPrivateCalls = trueを指定します。
こうすることでprivateメソッドもテストすることが可能になります。
テスト時のprivateメソッドの検証は以下のようになります。
mockPresenter["requestFirstPageIfNotRequested"]()
publicメソッドはメソッドとして呼び出していましたが、privateメソッドでは文字列を指定することでモックオブジェクトから間接的に呼び出すことになります。
実際のテストコードでは以下のようになります。
verify(exactly = 1) { mockPresenter["requestFirstPageIfNotRequested"]() mockPresenter["requestFirstPage"]() mockPresenter["request"](1) }
今回はverifySequenceを使わずverifyを使っています。
verifySequenceとの違いはメソッド呼び出しの順番や呼び出されるメソッドの抜けもれを意識しなくていい点です。
exectly = 1
はメソッドが呼び出される回数を指定しています。
このように書くことでprivateメソッドをテストすることが可能です。
ではverifyではなくpublicメソッドと同じくverifySequenceを使った場合どうなるでしょうか?
verifySequence { mockPresenter.onCreateView() mockPresenter.onViewCreated() mockPresenter.view mockPresenter.onResume() }
これは失敗します。なぜならprivateメソッドもモックオブジェクトが監視しているためです。
privateを含めたverifySequenceのテストは以下のようになります。
verifySequence { mockPresenter.onCreateView() mockPresenter.onViewCreated() mockPresenter.view mockPresenter.onResume() mockPresenter["requestFirstPageIfNotRequested"]() mockPresenter["requestFirstPage"]() mockPresenter["request"](1) }
Koinのテスト時の書き方
KoinはDIライブラリです。
今回はFragment内で使っているPresenterとRepositoryをモックオブジェクトにするために使っています。
Koinを使ったDIの抜粋です。
loadKoinModules(listOf(module(override = true) { factory<MytContract.Presenter>(override = true) { (view: MyContract.View) -> spyk(MyPresenter(view, get())) } single<MyContract.Repository>(override = true) { spyk(MyRepository()) } }))
loadKoinModules
でDIするオブジェクトを指定しています。
KoinではDIするオブジェクトの初期化はstartKoinメソッドで行います。しかしstartKoinは1回しか呼び出すことができません。
startKoinはApplicationクラスで呼び出しているためテスト時に呼び出すことができないのです。
そのためテスト時にはstartKoinメソッドの代わりにloadKoinメソッドを呼び出します。
使い方はstartKoinメソッドと同じですが、startKoinで指定済みのオブジェクトを上書きする必要があるので、module、factory、singleでは引数にoverride = true
を指定しています。
これによりテスト時に対象のオブジェクトを置き換えることが可能です。
今回は置き換えにspykメソッドを使っています。
またKotlinではクラスは基本的にfinalになります。mockitoではopenでクラスを作成しないとspyすることはできませんでしたがMockKではopenせずにspykでモックオブジェクトを作ることができます。
Koin でDI
発端
DIについて勉強したいと思いDIフレームワークを探していたところKoinというDIフレームワークを見つけました。 しかし最近メジャーバージョンが上がったためか情報が少なかったのでメモとしてまとめます。
Koinとは
insert-koin.io
KoinとはKotlinで書かれたDIフレームワークです。
拡張関数やbyによるDelegateを多用しています。
なのでKotlinに慣れてないと所見では理解しにくいのではないかと思います。僕は理解できなかったですが、とりあえず書くことで理解できました。
拡張関数とか使っているのでKotlinじゃないと使えないのか?と思っていたのですがJavaからも呼び出せるようです。バイトコードが同じだからそれはそうですよね。
またKoinを使おうと思った理由はAnnotationを使わないということです。
Annotationは便利なのですが、エラーになった時にエラーを追いにくいのが苦手です。
Koinのバージョンについて
2018年9月にメジャーバージョンが1になったそうです medium.com またフレームワークのAPIが大幅に変わっているようで、ちょっと情報がすくない状態です。 さらに2.0のbetaが公開されており、まだまだAPIが変わる可能性があり、長期的に利用するにはちょっと辛いかもしれません。(Androidのほうがつらいので気にならない気もします)
AndroidでのKoinの使い方
基本的な使い方
interface MyContract { interface Presenter {} interface View{} } class MyPresenter: MyContract.Presenter {} class MyFragment: MyContract.View { val presenter: Presenter by inject() } class MyApplication : Application() { override fun onCreate(){ super.onCreate() // start Koin! startKoin(this, listOf(myModule)) } val myModule = module { factory<MyContract.Presenter>{ MyPresenter() } } }
ざっとですが、KoinでのDIはこんな感じでできます。
Koinの初期化はApplicationクラスで行います。
startKoin
メソッドでDIに使うクラスの初期化を行っています。
startKoin
メソッドに渡す変数はKoinのModuleクラスのListです。Listに与えているModuleのインスタンスはApplicationクラスの拡張関数で生成しています。
Moduleインスタンス内ではfactory、singleメソッドを使うことができ、それぞれFactoryメソッドとして、Singletonを生成するメソッドとして動作します。
factory、singleメソッドの戻り値は型推定によって自動的に設定されるか、明示的に設定することが可能です。DI時には型かfactory、singleに指定可能なnameを頼りにインスタンスが生成されます。なのでDIする対象がinterfaceの場合はfactory、singleの戻り値もinterfaceにしておかないとクラスが見つけられずにエラーになってしまいます。
以上の理由から今回の例ではMyViewクラスのpresenterの型がMyContract.Presenterなのでfactory<MyContract.Presenter>
としてfactoryの戻り値を明示的に指定しています。
コンストラクタに引数がある場合
先ほどの例ではDIするクラスのコンストラクタには引数がありませんでしたが、コンストラクタに引数がある場合について示します。
interface MyContract { interface Presenter {} interface View{} interface Repository {} } class MyPresenter(val view: View, val repository: Repository): MyContract.Presenter {} class MyFragment: MyContract.View { val presenter: Presenter by inject { parameterOf(this) } } class MyRepository: MyContract.Repository {} class MyApplication : Application() { override fun onCreate(){ super.onCreate() // start Koin! startKoin(this, listOf(myModule)) } val myModule = module { factory<MyContract.Presenter>{(view: MyContract.View) -> MyPresenter(view, get()) } single<MyContract.Repository>{ MyRepository() } } }
この例ではMyPresenterのコンストラクタにviewとrepositoryの引数を与えています。
このような引数付きのコンストラクタではfactory<MyContract.Presenter>{(view: MyContract.View) -> MyPresenter(view, get()) }
のようにしてインスタンスを生成します。viewはinjectメソッドの引数から与えられるようにし、repositoryはSingletonで生成するのでkoinのget()メソッドによって与えられます。
MyFragmentでのpresenterへのDIはinjectメソッドを介してval presenter: Presenter by inject(this)
のようにして引数にviewであるthisを与えています。
ActivityやFragment以外でDIしたい場合
Koinは拡張関数を使ってDIしています。なので既知のクラスを使っている必要があります。
AndroidではActivityとFragmentが既知のクラスにあたり、これらに拡張関数が設定されているため何も意識せずにDIすることができます。
しかし、ActivityやFragment以外でDIしたい場合もあります。 このような場合はKoinComponentインターフェースを使用します。
KoinComponentはメソッドも何もないinterfaceなのでどのようなクラスにも使うことができます。
今回試していた内容ではRetrofitのServiceインターフェースで使いました。
object API : KoinComponent { var apiKey: String = "" val service: Service by inject{ parameterOf(apiKey) } }
テスト
DIしたからにはテストをしなければなりません。なんのためにDIしたのかわかりませんから。
テストするにはテストクラスでKoinTestインターフェースを継承します。
class MyTest: KoinTest { @Test fun 例外なく動く() { startKoin(listOf(module { factory<MyContract.Presenter> { mock(MyContract.Presenter::class.java) } single<MyContract.Repository> { mock(MyContract.Repository::class.java) } })) } }
継承したクラスではテスト実行時にApplicationクラスで実行していたstartKoinメソッドで初期化します。
またインスタンスをモック化したいのであればmockitoを使ってインスタンスを生成すれば良さそうです(時間がなくて未検証)
AndroidTestの場合
koinもMockitoを使っているらしく設定がややこしい。
build.gradleの抜粋。AndroidTestを実行するのに必要なところだけを抜き出している。
android { packagingOptions { pickFirst 'mockito-extensions/org.mockito.plugins.MockMaker' } } dependencies { androidTestImplementation ("org.koin:koin-test:1.0.2") { exclude group: 'org.mockito' } androidTestImplementation "org.mockito:mockito-android:2.24.5" }
コンパイル時にこんなエラーが出ていて調べたら下のissueが見つかったので使っている。
More than one file was found with OS independent path 'mockito-extensions/org.mockito.plugins.MockMaker'
github.com
それでも直らずkoin mockito
でpackagingOptionsを使う方法が書かれていたので使った。
github.com
さらにMockitoがAndroidTestでも使えるようになっていた。それがorg.mockito:mockito-android:2.24.5 またMockitoがAndroidTestでも使えるようになったことでテストの書き方も変わっている。
テストランナーにはMockitoJUnitRunnerを使うことでよいようだ。 今回はKoinを使ったテストも行うためKoinTestインターフェースを継承したテストを作成している
@RunWith(MockitoJUnitRunner::class) class MyFragmentTest : KoinTest { ... }
なぜかあまり報じられないがAndroidがその座をFuchsiaに譲るかもしれない。
はじめに
以前にも同じ意見はあった
www.gizmodo.jp
決して私だけが思い込んでいるわけではない。
根拠となるニュースや情報など
- GoogleのFlutterへの注力が半端ない。対してGoogleによるAndroid開発に関する動画に新しい投稿はかなり少ない。
Google Developers - YouTube - Fuchsiaの標準開発フレームワークはFlutter。
Google Fuchsia - Wikipedia
→ Flutterはクロスプラットフォーム。AndroidでもアプリをFlutterで開発していればFuchsiaにスムーズな移行が可能 - FuchsiaにAndroidアプリのRuntimeが追加された。
Google's Fuchsia OS confirmed to support Android apps - 9to5Google
→ だれでも思うだろうがこれはかなり大ニュースだ。既存のAndroidアプリをそのままFuchsiaでも動かせるので既存ユーザーをそのまま引き込める。 個人的にはこんな面倒なことを思い付きでやるとは思えないので、Googleは本気で取り組んでいるんだと思っている。
これらの内容から、開発者側はAndroidアプリを開発しながらFuchsiaへ移行可能になり、Androidユーザーは既存のアプリをFuchsiaでも使うことができる。この二つがそろうことはとても大きい。既存の開発者もAndroidユーザーもそのままFuchsiaに引き込むことができるから。
移行するわけないだろうという反対意見もある
FuchsiaがAndroidを置き換えるかもという意見に対して否定的な意見もあるが、
既存ユーザーを気にかけているような内容がかかれているので、
それはAndroidアプリのRuntimeが追加されたことで解決されてしまったことになる。
Is Fuchsia OS the next Android? - Dignited
私見とまとめ
ここで書きたいことはAndroidプラットフォームは今後縮小する可能性が高い、ということはもちろんだが、今後Androidアプリを開発しようとしている開発者はAndroid SDKで直に開発するのではなく、Flutterを使うべき、ということだ。
Flutterを使うべき理由はFuchsiaのアプリ開発にスムーズに移行できることは当たり前だが、AndroidのViewは複雑すぎ独自のViewを正しく作ることが難しく、明記されていない仕様、Android OSのバージョンや端末ごとで異なる動作など問題が多いので、Flutterを使ってこのような問題とは無縁な状態で開発するべきだと考えているからだ。
ソースコードを見ればわかる?そうではない。Viewのそもそもの設計や画面制御が良くないと感じているのでより良いものがあるならそんなものは使うべきではない、そう考えているだけだ。
反発する意見はもちろんあると思うが、まじめに開発しているAndroid開発者ほど意味不明なバグに悩まされてきたと思うので、それらが少しでも解決されて欲しいと思っているなら・・・・AndroidがFuchsiaにその座を譲る(かもしれない)こと、Flutterで開発すべきという内容に納得できるのではないかと思う。
Flutterでネットワーク通信
はじめに
覚書のために書いています。
使うパッケージ
- http
使い方
GET get(
) POST post(
, body: )
戻り値は Future< Response >
Responseの処理はFutureのthenメソッドにCallbackとして渡す。
余談
パッケージのインポートは
import 'package:http/http.dart' as http;
と書くが、
import 'package:http/http.dart'
と書くとネームスペースがグローバルになるみたい。
Dartの勉強がまだまだ。