Android Service的使用
一、Service:
(一)、Service 简介:
1、何谓“Service”?
“Service” 意思即“服务”的意思,像Windows 上面的服务一样,服务是在后台上运行,承担着静悄悄的不为人所注意的工作。Service运行在后台,它是不可见的、无界面的程序。
Service可以在很多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity,这个时候程序要在后台继续播放;比如检测SD卡上文件的变化;再或者在后台记录用户的地理信息位置的改变;或者启动一个服务来运行并一直监听某种动作等等。
2、Service概念的总结:
?Service在后台运行,不可以与用户直接交互;
?一个服务不是一个单独的进程。服务对象本身并不意味着它是在自己的进程中运行,除非另有规定,否则它与运行程序是同在一个进程中;
?一个服务不是一个单独的线程。Service和其他组件一样,默认情况下,Service中的所有代码都是运行在主线程中;
?Service存在的价值虽然不如Activity那么清晰。但是一般都让Service 执行耗时较长的操作。例如:播放音乐、下载文件、上传文件等等。但是因为Service默认运行在主线程中,因此不能直接用它来做耗时的请求或者动作,最好在Service中启动新线程来运行耗时的任务;
?需要通过某一个Activity或其他Context对象来启动Service。
context.startService() 或context.bindService();
?Service很大程度上充当了应用程序后台线程管理器的角色。(如果Activity中新开启一个线程,当该Acitivyt关闭后,该线程依然在工作,但是与开启它的Activity失去联系。也就是说此时的这个线程处于失去管理的状态。但是使用Service,则可以对后台运行的线程有效地管理。)
3、为什么不使用后台线程而使用Service?
?1、Service可以放在独立的进程中,所以更安全;
?2、使用Service可以依赖现有的binder机制,不需要在应用层面上处理线程同步的繁杂工作;
?3、系统可以重新启动异常死去的Service。
4、Service 与Activity 的相同点与不同点:
?不同点:Activity是与用户交互的组件,即可以看到UI界面,而Service 是在后台运行、无需界面;
?相同点:使用Activity 时我们需要在配置文件中声明
定的生命周期。
(二)、服务的分类:
1、本地服务Local Service:
Local Service 用于应用程序内部。用于实现应用程序自己的一些耗时任务,比如查询升级信息,并不占用应用程序比如Activity所属线程,而是单开线程后台执行,这样用户体验比较好。
启动service有两种方法:
1)、Context.startService()
调用者与服务之间没有关联,即使调用者退出,服务仍可运行
2)、Context.bindService()
调用者与服务绑定在一起,调用者一旦退出,服务也就终止
A、根据启动方式将本地服务分为:启动服务Started Service和绑定服务Bound Service。【重点】
?Started Service:被启动的服务
?
o被启动的服务是由其它组件调用startService()方法而启动的,该方法会导致被启动服务的生命周期方法onStartCommand()被回
调。当服务是被启动状态后,其生命周期与启动它的组件无关,即
使启动服务的组件(Activity,BroadcastReceiver)已经被销毁,
该服务还可以在后台无限期运行。除非调用stopSelf()或
stopService()来停止该服务。
?Bound Service:被绑定的服务
?
o绑定服务是允许其它应用程序绑定并且与之交互的Service的实现类。为了提供绑定,必须实现onBind()回调方法。该方法返回
IBinder对象,它定义了服务类与Activity交互的程序接口。
o Activity通过bindService()方法绑定到服务类,同时Activity 必须提供ServiceConnection接口的实现类,它监视Activity与
服务类之间的连接。在重写ServiceConnection接口的
onServiceConnected()方法时,实现了将服务类顺利赋值到了
Activity中,实现了在Activity中使用该服务类并执行其中的方
法。
B、根据onStartCommand()回调方法的返回值,将Service分为粘性Service和非粘性Service:
onStartCommand()方法有三种返回值:
?START_STICKY(常量值:1):sticky的意思是“粘性的”。使用这个返回值时,我们启动的服务跟应用程序"粘"在一起,如果在执行完
onStartCommand后,服务被异常kill掉,系统会自动重启该服务。当再次启动服务时,传入的第一个参数将为null;
?START_NOT_STICKY(常量值:2):“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务。
?START_REDELIVER_INTENT(常量值:3):重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。
【备注:】
以上三种情况,可以理解为发生车祸后的人:
?START_STICKY:(常量值:1)车祸后自己苏醒,但是失忆;
?START_NOT_STICKY:(常量值:2)车祸后再也没有苏醒;
?START_REDELIVER_INTENT:(常量值:3)车祸后自己苏醒,依然保持记忆。
2、远程服务Remote Service:
Remote Service 用于android系统内部的应用程序之间。可以定义接口并把接口暴露出来,以便其他应用进行操作。可被其他应用程序复用,比如天气预报服务,其他应用程序不需要再写这样的服务,调用已有的即可。
(三)、Service的生命周期:
1、Started Service的生命周期:
?onCreate():创建服务
?onStartCommand():服务开始运行(在2.0以前版本中,使用onStart()回调方法)
?onDestroy() :服务被停止
【详细说明:】
?在程序中调用:context.startService() 会触发执行Service生命周期中的onCreate()、onStartCommand()回调方法,此时服务就开始正式运行;
?如果Service还没有运行,则android先调用onCreate()然后调用onStartCommand();如果Service已经运行,则只调用onStartCommand(),所以一个Service的onStartCommand方法可能会重复调用多次;
?如果在程序中调用:context.stopService()会触发执行Service生命周期中的onDestroy()回调方法,会让服务停止;
?stopService()的时候直接onDestroy,如果是调用者自己直接退出而没有调用stopService()的话,Service会一直在后台运行。该Service的
调用者再启动该Service后可以通过stopService关闭Service;
stopSelf()
?所以StartService的生命周期为:onCreate --> onStartCommand(可多次调用) -->onDestroy。
2、Bound Service的生命周期:
?onCreate():创建服务
?onBind():绑定服务,服务开始运行
?onUnbind():取消绑定
?onDestroy() :服务被停止
【详细说明:】
?在程序中调用:context.bindService()会触发执行Service生命周期中的onCreate()、onBind()回调方法,此时服务开始运行;
?onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。此后调用者(Context,例如Activity)会和Service绑定在一起;
?如果调用Service的调用者Context退出了,那么会依次调用Service 生命周期中的onUnbind()、onDestroy()回调方法,会让服务停
止;
?所以BindService的生命周期为:onCreate -->onBind(只一次,不可多次绑定) -->onUnbind -->onDestory。
【备注:】
?Service是不能自己启动的,只有通过 Context 对象调用
startService() 和bindService() 方法来启动。
?在Service每一次的开启关闭过程中,只有onStartCommand()可被多次调用(通过多次startService调用),其他onCreate()、onBind()、
onUnbind()、onDestory()在一个生命周期中只能被调用一次。
?
?Service可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他Activity这个时候程序要在后台继续播放,比如检测SD卡上文件
的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后头的。
二、本地Service:
(一)、Started Service启动MediaPlayer播放音乐:
1、操作步骤:
?1、写xml布局文件;
?2、写MainActivity文件,通过按钮点击事件启动Service;
?3、写继承于Service的StartService类文件:重写
onCreate()/onStartCommand()/onDestroy()回调方法。
2、XML布局文件核心代码:
android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical">
3、MainActivty核心代码:
publicclass MainActivity extends Activity { privatestaticfinal String TAG = "MainActivity";
private Intent intent = null;
@Override
protectedvoid onCreate(Bundle savedInstanceState) { Log.i(TAG, "==onCreate执行");
super.onCreate(savedInstanceState);
setContentView(https://www.360docs.net/doc/102805639.html,yout.activity_main);
intent = new Intent(this, StartServicePlayMusic.class); }
@Override
protectedvoid onDestroy() {
Log.i("MainActivty", "==onDestroy()");
super.onDestroy();
if (intent != null) {
// 停止服务可以通过stopService(intent)来停止,也可以intent到Service程序中,通过stopSelf()来停止。
stopService(intent);
}
}
publicvoid clickButton(View view) {
int type = 0;
switch (view.getId()) {
case R.id.button_main_play:
type = 1;
break;
case R.id.button_main_pause:
type = 2;
break;
case R.id.button_main_stop:
type = 3;
break;
case R.id.button_main_exit:
finish();
break;
case R.id.button_main_stopservice:
// 停止服务可以通过stopService(intent)来停止,也可以intent到Service程序中,通过stopSelf()来停止
// stopService(intent);
// finish();
type = 4;
break;
}
Bundle bundle = new Bundle();
bundle.putInt("type", type);
intent.putExtras(bundle);
startService(intent);
}
}
4、StartServicePlayMusic核心代码:
publicclass StartServicePlayMusic extends Service { privatestaticfinal String TAG = "StartServicePlayMusic"; private MediaPlayer mediaPlayer;
@Override
public IBinderonBind(Intent intent) {
Log.i(TAG, "==onBind执行");
returnnull;
}
@Override
publicvoid onCreate() {
Log.i(TAG, "==onCreate执行");
super.onCreate();
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(this, R.raw.hitta); mediaPlayer.setLooping(false);
}
}
@Override
publicint onStartCommand(Intent intent, int flags, int startId) { Log.i(TAG, "==onStartCommand执行");
if (intent != null) {
Bundle bundle = intent.getExtras();
int type = bundle.getInt("type");
switch (type) {
case 1:
play();
break;
case 2:
pause();
break;
case 3:
stop();
break;
case 4:
stopSelf();
break;
}
}
return START_STICKY;
}
@Override
publicvoid onDestroy() {
Log.i(TAG, "==onDestroy执行"); super.onDestroy();
if (mediaPlayer != null) { mediaPlayer.stop(); mediaPlayer.release();
}
}
publicvoid play() {
if (!mediaPlayer.isPlaying()) { mediaPlayer.start();
}
}
publicvoid pause() {
if (mediaPlayer.isPlaying()) { mediaPlayer.pause();
}
}
publicvoid stop() {
if (mediaPlayer != null) {
mediaPlayer.stop();
}
}
}
5、Manifest清单配置文件核心代码:
android:name=".StartServicePlayMusic">
6、Started Service总结:
Activity页面中需要startService(intent) 和stopService(intent)两个方法来启动Service和停止Service;
?继承于Service类的自定义子类——MyStartService类中,生命周期回调方法有:onCreate() 、onStartCommand() 、onDestroy();
?如果停止服务,可以在Activity中调用stopService(intent),也可以intent到Service 中执行stopSelf()方法;
?执行停止服务方法,会回调Service生命周期中的onDestroy()方法;
?如果希望关闭Activity窗体,服务也停止,那么在Activity的onDestroy()方法中执行stopService()方法。如果希望关闭窗体后,服务还继续,那么Activity的
onDestroy()中不执行停止服务即可;
?在StartService中不会回调onBind()方法;
?在停止服务后,如果再次点击“播放”,可以重新启动StartService。
7、IntentService与Service:
不管是何种Service,它默认都是在应用程序的主线程(亦即UI线程)中运行的。所以,如果你的Service将要运行非常耗时或者可能被阻塞的操作时,你的应用程序将会被挂起,甚至会出现ANR错误。为了避免这一问题,你应该在Service中重新启动一个新的线程来进行这些操作。现有两种方法大家参考:
①直接在Service的onStartCommand()方法中新建一个线程来执行;
②Android SDK 中为我们提供了一个现成的Service类来实现这个功能,它就是IntentService,它主要负责以下几个方面:
?Creates a default worker thread that executes all intents delivered to onStartCommand() separate from your application's main thread.
?生成一个默认的且与主线程互相独立的工作者线程来执行所有传送至onStartCommand() 方法的Intetnt
?Creates a work queue that passes one intent at a time to your onHandleIntent() implementation, so you never have to worry about
multi-threading.
?生成一个工作队列来传送Intent对象给你的onHandleIntent()方法,同一时刻只传送一个Intent对象,这样一来,你就不必担心多线程的问题。
?Stops the service after all start requests have been handled, so you never have to call stopSelf().
?在所有的请求(Intent)都被执行完以后会自动停止服务,所以,你不需要自己去调用stopSelf()方法来停止该服务
?Provides default implementation of onBind() that returns null.
?提供了一个onBind()方法的默认实现,它返回null
?Provides a default implementation of onStartCommand() that sends the intent to the work queue and then to your onHandleIntent()
implementation
?提供了一个onStartCommand()方法的默认实现,它将Intent先传送至工作队列,然后从工作队列中每次取出一个传送至onHandleIntent()方法,在该方法中对Intent对相应的处理
IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread(线程)来处理队列中的Intent,对于异步的startService请求,IntentService会处理完成一个之后再处理第二个,每一个请求都会在一个单独的worker thread中处理,不会阻塞应用程序的主线程。
这里就给我们提供了一个思路,如果有耗时的操作可以在Service里面开启新线程,也可以使用IntentService来处理耗时操作。但你若是想在Service中让多个线程并发的话,就得使用第一种方法,在Service内部起多个线程,但是这样的话,你可要处理好线程的同步。
(1)、Service实现加载图片的核心代码:
publicclass DownloadService extends IntentService { privatestaticfinal String TAG = "DownloadService";
private String urlString =
"https://https://www.360docs.net/doc/102805639.html,/images/srpr/logo11w.png";
private NotificationCompat.Builder builder = null;
private NotificationManager manager = null;
public DownloadService() {
super("");
}
@Override
protectedvoid onHandleIntent(Intent intent) {
manager = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
builder= new NotificationCompat.Builder(getApplicationContext()); builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("提示:");
builder.setContentText("图片加载完成,请点击查看!");
builder.setTicker("图片加载完成");
builder.setAutoCancel(true);
Intent intent2 = new Intent(getApplicationContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntentpIntent = PendingIntent.getActivity( getApplicationContext(), 0, intent2,
PendingIntent.FLAG_ONE_SHOT);
builder.setContentIntent(pIntent);
byte[] data = HttpClientHelper.loadByteFromURL(urlString); boolean flag = SDCardHelper.saveFileToSDCard(data, "Download", "logo11w.png");
if (flag) {
manager.notify(1, builder.build());
}
}
}
(2)、IntentService实现加载图片的核心代码:
publicclass DownloadService extends Service {
privatestaticfinal String TAG = "DownloadService";
private String urlString =
"https://https://www.360docs.net/doc/102805639.html,/images/srpr/logo11w.png";
private NotificationCompat.Builder builder = null;
private NotificationManager manager = null;
@Override
public IBinderonBind(Intent intent) {
returnnull;
}
@Override
publicvoid onCreate() {
super.onCreate();
manager = (NotificationManager)
getSystemService(NOTIFICATION_SERVICE);
builder= new NotificationCompat.Builder(getApplicationContext());
builder.setSmallIcon(R.drawable.ic_launcher);
builder.setContentTitle("提示:");
builder.setContentText("图片加载完成,请点击查看!");
builder.setTicker("图片加载完成");
builder.setAutoCancel(true);
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); PendingIntentpIntent = PendingIntent
.getActivity(getApplicationContext(), 0, intent, PendingIntent.FLAG_ONE_SHOT);
builder.setContentIntent(pIntent);
}
@Override
publicint onStartCommand(Intent intent, int flags, int startId) { new Thread(new Runnable() {
@Override
publicvoid run() {
byte[] data = HttpClientHelper.loadByteFromURL(urlString); boolean flag = SDCardHelper.saveFileToSDCard(data, "Download", "logo11w.png");
if (flag) {
manager.notify(1, builder.build());
stopSelf();
}
}
}).start();
return START_STICKY;
}
@Override
publicvoid onDestroy() {
super.onDestroy();
}
}
(二)、Bound Service启动MediaPlayer播放音乐:
1、操作步骤:
?1、写xml布局文件;
?2、写MainActivity文件,构建ServiceConnection对象,重写其中的抽象方法onServiceDisconnected()和onServiceConnected();