phicdy devlog

Androidアプリ開発やその他技術系の記事をたまに書きます

AndroidとかiOSとかモバイル多め。その他技術的なことも書いていきます。

【Android】Activityを2重で開かないようにする

なにも考えずに実装を行うとActivityが2重で開いてしまうことがある。 例えばボタンを押して次のActivityが開くとき、そのActivityやIntentに設定をしていなければ、素早く連打することで2重に開くことができる。

修正方法

launchModeは指定しないとstandardに設定される。singleTopはほとんどstandardと同じだが、タスクのスタックの一番上に既存のActivityのインスタンスが既に存在する場合挙動が異なる。

"standard" モードと "singleTop" モードの違いは、1 点だけです。 "standard" アクティビティが新たなインテントを受け取るたびに、クラスの新しいインスタンスが作成され、そのインテントに応答します。各インスタンスは 1 つのインテントを処理します。同様に、"singleTop" アクティビティの新しいインスタンスは、新しいインテントを処理するために作成されることもあります。 しかし、ターゲット タスクのスタックの一番上に、既存のアクティビティのインスタンスが既に存在する場合、そのインスタンスが新しいインテントを受け取ります(onNewIntent() の呼び出し)。新しいインスタンスは作成されません。その他の状況では、たとえば、"singleTop" アクティビティの既存のインスタンスがターゲット タスク内に存在するもののスタックの一番上に配置されていない場合、あるいはスタックの一番上に存在するもののターゲット タスク内ではない場合、新しいインスタンスが作成され、スタック上にプッシュされます。

つまり連打で1回Activityを開き2回目が開かれたとしても、そのActivityを使いまわし、onNewIntent()が呼ばれるという流れになる。これでActivityが2重で開くのを防ぐことができる。

ただstartActivityForResult()の場合はこれが効かず毎回新しいActivityが生成される。未調査だが恐らく結果を次のActivityが受け取るという特性上、Activityごとに結果が違うだろうということで新しいインスタンスが作られるのではないかと思う。これはstartActivityForResult()に渡すIntentにFLAG_ACTIVITY_CLEAR_TOPを指定してあげれば解決できる。

タスクがA→B→C→DとあったときにDからBをFLAG_ACTIVITY_CLEAR_TOP付きで呼ぶと、新しいインスタンスを作らずにC, Dを終了してA→Bとする。今回のケースの場合、A→B→C→DでEをstartActivityForResult()で呼び、A→B→C→D→Eとなった直後にEを呼ぶので、新しいインスタンスを作らずにA→B→C→D→Eのままになる。

最後にアンインストールダイアログなどAndroidのシステムのActivityを呼ぶときも同様にFLAG_ACTIVITY_CLEAR_TOPを付ければ同様に解決できる。これらはsingleTopをアプリ側から指定することができない。