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。从而可以调用他的实现方法,达到一个交互的效果。
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掉服务。