2013/03/28

[android]getSystemService()はいつやるとよい?

NFCを使うときだけじゃないが、getSystemService()を使うときは多い。
システムサービス、というくらいだから、onCreate()で取得しておいて、あとは使い回せばいいだろうと何となく思っているのだが、本当だろうか?

心配になってきたので、調べよう。
うぅ、身近にAndroidの先生がほしい・・・。


まず、Context#getSystemService()を見る。
・・・abstructだから、実装は派生クラスが持っているのか。
そういえば、未だにabstructとinterfaceの違いがわかってないな。
C++だと、

class Context {
public:
  Object getSystemService(String name) = 0;

となっているイメージだけど、あってるのかな。まあ、後で調べよう。


次は、Activity#getSystemService()を見る。
普段呼び出しているのが、ここ経由だからだ。

  1. Contextがなければ、エラー
  2. WindowServiceを要求しているなら、mWindowManagerを返す。
    mWindowManagerは、attach()ってところで設定されている。誰が呼ぶかは知らない・・・。
  3. SearchServiceを要求しているなら、mSearchManagerを返す。
    もしmSearchManagerがnullだったら、この時点でnewして返す。
  4. それ以外なら、super.getSystemService()を呼び出す。

Contextは後から変化することはなさそうだから、使い回してもよさそうだ(根拠無し)。
WindowServiceも、最初に作るだけだろうから、使い回してもよいだろう。
SearchServiceも、その場で作るくらいだし、newするときの引数もthisだから使い回してもよいだろう。

また、特に重たそうな処理もないので、毎回取得しても悪いことはないかな、という気分になった。


では、Activityの上。ContextThemeWrapper#getSystemService()を見る。

  1. LayoutInflaterServiceを要求しているなら、mInflaterを返す。
    もしmInflaterがnullだったら、この時点でLayoutInflaterを作って(cloneして)返す。
  2. それ以外なら、mBase.getSystemService()を呼び出す。

LayoutInflaterは、レイアウトXMLからViewを作るときに使うものらしい。
"inflate"は「膨張させる」という意味らしい。インフレってやつか。

まあ、ここもcloneInContext()するときにthisを使うくらいだから、使い回してもよさそうだ。
取得にも時間はかからないだろう。

Activityと違うのは、次に呼び出すgetSystemService()が、superではなくてmBaseになっていることだ。
mBaseはContextのインスタンスで、コンストラクタで指定するようになっている。
足跡が途切れてしまった・・・。


仕方ないので、デバッガで探すことにした。
さっき作ったアプリでは、app.ContextImpl#getSystemService()が呼ばれるようだった。

  1. SYSTEM_SERVICE_MAPから、サービス名に合うServiceFetcherを取得
  2. ServiceFetcherがnullなら、nullを返す。
    そうでなければ、fetcher.getService()を返す。

SYSTEM_SERVICE_MAPは、HashMap<String, ServiceFetcher>となっている。
ServiceFetcherはこのContextImpl内にあるstatic classである。

 

・・・static class ?
またよくわからないものが出てきた。。。
調べたが、こちらがわかりよかった。
 Java static クラス - グロブ

わざわざそげんことせんでも、staticメソッドかなんか作りゃいいやん、と思ったが、ServiceFetcherを継承したStaticServiceFetcherクラスもあるので、classにしたかったのだろう。
この辺りの実装感覚が、全然ないんだな。。。

 

ともかく、ServiceFetcherやStaticServiceFetcherのインスタンスは、ContextImplが読み込まれたときにnewするようだ。
static{}の中に書いてあるから、そうなのだろう。

ただ、WallPaperServiceだけは別物らしく、static{}外に書いてある。
これは、getWallpaperManager()で再利用できるようにするためと、HashMapの検索対象外にするため、らしい。
うーむ・・・意味がわからないな。

fetcher.getService()では、既にサービスのインスタンスがあるならそれを返し、なければ生成して返すようになっているみたいだ。


見て回った感触からすると、システムサービスはonCreate()で保持しててもいいかな、という気がする。
毎回取得してもいいのだろうけど、HashMapを検索するコストが重たいのかどうかがよくわからない。
けど、インスタンスは既にあるのだから、保持することでそんなにメモリを食ってしまうということもなさそうだ。
まあ、個人の感想に過ぎないがね。

ただ、WallpaperServiceだけは別物のような気がしている。
WallpaperService自身は小さくて、WallpaperService.Engineを生成するためだけのものらしい。
ライブ壁紙を作る方法を読むと「WallpaperServiceとそのEngineを継承したクラスを作る」とあるので、動的に変化するように思うのだ。
onResume()で取得するようにした方が無難なのかな。

そういう解釈でいいのかね?

1 件のコメント:

  1. onCreateでいいと思います!
    WallpaperServiceは壁紙作る時に使うと思うんですが、壁紙とかappwidgetは作り方がまた特殊なので、その時考えるのが良いかと!

    返信削除

コメントありがとうございます。
スパムかもしれない、と私が思ったら、
申し訳ないですが勝手に削除することもあります。