Android服务和活动之间的通信

时间:2016-04-10 03:42:47

标签: android android-activity android-service localbroadcastmanager

see architecture

我想开发一个包含三项活动和两项服务的Android应用程序。

第一个名为 WebClientService 的服务使用处理程序每​​30秒调用一次REST API,并且必须通过结果通知活动的Activity。 它还必须通知名为 DatabaseService 的第二个服务,以便更新本地数据库。

数据库服务只会在活动的 onCreate 上调用(如果是应用程序崩溃并重新启动),只需在 onRestart 调用一次(这样我们就可以获得数据)显示以防出现连接问题)。由于WebClientService每30秒通知一次“活动”活动,活动将自动更新。

问题是:

  • 通知更新活动活动和后台DatabaseService的最佳方法是什么? 我的想法是在WebClientService中使用 sendBroadcast(),在每个活动和DatabaseService中使用 BroadcastReceiver ,这是正确的方法吗?

    < / LI>
  • 我应该使用相同的方法进行AllMeetingRoomActivity和DatabaseService之间的通信,还是应该使用Bound Service

由于

更新: 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 个答案:

答案 0 :(得分:8)

有史以来最好的选择:

使用LocalBroadcastManager。更多参考here

<强> 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);
}

在服务中添加以下方法,只要您想要将服务中的数据更新为Activity,通过传递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();
}

希望这会对你有所帮助。

答案 1 :(得分:7)

我认为您的方法适用于BroadCastReceiver。但是,BroadCastReceiver应该用于全局目的(比如两个应用程序之间的通信)。如果您打算仅为自己的应用使用BroadCastReceiver,我更愿意使用LocalBroadcastManager。只有您的应用才能捕获LocalBroadcastManager,使用activity会更快,更安全。

serviceBroadCastReceiver之间的另一种沟通方式是使用EventBus。它比使用LocalBroadcastmanager容易得多(特别是在它们之间传递数据)。

更新:关于您的更新问题:

  1. 这是一个很好的方法,只需将我的30秒更新写入本地数据库,并允许活动每隔几秒钟更新一次,只需从本地数据库读取? - &GT;当然不。您应该在需要时让您的活动自行更新。更新本地数据库时,您应该知道是否有任何更改。如果有任何更改,请使用ExecutorService通知您的活动进行更新。
  2. 这会对性能产生太大影响吗? - &GT;是的,那样做。数据库连接需要一段时间才能执行,在某些情况下它会阻止你的用户界面。在这种情况下,您应该为每个execute使用replaceAll = map . replace 的线程(插入,更新...)。还需要考虑的另一件事是更新频繁会非常非常快地耗尽手机电池。

答案 2 :(得分:1)

您可以将服务绑定到活动并更新UI。 或者,您可以使用Otto或EventBus等库创建发布者/订阅者依赖关系,并在每次服务发布信息更新时通知您的活动。

答案 3 :(得分: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);
        }
      });
    }
  }