

こんにちは、Android開発初心者です。  
現在、Androidアプリでサービスを動かし処理を実行し続ける部分を実装しています。  
そこでコールバックを受け取るクラス(仮にクラスAとします)へabstract
interfaceをimplementsさせ、クラスAへの参照保持しコールバックしていたのですが、アプリキル時にそのクラスAへの参照が消えてしまいコールバックできない問題に当たりました。  
解決法として、リスナークラスへBroadcastReceiverをextendsし、クラスAへの参照を持たずともコールバックする方法とクラスAへの参照をWeakReferenceで保持する方法を考えています。  
そこで、この2つの方法の問題点があれば教えていただきたいのと、他にもっとうまい方法があれば実装方法も含めご教授いただければと思います。  
よろしくお願いします。

一応考えている方法のコードを記載します。

方法1

[code]

    public class A extends B {
        @Override
        public void onB(String name) {
            // 以下、Push通知を行う
        }
    }
    public adstract class B extends BroadcastReceiver {
    
        @Override
        public void onReceiver(Context context, Intent intent) {
    
            Bundle extra = intetn.getExtras();
            String name = extras.getString("key");
            onC(name);
        }
    
        public abstract void onB(String name);
    }
    
[/code]

方法2

[code]

    public class A implements B {
    
        @Override
        public void onB(String name) {
            // 以下、Push通知を行う
        }
    
    }
    
    public adstract interface B {
    
        public abstract void onB(String name);
    
    }
    
    public class C {
    
        private A mClassA;
    
        public static void b(String name) {
    
            mClassA.onB(name);
    
        }
    
    }
    
[/code]


CC BY-SA 3.0
2015-05-31T08:02:54.783
10811
2015-07-31T03:11:09.810
2015-05-31T15:03:56.923
9936
9936
question
android java
Androidでアプリキル時にコールバックする方法


Androidのライフサイクルに関係が深いActivity、Serviceとのからみが分からないので、onBにおけるpush通知でintentによりActivityを呼び出し、ダイアログにてユーザ通知するのかな？という予想で書きます。また、AndroidManifest.xmlは適切に設定されているものと考えます。

まず、方法１と方法２ですが、確実なのは方法１です。方法２はライフサイクル的にどこにあるのか判断しかねますが、Service等のキルと共にキルされるように思います。そして、これはAndroidのバージョン依存ですが、Android4.4においてServiceがキルされた場合の自動復旧フラグであるSTART_STICKYが無効になるバグがあります。ユーザ操作でキルされた場合も復旧できないことがありますし、現在よく使われているバージョンなので対処する必要があり、通常は方法１にて対処することになると思います。

次に、方法１についてですが、このままではコードにいくつか問題がありますので、修正すると以下のようになります。

[code]

     @Override
     public void onReceiver(Context context, Intent intent) {
     //WakeLockの取得
     WakeLock wakelock = ((PowerManager) context
     .getSystemService(Context.POWER_SERVICE)).newWakeLock(
     PowerManager.PARTIAL_WAKE_LOCK
     | PowerManager.ON_AFTER_RELEASE, "Sleep_Lock");
     // 強制的にアプリを起こしたままにする（処理に時間がかかる場合は時間指定ではない方で処理する）
     wakelock.acquire(150);
     // intentのフィルタリング(equalsの中は用途によって使い分ける)
     String action = intent.getAction();
     if (action.equals(Intent.X)) {
     Bundle extra = intetn.getExtras();
     String name = extras.getString("key");
     onC(name);
     }
     }
    
[/code]

追加部分を説明しますと、このBroadcastReceiverでは確実にユーザ通知するためにWakeLockを取得し、スリープ状態を回避する必要があります。こうしないと、BroadcastReceiverの処理中でもユーザ操作や端末放置による画面OFFで処理が中断されてしまいます。また、intentを適切にフィルタリングしてやらないと、「受信した全てのintent」に反応して毎回処理が走ります。

BroadcastReceiverは、実装によってはセキュリティが問題になってくるため、必要に応じて呼び出し用intent、及びAndroidManifest.xmlに独自パーミッションを設定してください。

その他、もっと良い方法についてですが、アプリのキルの種類と実装によっては、Activityのキルそのものを防ぐということも可能です。この場合、防げるのはホームボタン長押しで出るアプリ履歴に対する、ユーザのスワイプ操作によるキルです。これを防ぐには、例えば特定のActivity呼び出しの際のintentに.setFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS|
Intent.FLAG_ACTIVITY_NEW_TASK)を設定してやります。こうすると、（実装によりますが）アプリの履歴に出なくなるため、スワイプでキルするのが不可能になります。ただし、この方法はメモリ不足で落ちた場合、それまでになります。また、「設定」画面から「強制停止」された場合もどうしようもありません。しかしながら、この方法と方法１を組み合わせるのは有用な場合がありますので、場合によっては検討されてはいかがでしょうか。


CC BY-SA 3.0
2015-06-01T01:10:27.440
10829
2015-06-01T01:57:07.460
2015-06-01T01:57:07.460
8795
8795
10811
answer