7

建築を見る

3 つのアクティビティと 2 つのサービスを備えた Android アプリを開発したいと考えています。

WebClientServiceという名前の最初のサービスは、ハンドラーを使用して 30 秒ごとに REST API を呼び出し、アクティブなアクティビティに結果を通知する必要があります。また、ローカル DB を更新するために、DatabaseServiceという名前の 2 番目のサービスに通知する必要があります。

データベース サービスは、アクティビティのonCreateで 1 回だけ呼び出され (アプリのクラッシュと再起動の場合)、onRestartで 1 回だけ呼び出されます(このようにして、接続の問題が発生した場合にデータを表示できます)。アクティビティは、30 秒ごとに「生きている」アクティビティを通知する WebClientService のおかげで、自分自身を更新し続けます。

質問は次のとおりです。

  • アクティブなアクティビティとバックグラウンドの DatabaseService の両方の更新を通知する最善の方法は何ですか? 私の考えは、WebClientService 内でsendBroadcast()を使用し、すべてのアクティビティと DatabaseService 内でBroadcastReceiverを使用することです。これは正しいアプローチですか?

  • AllMeetingRoomActivity と DatabaseService の間の通信に同じアプローチを使用する必要がありますか、それともBound Serviceを使用する必要がありますか?

ありがとう

UPDATE : DatabaseService はもはやバックグラウンド サービスではなく、WebClientService とアクティビティの間の db レイヤーの単なる共有インスタンスです。

質問は次のとおりです。ローカル データベースに 30 秒の更新を書き込むだけで、アクティビティがローカル データベースから読み取るだけで数秒ごとに自分自身を更新できるようにするのは良い方法ですか? それはパフォーマンスに大きく影響しますか?

環境:

これまでに実装したものに従いますが、SettableFutures を使用しているため、サービスとブロードキャストを効果的に通信させる方法を明確にしたら、サービスとブロードキャストを使用して再実装する必要があります。

public class MainActivity extends AppCompatActivity {               

    private TextView meetingsTextView;
    private EditText mEdit, editSubject;

    private final ConnectorInitializer clientInitializer = new ConnectorInitializer();
    private AppConnector genericClient; // can use OutlookClient or a test client to talk with a mock server

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        // initializes client based on the settings in "config.json"
        genericClient = clientInitializer.create(this);

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        meetingsTextView = (TextView) findViewById(R.id.NowMeeting);
        mEdit   = (EditText)findViewById(R.id.editText);
        editSubject   = (EditText)findViewById(R.id.editSubject);

        Futures.addCallback(genericClient.logon(this, scopes), new FutureCallback<Boolean>() {
            @Override
            public void onSuccess(Boolean result) {
                Log.d("APP", "-- Logged in. --");

                databaseConnector.synchronouslyGetBackupFromLocalDatabase() // FUTURE
                // callback here
                // onSuccess, onFailure

            }

            @Override
            public void onFailure(@NonNull Throwable t) {
                Log.e("\n ~~~~>> logon \n", t.getMessage());
                meetingsTextView.setText(R.string.Login_Failed);
            }
        });

    }

    /** At the moment the UI is not updated automatically every 30 seconds
    *   but manually using a refresh button
    */
    public void getBookings(@SuppressWarnings("UnusedParameters") View view){

        Log.d("APP", "Retrieve button clicked: "+(DateTime.now())+". Calling async getCalendar.");
        meetingsTextView.setText(R.string.retrieving_events);

        try{
            Futures.addCallback( genericClient.getCalendarEvents(), new FutureCallback<String>(){
                @Override
                public void onSuccess(final String resultCalendars) {

                    Log.d("APP", "Success. Result: "+resultCalendars);

                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {

                            Log.d("APP", "Calendars SUCCESSFULLY retrieved.");
                            String meetingsRetrieved = getString(R.string.calendar)+resultCalendars;
                            meetingsTextView.setText(meetingsRetrieved);

                            Toast.makeText(getApplicationContext(), "Success!", Toast.LENGTH_LONG).show();
                        }
                    });

                    databaseConnector.asyncUpdateLocalDbWithResults(); // FUTURE
                    // callback here
                    // onSuccess, onFailure

               }

                @Override
                public void onFailure(@NonNull Throwable t) {
                    Log.e( "APP", "Calendar error. Cause: "+t.getLocalizedMessage() );
                    String retrieveError = "Retrieve error. \n\n\n"+t.getLocalizedMessage();
                    meetingsTextView.setText(retrieveError);
                    Toast.makeText(getApplicationContext(), "Fail!", Toast.LENGTH_LONG).show();
                }
            });

        }catch(Exception ex){    
            Log.e("APP","Something went wrong in your code. Cause:"+ex);
        }
    }
4

4 に答える 4

10

これまでで最高のオプション:

を使用しLocalBroadcastManagerます。詳細はこちらを参照してください

MyService.java:

private LocalBroadcastManager localBroadcastManager;
private final String SERVICE_RESULT = "com.service.result";
private final String SERVICE_MESSAGE = "com.service.message";

@Override
public void onCreate() {
    super.onCreate();

   // Other stuff

   localBroadcastManager = LocalBroadcastManager.getInstance(this);
}

以下のメソッドをサービスに追加します。サービスからアクティビティにデータを更新するたびに、 を渡してメソッドを呼び出しますArguments

private void sendResult(String message) {
    Intent intent = new Intent(SERVICE_RESULT);
    if(message != null)
        intent.putExtra(SERVICE_MESSAGE, message);
    localBroadcastManager.sendBroadcast(intent);
}

HomeActivity.java:

private BroadcastReceiver broadcastReceiver;

public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.setContentView(R.layout.activity_home);
    broadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String s = intent.getStringExtra(MyService.SERVICE_MESSAGE);
            // do something here.
        }
    };
}

@Override
protected void onStart() {
    super.onStart();
    LocalBroadcastManager.getInstance(this).registerReceiver((broadcastReceiver), 
        new IntentFilter(MyService.SERVICE_RESULT));
}

@Override
protected void onStop() {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(broadcastReceiver);
    super.onStop();
}

これがあなたを助けることを願っています。

于 2016-04-21T04:33:25.943 に答える
9

あなたのアプローチは大丈夫だと思いますBroadCastReceiver。ただし、BroadCastReceiverグローバルな目的 (2 つのアプリケーション間の通信など) には使用する必要があります。BroadCastReceiverアプリのみに使用する場合は、代わりにLocalBroadcastManagerを使用することをお勧めします。LocalBroadcastManagerアプリだけがキャッチできる場合は、使用する方が高速でセキュリティが強化されます。

activityと の間で通信する別の方法は、 EventBusserviceを使用することです。使用するよりもはるかに簡単です(特にそれらの間でデータを渡す場合)。BroadCastReceiver

更新:更新に関する質問について:

  1. ローカル データベースに 30 秒の更新を書き込むだけで、アクティビティがローカル データベースから読み取るだけで数秒ごとに更新できるようにするのは良い方法ですか? --> もちろんいいえ。必要に応じてアクティビティを更新できるようにする必要があります。ローカル データベースを更新するときは、変更があるかどうかを確認する必要があります。変更がある場合は、LocalBroadcastmanager更新するアクティビティを通知するために使用します。
  2. それはパフォーマンスに大きく影響しますか?--> はい、そうです。データベース接続の実行には時間がかかり、場合によっては UI がブロックされます。その場合、ExecutorServicefor each execute(挿入、更新...) でスレッドを使用する必要があります。考慮すべきもう1つのことは、頻繁に更新すると、携帯電話のバッテリーが非常に速く消耗することです.
于 2016-04-10T16:29:07.543 に答える
1

サービスをアクティビティにバインドして、UI を更新できます。または、Otto や EventBus などのライブラリを使用してパブリッシャー/サブスクライバーの依存関係を作成し、サービスが情報の更新を公開するたびにアクティビティを通知できます。

于 2016-04-26T20:42:12.460 に答える
1

この通信にはイベントバスを使用します。EventBus を使用すると、コンポーネントが相互に明示的に登録する必要なく (したがって、相互を認識しなくても)、コンポーネント間のパブリッシュ/サブスクライブ スタイルの通信が可能になります。これは、明示的な登録を使用する従来の Java インプロセス イベント配布を置き換えることのみを目的として設計されています。

それらはたくさんあります:

http://square.github.io/otto/

https://github.com/greenrobot/EventBus

これは Otto の使用例です。

Bus bus = new Bus();

bus.post(new AnswerAvailableEvent(42));

@Subscribe public void answerAvailable(AnswerAvailableEvent event) {
    // TODO: React to the event somehow!
}

bus.register(this); // In order to receive events, a class instance needs to register with the bus.

任意のスレッド (メインまたはバックグラウンド) から投稿するには、サービスの場合、メイン スレッドでイベントを受信します。

public class MainThreadBus extends Bus {
  private final Handler mHandler = new Handler(Looper.getMainLooper());

  @Override
  public void post(final Object event) {
    if (Looper.myLooper() == Looper.getMainLooper()) {
      super.post(event);
    } else {
      mHandler.post(new Runnable() {
        @Override
        public void run() {
          MainThreadBus.super.post(event);
        }
      });
    }
  }
于 2016-04-28T05:54:45.710 に答える