web-dev-qa-db-ja.com

インテントよりもサービスからUIを更新するより効率的な方法?

私は現在Androidにサービスを持っています。これはサンプルVOIPクライアントなので、SIPメッセージをリッスンし、それを受信するとアクティビティ画面を起動しますUIコンポーネント。

次に、次のSIP=メッセージによって、アクティビティが画面に表示される内容が決まります。たとえば、着信コールの場合は、応答または拒否が表示され、発信コールの場合は、ダイヤル画面が表示されます。

その瞬間、インテントを使用して、アクティビティに表示する必要がある状態を通知します。

次に例を示します。


        Intent i = new Intent();
        i.setAction(SIPEngine.SIP_TRYING_INTENT);
        i.putExtra("com.net.INCOMING", true);
        sendBroadcast(i);

        Intent x = new Intent();
        x.setAction(CallManager.SIP_INCOMING_CALL_INTENT);
        sendBroadcast(x);
        Log.d("INTENT SENT", "INTENT SENT INCOMING CALL AFTER PROCESSINVITE");

そのため、アクティビティはこれらのインテントに対して登録されたブロードキャストレシーバーを持ち、最後に受信したインテントに従ってその状態を切り替えます。

次のサンプルコード:


       SipCallListener = new BroadcastReceiver(){

            @Override
            public void onReceive(Context context, Intent intent) {
                    String action = intent.getAction(); 

                    if(SIPEngine.SIP_RINGING_INTENT.equals(action)){
                        Log.d("cda ", "Got RINGING action SIPENGINE");
                        ringingSetup();
                    }         

                    if(CallManager.SIP_INCOMING_CALL_INTENT.equals(action)){
                        Log.d("cda ", "Got PHONE RINGING action");
                        incomingCallSetup();
                    }  
            }
        };
        IntentFilter filter = new IntentFilter(CallManager.SIP_INCOMING_CALL_INTENT);
        filter.addAction(CallManager.SIP_RINGING_CALL_INTENT);
        registerReceiver(SipCallListener, filter);

これは機能しますが、あまり効率的ではないようです。インテントはブロードキャストシステム全体に及ぶため、さまざまな状態でインテントを起動する必要があるため、複雑さを追加するだけでなく、含める必要があるほど非効率になる可能性があります。

だから私はこれを行うための別のより効率的でよりクリーンな方法があるのだろうかと思っていましたか?

アプリケーション内でのみインテントをブロードキャストし続ける方法はありますか?

コールバックはより良いアイデアでしょうか?もしそうなら、なぜそしてどのようにそれらを実装すべきですか?

41
Donal Rafferty

2015年更新:

この質問/回答はまだ少し活動していますが、5年以上前のものであり、状況はかなり変化しています。 5年前、以下の答えは私がそれをどのように扱ったかでした。その後、しばらく使用していた非常に軽量な依存性注入ソリューションを作成しました(コメントで述べました)。最近では、ダガーとRxAndroidを使用してこの質問に答えます。ダガーは「メディエーター」クラスをサービスと通知が必要なすべてのアクティビティの両方に注入し、サービスはステータス更新をメディエータークラスにプッシュし、メディエータークラスはアクティビティのオブザーバブルを公開してステータス更新を消費します( OPの放送受信機の代わりに)。

元の答え

私は通常、アプリケーションをサブクラス化し、アプリ内通信にこのクラスを通過させます(または、アプリケーションが所有するメディエーターに作業を行わせます...アプリケーションが通信するサービスのエントリポイントであるかどうかに関係なく)。 UIも更新する必要があるバインドされたサービスがあり(あなたのサービスよりもはるかに単純ですが、同じ考えです)、基本的にアプリに新しい状態を通知し、アプリはこの情報を何らかの方法で現在に渡すことができますアクティブな活動。また、現在アクティブなアクティビティへのポインタ(複数ある場合)を維持し、単に現在のアクティビティを更新するか、別のアクティビティを起動するインテントをブロードキャストするか、メッセージを無視するかなどを決定できます。また、Activityをサブクラス化し、新しいアクティビティの基本クラスが、アプリケーションを現在onResumeでアクティブであり、onPauseで一時停止されていることをアプリケーションに通知します(サービスがバックグラウンドで実行され、アクティビティがすべて一時停止されている場合)。

編集:

コメントへの回答として、ここに詳細を示します。

アプリケーションは現在、ほとんどの部分がActivity派生クラスとService派生クラスで構成されています。本質的に、Android.app.Applicationクラスのインスタンスから機能を取得します。これはマニフェストで(デフォルトで)次の行で宣言されています。

<application Android:icon="@drawable/icon" Android:label="@string/app_name">

マニフェストのapplication要素はAndroid:name属性を使用しないため、デフォルトのAndroid.app.Applicationクラスのインスタンスを作成してグローバルアプリケーションコンテキストを表すだけです。

私のアプリでは、Applicationのサブクラス(ApplicationExなど)を作成し、マニフェストを通じて、これがMYグローバルアプリケーションコンテキストとしてインスタンス化するクラスであることをアプリに伝えます。例えば:

<application
    Android:name="com.mycompany.myapp.app.ApplicationEx"
    Android:icon="@drawable/app_icon"
    Android:label="@string/app_name">

通信に使用するアクティビティとサービスのメソッドをApplicationExに追加できるようになりました。グローバルアプリケーションコンテキストのインスタンスは常に1つ存在するため、これをアプリのグローバルにする必要がある場合の出発点にします。

この2番目の部分は、ServiceとActivityからサービスとアクティビティを派生させる代わりに、getApplicationContextの戻り値をキャストするgetAppContextメソッドを使用して、それぞれのサブクラスを作成します(これらは、Contextから派生しているため、これらのクラスの両方にすでに存在します) )私のApplicationExクラスに追加します。

そう........

そうは言っても、CurrentActivityプロパティを、ActivityタイプのApplicationExクラス(または、私がサブクラス化する場合はActivityBase)に追加します。 ActivityBaseのonResumeメソッドでは、ApplicationExに自分自身を渡して、CurrentActivityをそのアクティビティに設定します。これで、ApplicationExのメソッドを公開して、Intentメカニズムに依存する代わりに、現在のアクティビティに情報を直接渡すことができます。

それは私ができる限りはっきりしています

52
Rich

LocalBroadcastManager を使用すると、システム全体ではなく、独自のアプリケーションにのみブロードキャストインテントを送信できます。

インテントのブロードキャストを登録し、プロセス内のローカルオブジェクトに送信するヘルパー。これには、sendBroadcast(Intent)を使用してグローバルブロードキャストを送信するよりも多くの利点があります。

ブロードキャストしているデータがアプリを離れることはないので、プライベートデータの漏洩を心配する必要はありません。

他のアプリケーションがこれらのブロードキャストを自分のアプリに送信することはできないため、セキュリティホールが悪用されることを心配する必要はありません。

システムを通じてグローバルブロードキャストを送信するよりも効率的です。

ただし、効率を上げるためにUIコンポーネントを更新する必要がある場合は、サービスアプローチを使用して、ローカルでハンドラーをバインドおよび通信することをお勧めします。

2
Edmund Chang