EDIT MODE

モバイルアプリ開発に関することを書いています

ZOZOテクノロジーズに入社して2ヶ月が経ちました

タイトルの通りですが、5月1日から株式会社ZOZOテクノロジーズで働き始めました。

今まで入社エントリは書かない派だったんですが、働いていて楽しいなと感じることを言語化してみたいなと思い、書くことにしました。 なお、後述する内容は僕が所属しているチームから見た感想なので、チームによっては当てはまらない内容もあるかもしれません。

入社に至った背景

ZOZOテクノロジーズのことは、以前所属していた会社で子供服ECに携わっていたときから、ファッションECの大手として良く参考にしていました。 その後、転職活動を始めたあたりでAndroidテスト全書の執筆や様々な登壇などで活躍されている堀江さんに「カジュアル面談しませんか?」と声を掛けて頂いたことや、丁度その時「全国在宅勤務制度(現在は募集が終了しています)」での採用を行われていたことがきっかけで、カジュアル面談を経て選考を受けました。

tech.zozo.com

この時に興味を持った理由としてはファッションEC自体の興味に加え、ZOZOが前澤前社長から澤田現社長にバトンタッチされたことやZホールディングス傘下となったことで、面白いステージなんじゃないかなと感じたからでした。

その後、幸いにもオファーを貰うことが出来たので入社を決断しました。 以下に入社2ヶ月が経過して感じたことを、書き出してみようと思います。

いいなと感じた点

入社オリエンがスムーズだった

コロナ禍ということで、勤務地に関わらずオンラインでの実施でしたがとてもスムーズで良かったです。特に人事担当の方が、スケジュールや資料等の事前準備、配属先チームとの接続をしっかり行ってくれたのだなということが伝わってくる内容でした。

いい人が本当に多い

選考を受けているときから、ZOZOテクノロジーズはいい人ばかりという話をよく聞きました。正直、いい雰囲気の組織ですよというニュアンスを一般的に言っているだけだろうなと、受け取っていました。ところが今は言葉通りだなという実感があります。なぜ「いい人」と感じるのだろうなと考えたところ、誰と話していても相手をきちんと敬う姿勢を感じるからだと気付きました。

その後、上長との1on1でそのことを話したところ、その上長は「社内に素晴らしい上司がいて、自然と見習いたいと思いお手本にしていた。たぶん他のメンバーも同じような感じで、そういったことが社内に伝搬していってるからだと思う。」ということを聞き、すごく腹落ちした気がします。

本当にいい人ばっかりです。

改善や挑戦を推奨する組織風土になっている

親会社からの案件や出店対応など本当にやることが多いのですが、その中でもサービス改善や業務フローの改善を自分たちでもアイデアを出しアウトプットしていこうという意識が強いなと感じています。会社が掲げるミッション「70億人のファッションを技術の力で変えていく」を、自分たちがやるんだという空気を感じ、とても楽しいです。

1on1がしっかり機能していると感じる

上長との1on1を週1でやってます。雑談もOKなのですが、基本は部下の成長を促す場として位置づけられているので、普段出来ない相談などが出来るので毎週とても楽しみにしています。

入社前に1on1をやっているという話は聞いていたので以下の書籍を読んだのですが、イメージしていたものとほぼ同じ形式で読んでおいて良かったなと思いました。

www.amazon.co.jp

課題があるなと感じた点

生産性の可視化

やはり親会社であるZOZOに対して、どういった業務にどのくらいのリソースが割り当てられ、どのくらいの成果が出たかを示す必要があるためだと思いますが、生産性を可視化するための作業が多いなと感じています。少しでもそこを省力化しようと、いろいろな改善策に取り組んでいますが、結構なリソースを必要とするので辛いなと感じます。

関係性が遠い人とのコミュニケーションが取りづらい

公表されている従業員数が405人(2020年末時点)の組織なので、slackですらコミュニケーション取らない人の方が多いです。コロナ禍でなければオフィスに行ってみたりすればまた違うんでしょうけど、今は本当に業務上関係性が近い人としかコミュニケーションが取れていません。

業務上絡まなくてもコミュニケーションが取れる、何か良い方法があれば試してみたいなと考えています。

終わり

まだまだ色々と「いいなと感じる点」や「課題を感じる点」があるんですが、主なところは書き出せたかなと思います。 思ったより書いていて面白かったので、また半年くらい経ったら今感じてることと比較しながら書いてみたいなと思いました。

一人開発でつまづいたときの処方せん for DroidKaigi 2020

はじめに

この記事は、やむなく中止となってしまったDroidKaigi 2020で発表予定だった、「一人開発でつまづいたときの処方箋」の内容をブログ用に書き下ろしたものになります。コロナ禍のこの半年、公私でいろいろなことがありすぎて全然気持ちが前を向かず、記事を書くのにめちゃくちゃ時間が掛かってしまいました。なんとか書き終えて良かったです。

droidkaigi.jp

なおDroidKaigiでは40分で発表する内容のため少し長文となりますが、もし興味を持っていただけたら最後まで読んでいただけると嬉しいです。 サッと眺めてみたい方は、以下のSpeaker Deckにアップしてあるスライドを見ていただく方がいいかもしれません。

一人開発でつまづいたときの処方箋 - Speaker Deck

目次

  • 自己紹介
  • 発表内容
    1. ビルドが通らないときの処方せん
    2. アプリがクラッシュするときの処方せん
    3. アプリの評価が上がらないときの処方せん
  • まとめ
続きを読む

モバイルアプリデバッガーFlipperの使用感

Androidのネイティブアプリで使ってみたら、思ったより使い心地良かったので備忘録的に使用感を書いていきます。

Flipperとは

iOSもしくはAndroidアプリをデバッグするためのプラットフォームです。シンプルなデスクトップアプリとして提供されていて、Mac/Linux/Windowsで利用することが出来ます。Flipperの機能はプラグイン形式で提供されているので、自作し拡張することも可能です。

また、ソースコードはOSSとして公開されていて、コントリビュートすることも可能です。

fbflipper.com

Stethoとの違い

facebook発のAndroid用デバッグツールとしては、Stethoが有名だと思います。主にネットワークやデータベースなどの中身を見るために、重宝されているかと思います。

FlipperとStethoの主な違いは、こんな感じです。

  Flipper Stetho
クライアント デスクトップアプリ Chrome DevTool
対応OS Android/iOS Android
機能拡張 ×

facebook.github.io

Flipperデスクトップアプリの使い方

クライアントアプリを起動した後で、実機もしくはエミュレーター/シミュレーターを接続すると認識し、Flipper SDKが組み込まれたアプリが起動されると、下図のようにアタッチされます。

左側のペインにアプリに組み込まれた機能が並び、クリックすると右側のペインに対応するUIが表示されるというシンプルなクライアントアプリです。(※下図はMac版デスクトップアプリです。)

キャプチャ機能があるのが地味に嬉しいですね。

f:id:androhi:20191127200103p:plain
Flipperクライアントアプリ

公式提供のプラグイン

2019年11月現在、公式が提供しているプラグインは全部で9個あります。(※対応OSは2019年11月時点のものです。)

Plugin 機能 Android iOS
Layout Viewの階層やプロパティをを解析してくれるお馴染みの機能です。
もちろん値を上書きすることも出来ます。
LithoやComponentKitにも対応したり、結構多機能。
Navigation ディープリンクに関するデバッグを楽にしてくれるます。
ディープリンクのログを自動記録したりアプリに送ったり出来ます。
×
Network 通信の内容をダンプしてくれるお馴染みの機能です。
AndroidではOkHttp用のInterceptorクラスも用意されていて実装が簡単です。
Database データベースの読み書きが出来ます。
クエリも発行出来ます。
Images ネットワーク上の画像に関するデバッグを楽にしてくれます。
ただしこのプラグインでサポートしている画像ローダーは、Frescoのみです。
×
Sandbox クライアントアプリからモバイルアプリに値を送るため仕組みを提供してくれます。
(イメージとしては簡易的なRemoteConfigみたいな感じかなと思います。)
×
SharedPreference モバイルアプリのPreferenceファイルの読み書きが出来ます。
LeakCanary メモリーリークを検知してくれるお馴染みのツールが、Flipperにも対応してくれています。 ×
CrashReporter モバイルアプリがクラッシュしたことを検知して、通知してくれます。 ×

実装方法

公式サイトに詳しく載っていますので、その通り進めれば問題なかったです。またiOS/Androidともにサンプルコードもしっかりあるので、不明点があればそちらも見た方がいいと思います。

fbflipper.com

Android向けTips

OkHttpのNetworkInspectorにFlipperを使いたいとき、OkHttpClientをDIで注入する方法がドキュメントに無かったのですが、以下のようにやるとうまくいきました。(以下はDaggerを使った場合の例です。)

    @Singleton
    @Provides
    fun provideHttpClient(context: Context): OkHttpClient {

        // ApplicationクラスでセットしたNetworkPluginのインスタンスを取得
        val plugin = AndroidFlipperClient.getInstance(context).getPlugin(NetworkFlipperPlugin.ID)

        return OkHttpClient.Builder()
            .connectTimeout(60, TimeUnit.SECONDS)
            .readTimeout(60, TimeUnit.SECONDS)
            .writeTimeout(60, TimeUnit.SECONDS)
            .addNetworkInterceptor(FlipperOkhttpInterceptor(plugin))
            .build()
    }

各プラグインはIDを持っているので、そこからプラグインのインスタンスを取得出来るようです。

まとめ

FlipperではStethoでやや不便だなと感じていた、Chrome DevToolを使う点やiOS未対応な点(まだ未対応のプラグインが多いですが...)などが解消されていて、とても使いやすいデバッグツールでした。

開発元が「Stethoは引き続き提供する」と言っていますが、個人的にはすぐにでも乗り換えてしまってもいいなと感じました。(僕はたぶんプロダクトに投入すると思います。)

AndroidXでのバックキー制御

AndroidX以前

今までバックキーの制御と言えば、例えば以下のように Activity#onBackPressed() でゴニョゴニョしていました。

    override fun onBackPressed() {
        // バックキーイベントでFragmentで何かしたいとき
        val fragment = supportFragmentManager.findFragmentByTag("HogeFragment")
        fragment?.something()

        // スタックに積んだFragmentを1個前に戻したいとき
        if (supportFragmentManager.backStackEntryCount > 0) {
            supportFragmentManager.popBackStack()
        } else {
            super.onBackPressed()
        }

BackStackとかはまだいいですけど、Fragment側の処理をcallしたいときなどはActivityに特定のFragmentの依存が入ってしまって、残念な気持ちになっていました。

AndroidX以降

AndroidXで提供されているActivityには、Version 1.0.0-alpha01で Activity#addOnBackPressedCallback () というメソッドが追加され、 OnBackPressedCallback インターフェース経由でバックキーのイベントを受けられるようになってました。

つまりFragment内にバックキー制御のロジックを閉じ込めることが出来るということです。控えめに言って最高です。

developer.android.com

こんな感じで書くことが出来ました。

...
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
...

class MyFragment : Fragment() {

    private val callback = object : OnBackPressedCallback {
        override fun handleOnBackPressed(): Boolean {
            return if (isHoge) {
                something()
                true
            } else {
                false
            }
        }
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        activity?.addOnBackPressedCallback(callback)
        ...
    }

    override fun onDestroy() {
        super.onDestroy()
        activity?.removeOnBackPressedCallback(callback)
    }

これだけでも嬉しいですが、リファレンスを見るとArchitecture ComponentのLifecycleにも対応していました。

developer.android.com

つまり先程のサンプルコードは、以下のように書けます。

...
import androidx.activity.OnBackPressedCallback
import androidx.fragment.app.Fragment
...

class MyFragment : Fragment() {

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        activity?.addOnBackPressedCallback(this, object : OnBackPressedCallback {
            override fun handleOnBackPressed(): Boolean {
                return if (isHoge) {
                    something()
                    true
                } else {
                    false
                }
            }
        })
        ...
    }

これでaddしたcallbackの開放を気にする必要が無くなりました。コード量も減るし、読みやすいです。 (OnBackPressedCallbackはJavaで実装されているのでSAM変換も可能ですし、実際にはさらにコード量は減らせます。)

これを発見したときにAndroidXに移行して良かったなと、心の底から思いました。AndroidX最高です。

MockKを使ったViewModelのテスト

概要

以下のような、シンプルなViewModelのテストをMockitoを使って書いたのですが、これをMockKで書き換えることにしました。

Mockitoを使ったViewModelのテスト

@RunWith(RobolectrictTestRunner::class)
class UserViewModelTest {

    @Mock private lateinit var mockRepository: MyRepository
    @Mock private lateinit var observer: Observer<List<UserPoint>>
    
    private lateinit var viewModel
    
    @Before
    fun init() {
        val immediate = object : Scheduler() {
            override fun createWorker(): Scheduler.Worker {
                return ExecutorScheduler.ExecutorWorker(Executor { it.run() })
            }
        }
        RxJavaPlugins.setInitIoSchedulerHandler { _ -> immediate }
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { _ -> immediate }

        MockitoAnnotations.initMocks(this)
    }
    
    @Test
    fun someTest() {
        val userList = listOf()
        `when`(mockRepository.findUserList()).thenReturn(Observable.just(userList))

        viewModel = UserViewModel(repository)
        viewModel.users.observeForever(observer)
        viewModel.loadUserList()

        verify(mockRepository, only()).findUserList()
        verify(observer).onChanged(userList)
    }
}

MockKとは

MockKはKotlinのためのMocking Libraryです。 ドキュメントも充実しているし、KotlintだとMockitoで困ることがほぼ解決出来ます。

便利なポイントはドキュメントにも列挙されています。個人的にはObjectのMockがシュッと書けて好きです。

mockk.io

MockKでViewModelのテスト

前述のコードをMockKで書き直すと以下のようになりました。

MockKを使ったViewModelのテスト

@RunWith(RobolectricTestRunner::class)
class UserViewModelTest {

    @MockK
    private lateinit var mockRepository: MyRepository
    @MockK
    private lateinit var observer: Observer<List<User>>

    private lateinit var viewModel

    @Before
    fun setup() {
        val immediate = object : Scheduler() {
            override fun createWorker(): Scheduler.Worker {
                return ExecutorScheduler.ExecutorWorker(Executor { it.run() })
            }
        }
        RxJavaPlugins.setInitIoSchedulerHandler { _ -> immediate }
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { _ -> immediate }

        MockKAnnotations.init(this)
    }

    @Test
    fun someTest() {
        val userList = listOf()
        every { mockRepository.findUserList() } returns Observable.just(userList)

        viewModel = UserViewModel(mockRepository)
        viewModel.users.observeForever(observer)
        viewModel.loadUserList()

        verify { mockRepository.findUserList(authToken) }
        assert(viewModel.users.value == userList)
    }
}

ところが、これを実行するとMockKのイニシャライズが失敗し、以下のようなエラーが発生してしまいました。

java.lang.NoClassDefFoundError: io/mockk/proxy/jvm/dispatcher/JvmMockKWeakMap

    at io.mockk.proxy.jvm.JvmMockKAgentFactory$init$Initializer.handlerMap(JvmMockKAgentFactory.kt:98)
    at io.mockk.proxy.jvm.JvmMockKAgentFactory$init$Initializer.init(JvmMockKAgentFactory.kt:43)
    at io.mockk.proxy.jvm.JvmMockKAgentFactory.init(JvmMockKAgentFactory.kt:102)
    at io.mockk.impl.JvmMockKGateway.<init>(JvmMockKGateway.kt:45)
    at io.mockk.impl.JvmMockKGateway.<clinit>(JvmMockKGateway.kt:163)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
        ...
Caused by: java.lang.ClassNotFoundException: io.mockk.proxy.jvm.dispatcher.JvmMockKWeakMap
    at org.robolectric.internal.bytecode.InstrumentingClassLoader.getByteCode(InstrumentingClassLoader.java:168)
    at org.robolectric.internal.bytecode.InstrumentingClassLoader.findClass(InstrumentingClassLoader.java:123)
    at org.robolectric.internal.bytecode.InstrumentingClassLoader.loadClass(InstrumentingClassLoader.java:95)
    ... 34 more

何が原因か分からなかったのですが、そもそもRobolectrictTestRunnerを使っている理由は、テストにLiveDataが含まれておりvalueの更新処理をMockしてもらうためです。

調べてみると、下記記事のようにLiveDataのユニットテストは core-testing ライブラリを使うのが正攻法のようなので、Robolectrictと置き換えてみました。

medium.com

MockK&core-testingを使ったViewModelのテスト

class UserViewModelTest {

    @Rule
    @JvmField
    var rule = InstantTaskExecutorRule()

    @MockK
    private lateinit var mockRepository: MyRepository
    @MockK
    private lateinit var observer: Observer<List<User>>

    private lateinit var viewModel

    @Before
    fun setup() {
        val immediate = object : Scheduler() {
            override fun createWorker(): Scheduler.Worker {
                return ExecutorScheduler.ExecutorWorker(Executor { it.run() })
            }
        }
        RxJavaPlugins.setInitIoSchedulerHandler { _ -> immediate }
        RxAndroidPlugins.setInitMainThreadSchedulerHandler { _ -> immediate }

        MockKAnnotations.init(this)
    }

    @Test
    fun someTest() {
        val userList = listOf()
        every { mockRepository.findUserList() } returns Observable.just(userList)

        viewModel = UserViewModel(mockRepository)
        viewModel.users.observeForever(observer)
        viewModel.loadUserList()

        verify { mockRepository.findUserList(authToken) }
        assert(viewModel.users.value == userList)
    }
}

変更したのは、 RobolectrictTestRunner を外しRuleに InstantTaskExecutorRule を設定したことです。 これでやっとうまくテストをパス出来ました。

まとめ

  • LiveDataのユニットテストは core-testing を使う
  • 今の所MockK(v1.8.9)とRobolectrict(v3.0.0)の組み合わせはうまくいかない

Android Architecture Components 勉強会#1 に参加してきました

勉強会の参加レポート

GDG東京が主催のAndroid Architecture Components 勉強会の第1回目に参加してきました。 素晴らしい勉強会だったので記事にしたかったのと、学んだことの復習のために書いてます。

ちなみにこの勉強会は全4回の予定だそうで、Lifecycles(今回) -> LiveData -> ViewModel -> Roomと続いていくそうです。

gdg-tokyo.connpass.com

第1回目の講師は「あんざい」さんで、この方は本当に発表が上手いのですが今回もとても聞きやすくて、最後まで1ミリも飽きませんでした。

前半はLifecyclesの説明で、後半はあんざいさんが用意してくださった課題をみんなで取り組みました。課題にはいくつかの問いが用意されていて、動作確認後に参加者が答える形式だったので、楽しく課題に取り組めました。

課題も1個が丁度いいボリュームで、1時間くらいしかなかったにも関わらず、全部で5個くらいやったと思います。

以下のスライドに課題も載ってますが、現地でみんなとやるのが楽しかったので、もしお時間と場所的に可能であれば第2回目に参加することをおすすめします。

Lifecyclesの復習

1. 基本

必要な設定

プロジェクトのbuild.gradleにgoogleリポジトリ(Android Gradle Plugin 3.0ならデフォルトで記述有り)を追加して、モジュールのbuild.gradleにsupport-v4もしくはappcompat-v7を入れる。理由は、v26.1.0からsupport-v4のFragmentActivityやFragmentが、Lifecycleを組み込んでいるため。

メインクラス

  • Lifecycle
    • ライフサイクル状態の取得やObserverの追加・削除を行う

Enum

  • Lifecycle.State
    • ライフサイクル状態を表すenum
  • Lifecycle.Event
    • ライフサイクルのイベントを表すenum

Interface

  • LifecycleOwner
    • Lifecycleを持つクラスを表すためのsingle method interface
  • LifecycleObserver
    • Lifecycleに追加・削除できるObserverを表すinterface
    • @OnLifecycleEvent アノテーションをつけて自分でイベントメソッドを作る
    • Lifecycle.Event.ON_ANYは全てのイベントで呼ばれる
    • 第2引数でEventを受け取れる
  • GenericLifecycleObserver
    • あんざいさんが資料作成中にドキュメントから姿を消した幻のinterface
    • 便利なんだけど今後は使用しないほうがいいかも

その他

  • LifecycleRegistry
    • 複数のObserverを扱えるLifecycleの実装

2. 拡張機能

必要な設定

モジュールのbuild.gradleに以下を追記するだけ

dependencies {
    implementation 'android.arch.lifecycle:extensions:1.0.0'
}

便利クラス

  • LifecycleService
    • LifecycleOwnerが実装されたService
  • ServiceLifecycleDispatcher
    • ServiceにLifecycle機能をもたせるためのヘルパークラス
  • ProcessLifecycleOwner
    • アプリのプロセス全体に対するLifecycleを提供
    • こいつでアプリのForeground/Background判定が簡単に出来る
    • サポートライブラリでないFragmentでも検知可能

3. 注意点

onSaveInstanceState() まわりは要注意。 SupportLibraryのFragmentでは、onSaveInstanceState()が呼ばれると状態は CREATED になるようだ。

詳細は以下を読むほうがいい。

https://developer.android.com/topic/libraries/architecture/lifecycle.html#onStop-and-savedState

4. 基本的なサンプルコード

MainActivity.kt

class MainActivity : AppCompatActivity() {

    private val TAG_NAME = MainActivity::class.java.simpleName

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // create observer
        val observer = object: LifecycleObserver {
            @OnLifecycleEvent(Lifecycle.Event.ON_START)
            fun calledWhenOnStart(source: LifecycleOwner) {
                Log.i(TAG_NAME, "ON_START: " + source.lifecycle.currentState.name)
            }

            @OnLifecycleEvent(Lifecycle.Event.ON_ANY)
            fun calledWhenOnAny(source: LifecycleOwner, event: Lifecycle.Event) {
                Log.i(TAG_NAME, "ON_ANY: " + source.lifecycle.currentState.name + " arg2: " + event.name)
            }

            @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
            fun calledWhenOnDestroy(source: LifecycleOwner) {
                Log.i(TAG_NAME, "ON_DESTROY: " + source.lifecycle.currentState.name)
                // onDestroy()でやるより、ここでremoveObserverするのが楽
                source.lifecycle.removeObserver(this)
            }
        }
        // 何回addしても呼ばれるのは1回
        lifecycle.addObserver(observer)
    }

Android SDKにバンドルされてるIntentアクションのリスト

知ってる人は知ってることなんでしょうけど、自分的には大発見だったので嬉しくて記事にしました。

ことの発端は、OSが投げてくるBroadcastの一覧を探してたところ、BroadcastのAPIガイドを呼んでいたら次のような一文を見つけたためです。

For a complete list of system broadcast actions, see the BROADCAST_ACTIONS.TXT file in the Android SDK.

引用:Broadcasts | Android Developers

慌ててfindコマンド叩いたら本当にありました。全然知らなかった。

$ find . -name "broadcast_actions.txt"
./platforms/android-24/data/broadcast_actions.txt
./platforms/android-25/data/broadcast_actions.txt
./platforms/android-27/data/broadcast_actions.txt
./platforms/android-21/data/broadcast_actions.txt
./platforms/android-26/data/broadcast_actions.txt
./platforms/android-19/data/broadcast_actions.txt

なるほど、APIレベルごとにあるんですね。

Android SDKにバンドルされているリスト

broadcast_actions以外にも一覧として欲しい情報が、いろいろとありました。いづれも sdk/platforms/android-[API level]/data の中にあります。

$ ls
NOTICE.txt            annotations.zip       broadcast_actions.txt features.txt          layoutlib.jar         service_actions.txt   widgets.txt
activity_actions.txt  api-versions.xml      categories.txt        fonts                 res                   tzdata
  • broadcast_actions.txt
    • OSがブロードキャストとして通知するアクションの一覧
  • features.txt
    • AndroidManifestに宣言できる機能の一覧
  • service_actions.txt
    • 各サービスを利用する際に指定するアクションの一覧(?)
  • widgets.txt
    • Viewコンポーネントの一覧(?)
  • activity_actions.txt
    • startActivity()とかで使えるアクションの一覧
  • categories.txt
    • 単なるカテゴリの一覧(?)

※ ?のものは推測です。もしかしたら別の意図のある一覧かもしれません。

まとめ

  • Android SDKには各APIレベル毎にIntentアクションなどのリストがある
  • 各リストはフルネームが列挙されてるので詳細はリファレンスで調べる必要がある