0

ここに新しい Android プログラマーがいます。ソケット管理と非同期 I/O を実行するサービスがあり、それとアプリ内のアクティビティとの間の通信パスを確立する必要があります。

現在のアプローチは、サービスとアクティビティの両方に BroadcastReceivers を装備し、それらを使用してアクティビティからサービスに「コマンド」インテントを送信し、サービスからアクティビティに「アラート」インテントを送信することです。

私のサービスには、ソケットの read() が発生する実行可能ファイルがあります。データが受信されると、ランナブルは「着信データ」インテントをサービスに送信し、サービスはアクティビティに警告します。

    @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            super.onStartCommand(intent, flags, startId);
            if (m_IsRunning == false) {
                m_IsRunning = true;
                (new Thread(new Runnable() {
                    byte[] inputBuffer = new byte[512];
                    public void run() {
                        while (m_IsRunning) {
                            if (m_IsConnected) {
                                try {
                                    m_Nis = m_Socket.getInputStream();
                                    m_Nis.read(inputBuffer, 0, 512);
                                    Intent broadcast = new Intent();
                                    Bundle bun = new Bundle();
                                    bun.putString("ServiceCmd", "ALERT_INCOMING_DATA");
                                    bun.putByteArray("MsgBuffer", inputBuffer);
                                    broadcast.putExtras(bun);
                                    broadcast.setAction(BROADCAST_TO_SERVICE);
                                    sendBroadcast(broadcast);
                                } catch (IOException e) {
                                    // Send fault to activity
                                }
                            }
                        }
                    }
                })).start();
            }
            return START_STICKY;
        }

BroadcastReceiver を使用した私のアプローチは次のようになります。

        private BroadcastReceiver serviceReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                Bundle bun = intent.getExtras();
                String cmdString = bun.getString("ServiceCmd");

                if (cmdString.equals("CMD_SETHOSTINFO")) {
                    // The activity has requested us to set the host info
                    String hostAddr = bun.getString("HostAddressString");
                    int hostPort = bun.getInt("HostPortNumber");
                    processSetHostInfoCommand(hostAddr, hostPort);
                }
                else if (cmdString.equals("CMD_CONNECT")) {
                    // The activity has requested us to connect
                    if ((m_IsRunning) && (m_IsConnected == false)) {
                        // Attempt to connect
                        processConnectCommand();
                    }
                }
                else if (cmdString.equals("CMD_DISCONNECT")) {
                    // The activity has requested us to disconnect
                    if ((m_IsRunning) && (m_IsConnected == true)) {
                        // Attempt to disconnect
                        processDisconnectCommand();
                    }
                }
                else if (cmdString.equals("CMD_SENDDATA")) {
                    // The activity has requested us to send data
                    if ((m_IsRunning) && (m_IsConnected == true)) {
                        // Attempt to send data
                        byte[] msgBuffer = bun.getByteArray("MsgBuffer");
                        processSendDataCommand(msgBuffer);
                    }
                }
                else if (cmdString.equals("ALERT_INCOMING_DATA")) {
                    // Our TCP receiver thread has received data
                    if (m_IsRunning) {
                        byte[] msgBuffer = bun.getByteArray("MsgBuffer");
                        processIncomingDataAlert(msgBuffer);
                    }
                }
            }
        };

(これらのprocessWhatever()メソッドは一般に、ソケット管理とデータ転送を行います。)

私が言ったように、それはうまくいくように見えますが、これはメッセージとハンドラーを使用することがより適切ではない場合ではないかと思います.

したがって、具体的な質問は次のとおりです。

  1. BroadcastReceiver/IntentsまたはHandler/Messagesをいつ使用するかを決定する際の「Androidのタオ」は何ですか?

  2. どのアプローチを使用するかを決定する際に、クロススレッドに関する考慮事項はありますか?

(そして、それはトピックから外れていますが、最後の質問です):

  1. Service は、私が行おうとしている種類のソケットベースの I/O を実行するのに適していますか?
4

2 に答える 2

4

ブロードキャスト インテント、インテント、ハンドラのタオ

ブロードキャスト インテントは、1 対多の pub/sub シナリオ用です。1 つのコンポーネントが何かが起こったことを世界に知らせたいが、誰か/リスナーが何人いるか、またはそれらが現在実行されているかどうかは気にしません。

通常のインテントは、1 つのコンポーネントがその代わりに特定の処理を実行する必要があるが、それを実行できる特定のコンポーネントがあるかどうか、またはそのようなコンポーネントが現在実行されているかどうかを気にしない/認識しない、1 対 1 のシナリオ用です。

一方、ハンドラーは、両方の当事者がよく知られており、現在実行されている 1 対 1 の同期/非同期シナリオ用です。

上記がシナリオにどのように関連するか

最も単純な実装では、インテントやハンドラーは必要ありません。次のように、バックグラウンドの Runnable からサービスのメソッドを直接呼び出すことができます。

MyService.this.processIncomingDataAlert(inputBuffer);

これにより、バックグラウンド スレッドでメソッドが実行され、データの処理中にソケット リスナーがブロックされることに注意してください。これにより、あなたが尋ねたスレッドに関する考慮事項にたどり着きます。

ソケット リスナーのブロックを解除したり、UI スレッドでデータを処理したりしたい場合は、次のように で Handler を作成し、onStartCommand()それを runnable から使用して、別の runnable を UI スレッドに戻すことができます。

myServiceHandler.post(new Runnable() {
    public void run() {
        MyService.this.processIncomingDataAlert(inputBuffer);
    }
};

onIncomingDataこれにより、UI スレッドが Runnable 呼び出しを処理するときと、 を更新する可能性のあるバックグラウンド スレッド リスナーとの間で競合状態が発生することに注意してください。そのinputBuffferため、コピーを作成することをお勧めします。

もちろん、サービスとアクティビティの間で共有される UI スレッドで処理が行われるという問題もあります。したがって、データ処理が遅いと、UI の応答性に影響します。

バックグラウンド ソケット リスナーと UI スレッドの両方が応答することを確認したい場合は、(まだ) 別のスレッドでデータを処理する必要があります。Runnable ごとに新しいスレッドを開始することもできますが、その結果、大量のスレッドがすぐに作成され、システム リソースが浪費されます。個別のスレッドを作成すると、データの複数のチャンクを処理する間に競合状態が発生し、アプリのロジックが複雑になりすぎる可能性があることは言うまでもありません。

幸いなことに、Android はAsyncTaskそのようなシナリオに対応しています。

AsyncTask にはバックグラウンド スレッドのプールがあり、そこでスケジュールされた Runnables を実行します。GingerBread 以下、または ICS 以上のバージョンで実行している場合、AsyncTask もそれらをシリアル化し、一度に 1 つのタスクのみを実行します。

スレッド、ハンドラー、および AsyncTask の優れた紹介については、この記事をお読みください。この記事では、AsyncTask をサブクラス化し、 を実装する例を示していますdoOnBackgroundが、これは少しやり過ぎです。静的execute(Runnable)メソッドは問題なく機能します。

サービスはシナリオに適していますか

この質問は、残りの質問とは多少直交しています。与えられたソケットでリッスンするには、バックグラウンド スレッドが必要です。しかし、サービスが必要かどうかは明確ではありません。

サービスは、アプリケーションがバックグラウンド処理を実行する必要があり、UI を介してユーザーを操作したり、ユーザーが別のアプリケーションに切り替えた後に処理を続行したりする必要がないシナリオ用です。

したがって、シナリオで、アクティビティが画面に表示され、ユーザーがアクティブに関与している間のみソケットでリッスンする必要がある場合は、サービスは必要ありません。上記で説明したように、アクティビティからバックグラウンド スレッドを開始し、ハンドラーを使用して新しいデータをバックグラウンド スレッドまたは AsyncTask にポストバックすることができます。

ただし、ユーザーがアクティビティを閉じた後もソケットでリッスンし続ける必要がある場合 (それは別のトピックです:-))、サービスが必要です。

ここでの主な問題は、Android のプロセス ライフサイクルです。デバイス リソースを適切に管理するために、OS はアイドル状態と見なされるプロセスを強制終了します。アクティビティや実行中のサービスがない場合、プロセスはアイドル状態と見なされます。バックグラウンド スレッドを開始するだけでは、プロセスがまだビジーであることを OS に知らせるには不十分です。そのため、サービスがない場合、アクティビティが閉じられると、Android の観点からは、プロセスは何もしていないため、プロセスが強制終了される可能性があります。

お役に立てれば。

于 2012-05-09T04:06:07.027 に答える
1

サービスを実際に使用する必要はありません。すべてが同じプロセスで実行されている場合は、ネットワーク コードをシングルトンにして、アクティビティがメソッドを直接呼び出すようにします。通知の場合、単にアクティビティに特定のインターフェイス (onData(String data)など) を実装させ、それらをネットワーク クラスに登録させることができます。アクティビティがなくなったら、登録を解除するように注意してください ( onStop())。一方、UI が表示されていないときにネットワーク コードを実行する必要がある場合 (たとえば、別のアプリがフォアグラウンドにある場合)、サービスを使用する必要があります。

メッセージとインテントは実際には異なります。handlers.messages を使用して特定のスレッドと通信しますが、インテントは実際には IPC であり、別のアプリ/プロセスに配信される可能性があります。ブロードキャスト レシーバーを使用する利点は、現在インテントが実行されていない場合に、Android が着信インテントを処理するプロセスを作成することです。

それが「タオ」かどうかはわかりませんが、一般的には次のとおりです。

  • UI なしで何かを実行する必要がある場合は、サービス (スケジュールされたネットワーク IO など) を使用します。
  • UI が存在する場合は、別のスレッドで実行するだけです (AscynTaskこれを行う便利な方法を提供します)。
  • 独自のアプリと (単一のプロセスで) 通信している場合は、ハンドラー/メッセージを使用します
  • クロスアプリ (プロセス) 通信が必要な場合は、インテントを使用します
  • 非同期イベントを処理する必要があり、それを処理するアプリが実行されていない可能性がある場合は、ブロードキャスト レシーバーを使用します。次に、IntentService時間がかかる場合は、実際の作業を行うためにサービスを開始する (ワーカー スレッドで処理を行う) ことをお勧めします。さらに、作業中 (ネットワーク IO など) にデバイスが再びスリープ状態にならないように、ウェイクロックを取得する必要がある場合があります。
于 2012-05-09T03:17:25.113 に答える