In Android, Activity is mainly responsible for the display of the foreground page, and Service is mainly responsible for tasks that need to run for a long time. Therefore, in our actual development, we often encounter the communication between Activity and Service. We generally start the background Service in Activity. Started by Intent, we can pass data to Service in Intent, and when our Service performs some operations and wants to update the UI thread, what should we do? Next, I will introduce two ways to realize the communication problem between Service and Activity
- through the Binder object
When the Activity calls bindService(Intent service, ServiceConnection conn, int flags), we can get an object instance of a Service, and then we can access the methods in the Service. Let's understand it through an example, a simulated download A small example to show you how to communicate through Binder
First, we create a new project Communication, and then create a new Service class
package com.example.communication; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; public class MsgService extends Service { /** * the maximum value of the progress bar */ public static final int MAX_PROGRESS = 100; /** * The progress value of the progress bar */ private int progress = 0; /** * Add get() method for Activity to call * @return download progress */ public int getProgress() { return progress; } /** * Simulate download tasks, update every second */ public void startDownLoad(){ new Thread(new Runnable() { @Override public void run() { while(progress < MAX_PROGRESS){ progress += 5; try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace (); } } } }).start(); } /** * Return a Binder object */ @Override public IBinder onBind(Intent intent) { return new MsgBinder(); } public class MsgBinder extends Binder{ /** * Get an instance of the current Service * @return */ public MsgService getService(){ return MsgService.this; } } }
The above code is relatively simple and the comments are relatively detailed. The application of the most basic Service, I believe you can understand, we call the startDownLoad() method to simulate the download task, and then update the progress every second, but this is done in the background , we can't see it, so sometimes we need him to display the download progress in the foreground, so we will use Activity next.
Intent intent = new Intent("com.example.communication.MSG_ACTION"); bindService(intent, conn, Context.BIND_AUTO_CREATE);
Through the above code, we bind a Service to the Activity, which requires a ServiceConnection object, which is an interface. We use an anonymous inner class here.
ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { //return a MsgService object msgService = ((MsgService.MsgBinder)service).getService(); } };
In the onServiceConnected(ComponentName name, IBinder service) callback method, a Binder object in MsgService is returned. We can get a MsgService object through the getService() method, and then we can call some methods in MsgService. The code of the Activity is as follows
package com.example.communication; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; public class MainActivity extends Activity { private MsgService msgService; private int progress = 0; private ProgressBar mProgressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView(R.layout.activity_main); //Bind Service Intent intent = new Intent("com.example.communication.MSG_ACTION"); bindService(intent, conn, Context.BIND_AUTO_CREATE); mProgressBar = (ProgressBar) findViewById(R.id.progressBar1); Button mButton = (Button) findViewById(R.id.button1); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //start download msgService.startDownLoad (); // monitor progress listenProgress(); } }); } /** * Monitor the progress, call the getProgress() method of MsgService every second to obtain the progress and update the UI */ public void listenProgress(){ new Thread(new Runnable() { @Override public void run() { while(progress < MsgService.MAX_PROGRESS){ progress = msgService.getProgress(); mProgressBar.setProgress(progress); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace (); } } } }).start(); } ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { //return a MsgService object msgService = ((MsgService.MsgBinder)service).getService(); } }; @Override protected void onDestroy() { unbindService(conn); super.onDestroy (); } }
In fact, I still have some doubts about the above code. The method of monitoring progress changes is to update the UI directly in the thread. Doesn't it mean that the UI operation cannot be updated in other threads? Maybe the ProgressBar is special, and I didn't study it. The source code, friends who know can tell me, thank you!
The above code completes the operation of updating the UI in the Service, but have you found it? We have to actively call getProgress() every time to get the progress value, and then call the getProgress() method every second, will you? Feel passive? Is there a way to actively notify the Activity when the progress in the Service changes, the answer is yes, we can use the callback interface to realize the active notification of the Service, if you don't understand the callback method, you can look at http://blog.csdn.net/xiaanming /article/details/8703708
Create a new callback interface
public interface OnProgressListener { void onProgress(int progress); }
There are some small changes in the code of MsgService. For the convenience of everyone, I still paste all the codes.
package com.example.communication; import android.app.Service; import android.content.Intent; import android.os.Binder; import android.os.IBinder; public class MsgService extends Service { /** * the maximum value of the progress bar */ public static final int MAX_PROGRESS = 100; /** * The progress value of the progress bar */ private int progress = 0; /** * Callback interface for update progress */ private OnProgressListener onProgressListener; /** * Register the method of the callback interface for external calls * @param onProgressListener */ public void setOnProgressListener(OnProgressListener onProgressListener) { this.onProgressListener = onProgressListener; } /** * Add get() method for Activity to call * @return download progress */ public int getProgress() { return progress; } /** * Simulate download tasks, update every second */ public void startDownLoad(){ new Thread(new Runnable() { @Override public void run() { while(progress < MAX_PROGRESS){ progress += 5; //Notify the caller of progress change if(onProgressListener != null){ onProgressListener.onProgress(progress); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace (); } } } }).start(); } /** * Return a Binder object */ @Override public IBinder onBind(Intent intent) { return new MsgBinder(); } public class MsgBinder extends Binder{ /** * Get an instance of the current Service * @return */ public MsgService getService(){ return MsgService.this; } } }
The code in the Activity is as follows
package com.example.communication; import android.app.Activity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.os.Bundle; import android.os.IBinder; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; public class MainActivity extends Activity { private MsgService msgService; private ProgressBar mProgressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView(R.layout.activity_main); //Bind Service Intent intent = new Intent("com.example.communication.MSG_ACTION"); bindService(intent, conn, Context.BIND_AUTO_CREATE); mProgressBar = (ProgressBar) findViewById(R.id.progressBar1); Button mButton = (Button) findViewById(R.id.button1); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //start download msgService.startDownLoad (); } }); } ServiceConnection conn = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { } @Override public void onServiceConnected(ComponentName name, IBinder service) { //return a MsgService object msgService = ((MsgService.MsgBinder)service).getService(); //Register a callback interface to receive changes in download progress msgService.setOnProgressListener(new OnProgressListener() { @Override public void onProgress(int progress) { mProgressBar.setProgress(progress); } }); } }; @Override protected void onDestroy() { unbindService(conn); super.onDestroy (); } }
Is it more convenient to use the callback interface? When the progress changes, the Service actively informs the Activity, and the Activity can update the UI operation.
- In the form of broadcast (broadcast)
When our progress changes, we send a broadcast, then register the broadcast receiver in the Activity, and update the ProgressBar after receiving the broadcast. The code is as follows
package com.example.communication; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.ProgressBar; public class MainActivity extends Activity { private ProgressBar mProgressBar; private Intent mIntent; private MsgReceiver msgReceiver; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate (savedInstanceState); setContentView(R.layout.activity_main); // Dynamically register the broadcast receiver msgReceiver = new MsgReceiver(); IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("com.example.communication.RECEIVER"); registerReceiver(msgReceiver, intentFilter); mProgressBar = (ProgressBar) findViewById(R.id.progressBar1); Button mButton = (Button) findViewById(R.id.button1); mButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // start the service mIntent = new Intent("com.example.communication.MSG_ACTION"); startService(mIntent); } }); } @Override protected void onDestroy() { //Out of service stopService(mIntent); // log out of the broadcast unregisterReceiver(msgReceiver); super.onDestroy (); } /** * Broadcast receiver * @author len * */ public class MsgReceiver extends BroadcastReceiver{ @Override public void onReceive(Context context, Intent intent) { //Get the progress, update the UI int progress = intent.getIntExtra("progress", 0); mProgressBar.setProgress(progress); } } }
package com.example.communication; import android.app.Service; import android.content.Intent; import android.os.IBinder; public class MsgService extends Service { /** * the maximum value of the progress bar */ public static final int MAX_PROGRESS = 100; /** * The progress value of the progress bar */ private int progress = 0; private Intent intent = new Intent("com.example.communication.RECEIVER"); /** * Simulate download tasks, update every second */ public void startDownLoad(){ new Thread(new Runnable() { @Override public void run() { while(progress < MAX_PROGRESS){ progress += 5; //Send a broadcast whose Action is com.example.communication.RECEIVER intent.putExtra("progress", progress); sendBroadcast (intent); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace (); } } } }).start(); } @Override public int onStartCommand(Intent intent, int flags, int startId) { startDownLoad (); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { return null; } }
Summarize:
- Activity calls the bindService (Intent service, ServiceConnection conn, int flags) method to get a reference to the Service object, so that the Activity can directly call the methods in the Service. If we want to actively notify the Activity, we can use the callback method
- Service sends messages to Activity, you can use broadcast, of course, Activity needs to register the corresponding receiver. For example, if Service wants to send the same message to multiple Activity, it is better to use this method