サイト移転のお知らせ

しばらくブログお休みしていましたが、こちらに移転しました。よろしければこちらへどうぞ。

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

Androidプログラミング:Theme/Style適用時の注意点

Androidアプリ Service Killerのアップデートv1.2.2を公開しました。バグ修正です。
とあるユーザ様からメールで不具合報告を頂き、修正することができました。ご報告いただいたユーザ様には感謝です。

というわけで恒例の、自らのバグを公開して恥ずかしめを受ける自虐ブログ、ではなく、Androidプログラミングの注意点として紹介したい思います(笑)

今回、v1.2.2で修正した不具合はこれ。まずは下の画像を見てください。

service_killer121_bug.png

見事に2つの画面の内容が重複された難易度の高い画面レイアウト(笑)

実はこれ、v1.2.0の時から多くのユーザの方がご覧になっているのではないかと思います。特に、Xperiaユーザの方々。
アプリ起動して最初に表示される画面でこんな不具合があるアプリを公開していた事自体、非常に申し訳ないです。お恥ずかしい。

では、この現象の詳細について言い訳説明したいと思います。

ご存知の方はご存知かと思いますが、AndroidにはActivityにTheme、Styleを設定する仕組みが備わっています。
この仕組みを使う事で、例えば、タイトルバーを無しにしたり、背景が透明する、ダイアログとして表示する、といったことが簡単に実装できます。
特に何も指定しなければ、デフォルトのテーマが適用されます。

Service Killerでは、ほとんどのActivityはデフォルトのThemeとしているので特にThemeの指定はしていないのですが、問題の重複されて表示されてしまっている"注意事項"を表示するActivityにのみThemeを指定していました。

設定しているThemeは、"android.R.style.Theme_DeviceDefault_DialogWhenLarge"。一般的な画面サイズのスマホでは全画面で表示し、タブレット等の大画面の端末ではダイアログとして表示するというThemeです。

Themeを設定する方法は2通りあり、1つは、Manifestのactivityに対して記述する方法、もう1つはコードとして実装する方法です。

Manifestで設定する場合は以下のようになります。ちなみに、CautionsActivityが"注意事項"を表示する画面です。
<activity
    android:theme="@android:style/Theme.DeviceDefault.DialogWhenLarge"
    android:name=".CautionsActivity"
    android:label="@string/app_name" >
</activity>

コードで書く場合はsetThemeメソッドを使います。これは、 setContentView(View) や inflate(int, ViewGroup))を呼ぶ前にコールする必要があります。
例えば、onCreate()メソッドの中で以下のようにします。
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setTheme(android.R.style.Theme_DeviceDefault_DialogWhenLarge);
    setContentView(R.layout.cautions);


そして、ここで使っているTheme Theme_DeviceDefault_DialogWhenLargeはAPI Level14(ICS)以降から定義されたものです。

では、1つ目のManifestで指定したアプリをAPI Level14より下(GingerBread等)で動作させるとどうなるでしょう?答えは、何もThemeは適用されません。実行させたときにワーニングのログもでません。見た目には問題なく動作しているように見えます。

2つ目のコードで書く方法を適用したアプリをGingerBreadで動作させると、以下のようなワーニングがログ(logcat)に出力されますが、こちらも見た目には問題なく動作しているように見えます。このメソッドは厄介な事に例外も発生しませんし、戻り値はvoidです。
W/ResourceType( 508): Entry identifier 0x136 is larger than entry count 0xab

ちなみに、このログを吐いているコードは/frameworks/base/libs/utils/ResourceTypes.cppにありますが、定義されているidの数より大きな値のidを指定した場合にワーニングを出力する様です。

以上から、GingerBread用のアプリとしてTheme_DeviceDefault_DialogWhenLargeを使っても、後方互換性を保ちながらAndroidがうまい事処理してくれるだろう勝手に思い込んでしまった事がこのバグの原因でした。

注意すべきは、ManifestのThemeの設定はapk化したときに単にid値として扱われること、そして、frameworkとして定義されているThemeは端末によってカスタマイズ(追加)されている可能性があるということ。

Service Killerでは、起動時にサービスの一覧を表示するActivityを表示し、初回起動時のみ、すぐに"注意事項"のActivityを表示するというロジックになっています。
Service Killerv1.2.1までは、ManifestにThemeを設定していましたが、このid値に該当するTheme定義の無いAVD(エミュレータ)等では運良く無視されて、サービス一覧のActivityに被さる形で注意事項のActivityが全画面に表示されていたのだと思います。
ただ、(おそらく)独自のTheme定義を追加しているGingerBreadのXperiaでは、意図しないThemeのid値と一致し、背景が透明になるようなThemeが適用されてしまった注意事項のActivityが重なって表示されるという現象が起きていたのだと推測しています。

というわけで、ICS以降で定義されたThemeをGingerBreadでも動作することを想定したアプリには、以下のようにコードでThemeを適用するのがよいのではないかと思います。
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if(android.os.Build.VERSION.SDK_INT >= 14){
        setTheme(android.R.style.Theme_DeviceDefault_DialogWhenLarge);
    }

    setContentView(R.layout.cautions);

くれぐれも勘違いの無いように書いておきますが、このバグはXperiaが原因ではなく、たまたま、Xperiaで再現するService Killerのバグです、念のため。

このあたりからも独自性を出すためにソニエリ、じゃなくて、ソニーモバイルがいろいろ尽力していることが推測されますね。
今度買う動作確認機としてはXperiaを第一候補にしときます。正直、多くの実機が無いとAndroidアプリ開発はしんどいですね・・・(笑)
関連記事
スポンサーサイト

テーマ : Android
ジャンル : 携帯電話・PHS

コメントの投稿

非公開コメント

サイト内検索
プロフィール

Author:imxs

Androidアプリ開発などを行っているimxsの開発者です。気になることを調べてメモって行きます。ほとんどの人にはどうでもいい内容でも、広い世の中一人くらいは同じ疑問を持った奇妙な人がいることを信じつつ。暖かい目で見守ってやってください。
imxsの開発者ブログは移転しました。よろしければこちらへどうぞ。

カテゴリ
最新記事
リンク
RSSリンクの表示
最新コメント
最新トラックバック
FC2カウンター
アクセスランキング
[ジャンルランキング]
携帯電話・PHS
131位
アクセスランキングを見る>>

[サブジャンルランキング]
Android(Google)
32位
アクセスランキングを見る>>
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。