一、 Service简介
Service是android 零碎中的四大组件之一(Activity、Service、BroadcastReceiver、ContentProvider),它跟Activity的级别差不多,但不能自己运行只能后盾运行,并且能够和其余组件进行交互。service能够在很多场合的利用中应用,比方播放多媒体的时候用户启动了其余Activity这个时候程序要在后盾持续播放,比方检测SD卡上文件的变动,再或者在后盾记录你地理信息地位的扭转等等,总之服务总是藏在后盾的。
Service的启动有两种形式:context.startService() 和 context.bindService()
二、 Service启动流程
context.startService() 启动流程:
context.startService() -> onCreate() -> onStart() -> Service running -> context.stopService() -> onDestroy() -> Service stop
如果Service还没有运行,则android先调用onCreate(),而后调用onStart();
如果Service曾经运行,则只调用onStart(),所以一个Service的onStart办法可能会反复调用屡次。
如果stopService的时候会间接onDestroy,如果是调用者本人间接退出而没有调用stopService的话,Service会始终在后盾运行,该Service的调用者再启动起来后能够通过stopService敞开Service。
所以调用startService的生命周期为: onCreate --> onStart (可屡次调用) --> onDestroy
context.bindService()启动流程:
context.bindService() -> onCreate() -> onBind() -> Service running -> onUnbind() -> onDestroy() -> Service stop
onBind()将返回给客户端一个IBind接口实例,IBind容许客户端回调服务的办法,比方失去Service的实例、运行状态或其余操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。
所以调用bindService的生命周期为:onCreate --> onBind(只一次,不可屡次绑定) --> onUnbind --> onDestory。
在Service每一次的开启敞开过程中,只有onStart可被屡次调用(通过屡次startService调用),其余onCreate,onBind,onUnbind,onDestory在一个生命周期中只能被调用一次。
三、 Service生命周期
Service的生命周期并不像Activity那么简单,它只继承了onCreate()、onStart()、onDestroy()三个办法
当咱们第一次启动Service时,先后调用了onCreate()、onStart()这两个办法;当进行Service时,则执行onDestroy()办法。
这里须要留神的是,如果Service曾经启动了,当咱们再次启动Service时,不会在执行onCreate()办法,而是间接执行onStart()办法。
它能够通过Service.stopSelf()办法或者Service.stopSelfResult()办法来进行本人,只有调用一次stopService()办法便能够进行服务,无论调用了多少次的启动服务办法。
四、 Service示例
上面我做了一个简略的音乐播放的利用,别离应用startService和bindService来启动本地的服务。
Activity
public class PlayMusicService extends Activity implements OnClickListener { private Button playBtn; private Button stopBtn; private Button pauseBtn; private Button exitBtn; private Button closeBtn; private Intent intent; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.music_service); playBtn = (Button) findViewById(R.id.play); stopBtn = (Button) findViewById(R.id.stop); pauseBtn = (Button) findViewById(R.id.pause); exitBtn = (Button) findViewById(R.id.exit); closeBtn = (Button) findViewById(R.id.close); playBtn.setOnClickListener(this); stopBtn.setOnClickListener(this); pauseBtn.setOnClickListener(this); exitBtn.setOnClickListener(this); closeBtn.setOnClickListener(this); } @Override public void onClick(View v) { int op = -1; intent = new Intent("com.homer.service.musicService"); switch (v.getId()) { case R.id.play: // play music op = 1; break; case R.id.stop: // stop music op = 2; break; case R.id.pause: // pause music op = 3; break; case R.id.close: // close activity this.finish(); break; case R.id.exit: // stopService op = 4; stopService(intent); this.finish(); break; } Bundle bundle = new Bundle(); bundle.putInt("op", op); intent.putExtras(bundle); startService(intent); // startService } @Override public void onDestroy(){ super.onDestroy(); if(intent != null){ stopService(intent); } } }
Service
public class MusicService extends Service { private static final String TAG = "MyService"; private MediaPlayer mediaPlayer; @Override public IBinder onBind(Intent arg0) { return null; } @Override public void onCreate() { Log.v(TAG, "onCreate"); Toast.makeText(this, "show media player", Toast.LENGTH_SHORT).show(); if (mediaPlayer == null) { mediaPlayer = MediaPlayer.create(this, R.raw.tmp); mediaPlayer.setLooping(false); } } @Override public void onDestroy() { Log.v(TAG, "onDestroy"); Toast.makeText(this, "stop media player", Toast.LENGTH_SHORT); if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release(); } } @Override public void onStart(Intent intent, int startId) { Log.v(TAG, "onStart"); if (intent != null) { Bundle bundle = intent.getExtras(); if (bundle != null) { int op = bundle.getInt("op"); switch (op) { case 1: play(); break; case 2: stop(); break; case 3: pause(); break; } } } } public void play() { if (!mediaPlayer.isPlaying()) { mediaPlayer.start(); } } public void pause() { if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); } } public void stop() { if (mediaPlayer != null) { mediaPlayer.stop(); try { mediaPlayer.prepare(); // 在调用stop后如果须要再次通过start进行播放,须要之前调用prepare函数 } catch (IOException ex) { ex.printStackTrace(); } } } }
AndroidManifest.xml
注册activity
<activity android:name=".service.PlayMusicService" android:label="@string/app_name" />
注册service
<service android:name=".service.MusicService" android:enabled="true" > <intent-filter> <action android:name="com.homer.service.musicService" /> </intent-filter> </service>
五、 代码解析
1、Activity中,PlayMusicService中通过重写OnClickListener 接口onClick()办法实现对播放音乐的管制,把音乐各种操作用数字通过Intent传递给service
而后通过结构一个Intent , intent = new Intent("com.homer.service.musicService");
其中,com.homer.service.musicService是 AndroidManifest.xml 对service的定义,即下面“注册service”
2、Activity中,音乐播放的管制,利用Bundle绑定数字op后,通过 startService(intent); 服务后发送进来
Bundle bundle = new Bundle();
bundle.putInt("op", op);
intent.putExtras(bundle);
startService(intent);
3、 Service中,会解决Activity启动的 startService(intent);服务,顺次调用service的启动过程:onCreate --> onStart(可屡次调用) --> onDestroy
onCreate(), 创立mediaPlayer
onStart(), 通过获取Bundle bundle = intent.getExtras();,提取int op = bundle.getInt("op");,而后执行响应的音乐播放操作
onDestroy(),进行并开释mediaPlayer音乐资源,如果当执行context.stopService()时调用此办法
4、Activity中,onClick()函数中close与exit是执行含意是不同的:
close : 只是执行了this.finish(); 敞开了本Activity窗体,service并没有被关掉,音乐仍然会持续在后盾播放
exit : 先调用了stopService(intent); 敞开了service服务,在Service中会调用3中的onDestroy()进行并开释音乐资源,后才执行this.finish(); 敞开了本Activity窗体
六、 拓展常识(过程和申明周期)
Android操作系统尝试尽可能长时间的放弃利用的过程,但当可用内存很低时最终要移走一部分过程。怎么确定那些程序能够运行,那些要被销毁,Android让每一个过程在一个重要级的根底上运行,重要级低的过程最有可能被淘汰,一共有5级,上面这个列表就是依照重要性排列的:
1 一个前台过程显示的是用户此时须要解决和显示的。下列的条件有任何一个成立,这个过程都被认为是在前台运行的。
a 与用户正产生交互的。
b 它管制一个与用户交互的必须的根本的服务。
c 有一个正在调用生命周期的回调函数的service(如onCreate()、onStar()、onDestroy())
d 它有一个正在运行onReceive()办法的播送接管对象。
只有多数的前台过程能够在任何给定的工夫内运行,销毁他们是零碎万不得已的、最初的抉择——当内存不够零碎持续运行上来时。通常,在这一点上,设施曾经达到了内存分页状态,所以杀掉一些前台过程来保障可能响应用户的需要。
2 一个可用过程没有任何前台组件,但它依然能够影响到用户的界面。上面两种状况产生时,能够称该过程为可用过程。
它是一个非前台的activity,但对用户依然可用(onPause()办法曾经被调用)这是可能产生的,例如:前台的activity是一个容许上一个activity可见的对话框,即以后activity半透明,能看到前一个activity的界面,它是一个服务于可用activity的服务。
3 一个服务过程是一个通过调用startService()办法启动的服务,并且不属于前两种状况。只管服务过程没有间接被用户看到,但他们的确是用户所关怀的,比方后盾播放音乐或网络下载数据。所以零碎保障他们的运行,直到不能保障所有的前台可见程序都失常运行时才会终止他们。
4 一个后盾过程就是一个非以后正在运行的activity(activity的onStop()办法曾经被调用),他们不会对用户体验造成间接的影响,当没有足够内存来运行前台可见程序时,他们将会被终止。通常,后盾过程会有很多个在运行,所以他们保护一个LRU最近应用程序列表来保障常常运行的activity能最初一个被终止。如果一个activity正确的实现了生命周期的办法,并且保留它以后状态,杀死这些过程将不会影响到用户体验。
5 一个空线程没有运行任何可用应用程序组,保留他们的惟一起因是为了设立一个缓存机制,来放慢组件启动的工夫。零碎常常杀死这些内存来均衡零碎的整个零碎的资源,过程缓存和根本外围缓存之间的资源。
Android把过程里优先级最高的activity或服务,作为这个过程的优先级。例如,一个过程领有一个服务和一个可见的activity,那么这个过程将会被定义为可见过程,而不是服务过程。
此外,如果别的过程依赖某一个过程的话,那么被依赖的过程会进步优先级。一个过程服务于另一个过程,那么提供服务的过程不会低于取得服务的过程。例如,如果过程A的一个内容提供商服务于过程B的一个客户端,或者过程A的一个service被过程B的一个组件绑定,那么过程A至多领有和过程B一样的优先级,或者更高。
因为一个运行服务的过程的优先级高于运行后盾activity的过程,一个activity会筹备一个长时间运行的操作来启动一个服务,而不是启动一个线程–尤其是这个操作可能会拖垮这个activity。例如后盾播放音乐的同时,通过照相机向服务器发送一张照片,启动一个服务会保障这个操作至多运行在service 过程的优先级下,无论这个activity产生了什么,播送接收者应该作为一个空服务而不是简略的把耗时的操作独自放在一个线程里。