android组件之一Service

   Service作为android四大组件之一,在开发中是相当重要的。

  Service(服务)是能够在后台执行耗时操作且不提供用户界面的应用程序组件,其他应用程序能启动服务,并且即便是用户切换到另一个应用程序时,服务还可以在后台运行。此外Service还可以与其他主键绑定并与之交互。后台播放音乐是最典型的列子。

一、Service生命周期:

                                           

从上图可以看出,启动service的方式不同,生命周期也就不同。

一种方式是通过startService(Intent)方式启动服务,该方式历经的周期从OnCreate()-->OnStartCommand()-->OnDestory()方法。

另一种是采用与activity绑定的方式启动服务,也就是图中的OnCreate()-->OnBind()-->onUnbind()-->onDestory()方法。

这两种启动方式有什么不同,下面用到一个列子来加深理解。


《一》:StartServie()启动方式启动service跟启动一个activity方式差不多,都要借助于Intent意图对象来指明具体启动那个service。同时也需要在Manifest里面去注册Service:

注册:指定android:name属性并对应服务类名。



自定义的MyService类,里面的所有方法都打印日志

public class MySercive extends Service {
	private MediaPlayer mMediaplaryer;
	File file;
	@Override
	public void onCreate() {   //只执行一次,当这个service已经创建好之后,再次调用startServic()方法时不会再执行这个方法,只会执行下面的onStartVommand()方法
		super.onCreate();
		file = Environment.getExternalStorageDirectory();
		mMediaplaryer = new MediaPlayer();
		Mylog.i("----service-------------onCreate----前----------------" + file.getAbsolutePath()+"/cafe.mp3");
		playMusic(file.getAbsolutePath()+"/cafe.mp3");
		Mylog.i("----service-------------onCreate-----后---------------" + file.getAbsolutePath()+"/cafe.mp3");
	}
	
	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		Mylog.i("----service-------------onStartCommand--------------------");
		return super.onStartCommand(intent, flags, startId);
	}

	@Override
	public IBinder onBind(Intent arg0) {
		Mylog.i("----service-------------onBind--------------------");
		return null;
	}
	
	@Override
	public boolean onUnbind(Intent intent) {
		
		Mylog.i("----service-------------onUnbind--------------------");
		return super.onUnbind(intent);
	}
	
	@Override
	public void onDestroy() {
		Mylog.i("----service-------------onDestroy--------------------");
		super.onDestroy();
		mMediaplaryer.stop();
	}
	
	
	public void playMusic(String filepath) {
		try {
		//	if (mMediaplaryer.isPlaying()) {
				mMediaplaryer.reset();
		//	}
			mMediaplaryer.setDataSource(filepath);
			mMediaplaryer.prepare();
			mMediaplaryer.start();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (IllegalStateException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

}


测试代码就两个按钮,一个启动服务(这里对应的是播放音乐),一个结束是解释播放服务

@Override
	public void onClick(View v) {
		switch(v.getId()){
		case R.id.service_start_btn:
			Intent intent = new Intent(TestServiceActivity.this,MySercive.class);
			startService(intent);
			break;
		case R.id.service_end_btn:
			Intent endIntent = new Intent(TestServiceActivity.this,MySercive.class);
			stopService(endIntent);
		}
	}
当点击启动按钮时,打印日志如下:


可以看出,在调用StartService启动服务时执行了onCreate()和onStartCommand(),之后点击停止按钮,日志如下:

停止服务StopService调用了onDestory方法,启动和停止都用到了Intent意图对象。

特别注意的是:当多次点击启动按钮时,第一次会调用onCreate()和onStartCommand(),第二次以后就不会再去调用

onCreate()方法了。只是调用了onStartCommand()方法。


《二》:采用bindService启动Service,这种方式可以将服务与某个Activity绑定,该服务专门为绑定的Activity服务。

前面那个MyService有两个方法OnBind()、OnUnBind()方法还没用上。这两个方法就是绑定Activity是需要调用的方法。这里的列子实现了在服务里面让一个count整型数据自加,并每隔一秒用日志打印该值。当启动Activity时就启动服务,在Activity上设置三个按钮,第一个重新设置这个count数值,第二个为取得当前count的数值,用这两个来表现service与activity之间的参数交互,第三个按钮用来结束服务。


/**
 * Bindservice启动service
 * 实现service与activity之间的交互
 * 1、定义一个交互接口
 * 2、定义一个IBinder类并实现交互接口 ServiceIBinder extends Binder implements ServiceCount
 * 3、onBind()方法返回该IBinder类的实列
 */
import mylog.Mylog;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;

public class MyBinderService extends Service {
	private boolean flag = true;  //循环标签
	private int count;
	private ServiceIBinder serviceIBinder = new ServiceIBinder();
	public interface ServiceCount{   //交互接口
		public void setCount(int _count);
		public int getCount();
		public void changeFalg(boolean _flag);
	}
	
	public class ServiceIBinder extends Binder implements ServiceCount{

		@Override
		public void setCount(int _count) {
			count = _count;
		}

		@Override
		public int getCount() {
			return count;
		}

		@Override
		public void changeFalg(boolean _flag) {
			flag = _flag;  // 结束死循环
		}
		
	}
	
	
	@Override
	public void onCreate() {
		super.onCreate();
		Mylog.i("-----------onCreate-----------------");
		new Thread(new Runnable() {
			@Override
			// 启动线程让count自增
			public void run() {
				while (flag) {
					try {
						Thread.sleep(1000);
						count++;
						Mylog.i("----------------------------" + count);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
		}).start();
	}

	@Override
	public IBinder onBind(Intent arg0) {
		Mylog.i("-----------onBind---------------------");
		return serviceIBinder;        //将这个IBinder返回回去
	}
	
	@Override
	public boolean onUnbind(Intent intent) {
		Mylog.i("-----------onUnbind---------------------");
		return super.onUnbind(intent);
	}
	
	@Override
	public void onDestroy() {
		super.onDestroy();
		Mylog.i("-----------onDestroy---------------------");
	}

}

Service实现类。

(1)、定义交互接口。

(2)、定义一个交互类继承Binder类并实现交互接口。

(3)、OnBind()方法返回一个IBinder对象,即返回交互类的对象实列(他们是子孙关系,交互类继承Binder,Binder又是继承IBinder)。这个对象就可以与activity关联。

测试Activity代码如下:

package service_by_binder;
/**activity与service交互
 * 1、bindService()启动service
 * 2、实列化ServiceConnection接口对象并实现方法得到service里面的IBinder对象
 * 3、用IBinder对象调用对应方法得到参数值
 */
import mylog.Mylog;
import service_by_binder.MyBinderService.ServiceIBinder;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;

import com.example.call.R;

public class ServiceActivity extends Activity implements OnClickListener{
	private Button mStartBtn,mGetConunBtn,mEndBtn;
	private TextView mShowCountTxt;
	private MyBinderService mService;
	private int count;
	private ServiceIBinder mBinder;
	private ServiceConnection mServiceConnection = new ServiceConnection() {
		@Override
		public void onServiceDisconnected(ComponentName name) {
			mBinder = null;
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			mBinder = (ServiceIBinder) service;   //这里可以看到service回调过来的IBinder对象。从而可以得到service的一些参数
		}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.service_binder_layout);
		initView();
	}
	
	public void initView(){
		mStartBtn = (Button) findViewById(R.id.service_startBtn);
		mStartBtn.setOnClickListener(this);
		mGetConunBtn = (Button) findViewById(R.id.service_getCountBtn);
		mGetConunBtn.setOnClickListener(this);
		mShowCountTxt = (TextView) findViewById(R.id.service_text);
		mEndBtn = (Button) findViewById(R.id.service_distoryBtn);
		mEndBtn.setOnClickListener(this);
		
		Intent intent = new Intent(this,MyBinderService.class);
		bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
	}

	@Override
	public void onClick(View v) {
		switch(v.getId()){
		case R.id.service_startBtn:
			count = 30;
			mBinder.setCount(count);  //调用方法设置对应参数
			break;
			
		case R.id.service_getCountBtn:
			int index = mBinder.getCount();  // 调用方法得到对应参数
			mShowCountTxt.setText("" + index);
			break;
		case R.id.service_distoryBtn:
			mBinder.changeFalg(false);
			unbindService(mServiceConnection);
			break;
		}
	}
bindService(intent ,serviceConnection,flag)启动service有三个参数,intent指定启动那个服务, serviceConnection参数则决定了服务完成后反馈给activity什么结果。这个参数采用如下方式构造:

private ServiceIBinder mBinder;
	private ServiceConnection mServiceConnection = new ServiceConnection() {
		@Override
		public void onServiceDisconnected(ComponentName name) {
			mBinder = null;
		}
		
		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			mBinder = (ServiceIBinder) service;   //这里可以看到service回调过来的IBinder对象。从而可以得到service的一些参数
		}
	};
里面可以得到我们在Servie类里面定义的交互类的对象Binder。从而可以调用他的实现方法,达到一个交互的效果。
调用unbindService(ServiceConnection)即可执行生命周期中的OnUnbind()和OnDestory()停止服务进行。日志如下:


Service与Thread的关系:

首先告诉大家,Service和Thread是完全不同的两个概念,这两者之间没有半毛钱的关系。

1). Thread:Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。

2). Service:Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程,它跟线程半毛钱的关系都没有!

既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个 Activity 被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的 run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。

   举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。

    因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。

因此一个较标准的Service就该如下定义onStartCommand()和交互类binder的子类里的方法了(对应两种启动方式)。

@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		new Thread(new Runnable() {
			
			@Override
			public void run() {
				//耗时操作
			}
		}).start();
		return super.onStartCommand(intent, flags, startId);
	}


public class ServiceIBinder extends Binder implements ServiceCount{
		@Override
		public void startDownLod() {
			new Thread(new Runnable() {
				
				@Override
				public void run() {
				<span style="white-space:pre">	</span>//耗时操作<span style="font-family: Arial, Helvetica, sans-serif;">	</span><span style="white-space:pre">
</span>				}
			}).start();
		}

总结:1、startservice启动不能进行交互,但可以达到结束掉与之绑定的控件后仍能在后台运行。采用stopservice结束服务

2、bindservice启动方式可以进行交互,当与之绑定的控件被Kill掉时,服务也结束。unbindservice停止服务。

3、startservice和bindservice两种方式结合使用就有两者的优点了,即可传参进行交互,又可以一直在后台跑起。

结束服务时,就需要调用stopservice和unbindservice两个方法才能kill掉服务。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值