Several ways of communication between Android Service and Activity

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. 

 

 

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:

 

 

  1. 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 
  2.  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 

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326847284&siteId=291194637