android的Service的由浅到深(一)

1.说明:

Service是Android中四大组件之一,在Android开发中起到非常重要的作用,先来看一下官方对Service的定义:

Service is an application component that can perform long-running operations in the background and does not provide a user interface. Another application component can start a service and it will continue to run in the background even if the user switches to another application. Additionally, a component can bind to a service to interact with it and even perform interprocess communication (IPC). For example, a service might handle network transactions, play music, perform file I/O, or interact with a content provider, all from the background.


翻译过来就是:Service(服务)是一个没有用户界面的在后台运行执行耗时操作的应用组件。其他应用组件能够启动Service,并且当用户切换到另外的应用场景,Service将持续在后台运行(Activity在进入到后台,它的活动是停止的)。另外,一个组件能够绑定到一个service与之交互(IPC机制),例如,一个service可能会处理网络操作,播放音乐,操作文件I/O或者与内容提供者(content provider)交互,所有这些活动都是在后台进行(可控)。


2.生命周期

Service有两种状态,“启动的”和“绑定”

Started

A service is "started" when an application component (such as an activity) starts it by calling  startService(). Once started, a service can run in the background indefinitely, even if the component that started it is destroyed. Usually, a started service performs a single operation and does not return a result to the caller. For example, it might download or upload a file over the network. When the operation is done, the service should stop itself.

Bound

A service is "bound" when an application component binds to it by calling bindService(). A bound service offers a client-server interface that allows components to interact with the service, send requests, get results, and even do so across processes with interprocess communication (IPC). A bound service runs only as long as another application component is bound to it. Multiple components can bind to the service at once, but when all of them unbind, the service is destroyed.


通过startService()启动的服务处于“启动的”状态,一旦启动,service就在后台运行,即使启动它的应用组件已经被销毁了。通常started状态的service执行单任务并且不返回任何结果给启动者。比如当下载或上传一个文件,当这项操作完成时,service应该停止它本身。多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。


还有一种“绑定”状态的service,通过调用bindService()来启动,一个绑定的service提供一个允许组件与service交互的接口,可以发送请求、获取返回结果,还可以通过夸进程通信来交互(IPC)。绑定的service只有当应用组件绑定后才能运行,多个组件可以绑定一个service,当调用unbind()方法时,这个service就会被销毁了。

另外,在官方的说明文档中还有一个警告:


Caution: A service runs in the main thread of its hosting process—the service does not create its own thread and does not run in a separate process (unless you specify otherwise). This means that, if your service is going to do any CPU intensive work or blocking operations (such as MP3 playback or networking), you should create a new thread within the service to do that work. By using a separate thread, you will reduce the risk of Application Not Responding (ANR) errors and the application's main thread can remain dedicated to user interaction with your activities.


意思是service与activity一样都存在与当前进程的主线程中,所以,一些阻塞UI的操作,比如耗时操作不能放在service里进行,比如另外开启一个线程来处理诸如网络请求的耗时操作。如果在service里进行一些耗CPU和耗时操作,可能会引发ANR警告,这时应用会弹出是强制关闭还是等待的对话框。所以,对service的理解就是和activity平级的,只不过是看不见的,在后台运行的一个组件,这也是为什么和activity同被说为Android的基本组件。

Service生命周期中的一些方法:


通过这个图可以看到,两种启动service的方式以及他们的生命周期,bind service的不同之处在于当绑定的组件销毁后,对应的service也就被kill了。service的声明周期相比与activity的简单了许多,只要好好理解两种启动service方式的异同就行。


service生命周期也涉及一些回调方法,这些方法都不用调用父类方法,具体如下:

public class MyService extends Service {
	private int localCount = 0;

	class MyIBinder extends Binder implements LocalService {
		@Override
		public int getCount() {
			return localCount;
		}
	}

	@Override
	public void onCreate() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					while (true) {
						Log.i("MyService", "onCreate:" + localCount);
						Thread.sleep(1000);
						localCount++;
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();

		super.onCreate();
	}

	@Override
	public void onStart(Intent intent, int startId) {
		Log.i("MyService", "onStart");
		super.onStart(intent, startId);
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Log.i("MyService", "onStartCommand");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public void onDestroy() {
		Log.i("MyService", "onDestroy");
		super.onDestroy();
	}

	@Override
	public boolean onUnbind(Intent intent) {
		Log.i("MyService", "onUnbind");
		return super.onUnbind(intent);
	}

	@Override
	public IBinder onBind(Intent arg0) {
		Log.i("MyService", "onBind");
		return new MyIBinder();
	}

}
关于Service生命周期还有一张比较易懂的图(来源于网络)

3.Service and Thread


Service如果是Local Service,那么它是运行在UI线程上的,可以把它看作就是UI线程上的一段代码,只是这段代码是在后台运行的。


如果是Remote Service,那么对应的 Service 则是运行在独立进程的 Main 线程上,它的运行周期已经和UI进程没有什么关系了,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread (Context.stopService())或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。


当然,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,这些都是 Thread 做不到的。     


多个组件可以绑定一个service。



另外,这里要说明Service的一个子类,IntentService,首先看下官方文档的说明:

IntentService

This is a subclass of Service that uses a worker thread to handle all start requests, one at a time. This is the best option if you don't require that your service handle multiple requests simultaneously. All you need to do is implement onHandleIntent(), which receives the intent for each start request so you can do the background work.


IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread(线程)来处理队列中的Intent,对于异步的startService请求,IntentService会处理完成一个之后再处理第二个,每一个请求都会在一个单独的worker thread中处理,不会阻塞应用程序的主线程,这里就给我们提供了一个思路,如果有耗时的操作与其在Service里面开启新线程还不如使用IntentService来处理耗时操作。而在一般的继承Service里面如果要进行耗时操作就必须另开线程,但是使用IntentService就可以直接在里面进行耗时操作,因为默认实现了一个worker thread。对于异步的startService请求,IntentService会处理完成一个之后再处理第二个。


看下IntentService的具体实现:

public class HelloIntentService extends IntentService {

  /** 
   * A constructor is required, and must call the super IntentService(String)
   * constructor with a name for the worker thread.
   */
  public HelloIntentService() {
      super("HelloIntentService");
  }

  /**
   * The IntentService calls this method from the default worker thread with
   * the intent that started the service. When this method returns, IntentService
   * stops the service, as appropriate.
   */
  @Override
  protected void onHandleIntent(Intent intent) {
      // Normally we would do some work here, like download a file.
      // For our sample, we just sleep for 5 seconds.
      long endTime = System.currentTimeMillis() + 5*1000;
      while (System.currentTimeMillis() < endTime) {
          synchronized (this) {
              try {
                  wait(endTime - System.currentTimeMillis());
              } catch (Exception e) {
              }
          }
      }
  }
}

          关于停止Service,如果service是非绑定的,最终当任务完成时,为了节省系统资源,一定要停止service,可      以通过stopSelf()来停止,也可以在其他组件中通过           stopService()来停止,绑定的service可以通过onUnBind()      来停止service。


当在service中启动了新的线程,在service结束的时候一定要注意是否需要结束线程,否则会造成资源的浪费,并可能会导致业务逻辑错误(若是新线程中进行业务逻辑的处理)。

</pre><pre name="code" class="java">	public void onCreate() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					while (true) {
						Log.i("MyService", "onCreate:" + localCount);
						Thread.sleep(1000);
						localCount++;
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();

		super.onCreate();
	}
在service结束的时候,new Thread仍然会继续执行。


4.通讯

Service与Activity的通讯手段很多,IBinder、BroaderCast,本文将介绍另一种手段:Messenger


官方文档解释:它引用了一个Handler对象,以便others(Messenger)能够向它发送消息(使用mMessenger.
send(Message msg)方法)。该类允许跨进程间基于Message的通信(即两个进程间可以通过Message进行通信),在服务端使用Handler创建一个Messenger,客户端持有这个Messenger就可以与服务端通信了。
 
以前我们使用Handler+Message的方式进行通信,都是在同一个进程中,从线程持有一个主线程的Handler对象,并向主线程发送消息。而Android既然可以使用bindler机制进行跨进行通信,所以我们当然可以将Handler与bindler结合起来进行跨进程发送消息。
查看API就可以发现,Messenger就是这种方式的实现。
一般使用方法如下:
1、远程通过mMessenger = new Messenger(mHandler)  创建一个信使对象
2、客户端使用bindlerService请求连接远程
3、远程onBind方法返回一个bindler  return mMessenger.getBinder();  
4.客户端使用远程返回的bindler得到一个信使(即得到远程信使)

public void onServiceConnected(ComponentName name, IBinder service) {    
              rMessenger = new Messenger(service);      
             ...... 
 }  
这里虽然是new了一个Messenger,但我们查看它的实现
public Messenger(IBinder target) {      mTarget = IMessenger.Stub.asInterface(target);  }  
 发现它的mTarget是通过Aidl得到的,实际上就是远程创建的那个。
5、客户端可以使用这个远程信使对象向远程发送消息:rMessenger.send(msg);
这样远程服务端的Handler对象就能收到消息了,然后可以在其handlerMessage(Message msg)方法中进行处理。(该Handler对象就是第一步服务端创建Messenger时使用的参数mHandler).
mHandler.obtainMessage()
经过这5个步骤貌似只有客户端向服务端发送消息,这样的消息传递是单向的,那么如何实现双向传递呢?

首先需要在第5步稍加修改,在send(msg)前通过msm.replyTo = mMessenger将自己的信使设置到消息中,这样服务端接收到消息时同时也得到了客户端的信使对象了,然后服务端可以通过/得到客户端的信使对象,并向它发送消息  cMessenger = msg.replyTo;  cMessenger.send(message);  
 即完成了从服务端向客户端发送消息的功能,这样客服端可以在自己的Handler对象的handlerMessage方法中接收服务端发送来的message进行处理。
双向通信宣告完成。

public class Binding extends Activity implements View.OnClickListener {
	private Messenger mMessenger;
	private Messenger rMessenger;
	private TextView mCallbackText;
	private boolean isBound;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		initView();
	}

	private Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MessengerService.MSG_SET_VALUE:
				mCallbackText.setText("Received from service: " + msg.arg1);
				break;
			default:
				break;
			}
		}
	};

	private ServiceConnection connection = new ServiceConnection() {
		public void onServiceConnected(ComponentName name, IBinder service) {
			rMessenger = new Messenger(service);
			mMessenger = new Messenger(mHandler);
			sendInitMessage();
		}

		public void onServiceDisconnected(ComponentName name) {
			rMessenger = null;
		}
	};

	/**
	 * 使用服务端的信使向它发送一个消息。
	 */
	private void sendInitMessage() {
		Message message = Message.obtain(null, MessengerService.MSG_SET_VALUE);
		message.replyTo = mMessenger;
		try {
			rMessenger.send(message);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	public void onClick(View v) {
		Intent intent = new Intent("com.app.messenger_service");
		switch (v.getId()) {
		case R.id.bind:
			if (!isBound) {
				isBound = bindService(intent, connection, BIND_AUTO_CREATE);
			} 
			break;
		case R.id.unbind:
			if (isBound) {
				unbindService(connection);
				isBound = false;
			}
			break;
		default:
			break;
		}
	}

	private void initView() {
		findViewById(R.id.bind).setOnClickListener(this);
		findViewById(R.id.unbind).setOnClickListener(this);
		mCallbackText = (TextView) findViewById(R.id.callback);
		mCallbackText.setText("Not attached.");
	}
}

public class MessengerService extends Service {
	private Messenger cMessenger;
	private boolean connecting = false;
	static final int MSG_REGISTER_CLIENT = 1;
	static final int MSG_UNREGISTER_CLIENT = 2;
	static final int MSG_SET_VALUE = 3;

	private Handler mHandler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			switch (msg.what) {
			case MSG_SET_VALUE:
				try {
					Message message = Message.obtain(null,
							MessengerService.MSG_SET_VALUE);
					message.arg1 = 0;
					cMessenger = msg.replyTo;
					cMessenger.send(message);
				} catch (RemoteException e) {
					e.printStackTrace();
				}
				break;
			default:
				super.handleMessage(msg);
			}
		}
	};
	private Messenger mMessenger = new Messenger(mHandler);

	@Override
	public void onDestroy() {

		super.onDestroy();
	}

	@Override
	public boolean onUnbind(Intent intent) {
		connecting = false;
		return super.onUnbind(intent);
	}

	@Override
	public IBinder onBind(Intent intent) {
		connecting = true;
		new Thread(new Runnable() {
			@Override
			public void run() {
				int i = 0;
				try {
					while (connecting) {
						Message message = Message.obtain(null, MSG_SET_VALUE);
						Thread.sleep(1000);
						message.arg1 = i++;
						cMessenger.send(message);
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				} catch (RemoteException e) {
					e.printStackTrace();
				}

			}
		}).start();
		return mMessenger.getBinder();
	}

	@Override
	public void onRebind(Intent intent) {
		Toast.makeText(this, "onRebind", Toast.LENGTH_SHORT).show();
	}

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值