博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android学习笔记39:Android四大组件之Service
阅读量:5026 次
发布时间:2019-06-12

本文共 10197 字,大约阅读时间需要 33 分钟。

  Android应用程序的四大组件分别是Activity、Service、BroadcastReceiver和ContentProvider。其中,有关Activity的介绍可以参阅博文《》。有关ContentProvider的使用方法可以参阅博文《》

  本文将主要对Service进行介绍。

 

1.Service简介

  与Activity不同,Service没有提供与用户进行交互的用户界面。Service是运行在后台的一种Android组件,当应用程序需要进行某种不需要前台显示的计算或数据处理时,就可以启动一个Service来实现。

  使用Service的目的通常有两个:后台运行和跨进程访问。通过启动一个Service,可以在不显示界面的前提下在后台运行指定的任务,这样可以不影响用户进行界面操作。通过AIDL(Android Interface Definition Language)服务实现不同进程之间的通信。

1.1Service的生命周期

  Service一般由Activity或其他的Context对象来启动,启动方式有两种,对应的生命周期也不相同,具体如图1所示。

图1 Service生命周期示意图

  由图1可以看出,通过startService()方法和通过bindService()方法启动Service是不一样的,下面就具体说说这两种方式的区别。

1.2通过startService()方法启动Service

  一个Service从启动到销毁会经历3个阶段,分别是启动服务、服务执行和销毁服务。当Service经历上述3个阶段时,会分别调用Service类中的3个相应的事件方法:

  (1)public void onCreate();

  (2)public int onStartCommand(Intent intent, int flags, int startId);

  (3)public void onDestroy();

  其中,onCreate()方法用于创建Service;onStartCommand()方法用于开始Service;onDestroy()方法用于销毁服务。

  需要注意的一点是,一个Service只会被创建一次,销毁一次,但可以开始多次,也就是说,onCreate()方法和onDestroy()只会被调用一次,而onStartCommand()方法却可以被多次调用。

  下面就来简单的验证一下。

  首先,我们需要自定义一个Service,让其继承android.app.Service类,并实现其中的onCreate()、onStartCommand()和onDestroy()方法。这里,我创建了一个名为“MyService”的Service类,具体代码如下:

1   /* 2      * 自定义的Service类 3      * 博客园-依旧淡然 4      */ 5   public class MyService extends Service { 6    7       private static final String TAG = "MyService"; 8        9       @Override10       public void onCreate() {11           super.onCreate();12           Log.i(TAG, "MyService-->onCreate()");13       }14 15       @Override16       public int onStartCommand(Intent intent, int flags, int startId) {17           Log.i(TAG, "MyService-->onStartCommand()");18           return super.onStartCommand(intent, flags, startId);19       }20   21       @Override22       public IBinder onBind(Intent intent) {23           return null;24       }25       26       @Override27       public void onDestroy() {28           super.onDestroy();29           Log.i(TAG, "MyService-->onDestroy()");30       }31   }

  其中,onBind()方法是android.app.Service类的抽象方法,所以必须在子类MyService中实现。使用bindService()方法启动Service时,需要用到该方法,这里暂时未用到,所以直接返回null即可。

  实现了自定义的MyService之后,我们便可以在Context对象(比如Activity)中通过调用startService(Intent intent)方法来启动Service,或是通过调用stopService(Intent intent)方法来终止Service。其中,startService()方法和stopService()方法中的参数intent可以用来指定想要启动和终止Service是哪一个。如下的代码,实现了通过Button按钮button_startService启动MyService,通过Button按钮button_stopService停止Service。

1     /* 2      * 实现事件监听器接口 3      * 博客园-依旧淡然 4      */ 5     private OnClickListener clickListenter = new OnClickListener() { 6         public void onClick(View view) { 7             switch(view.getId()) { 8             case R.id.button_startService:                    //启动Service 9                 Intent intent_startService = new Intent(MainActivity.this, MyService.class);10                 startService(intent_startService);11                 break;12             case R.id.button_stopService:                    //停止Service13                 Intent intent_stopService = new Intent(MainActivity.this, MyService.class);14                 stopService(intent_stopService);15                 break;16             }17         }18     };

  最后,我们还需要在AndroidManifest.xml文件中注册我们自定义的MyService组件,即在<application></application>标签中添加如下代码即可:

1   
2
4

  至此,我们便完成了MyService的创建。

  运行程序,点击Button按钮button_startService启动MyService,可以看到“MyService-->onCreate()”以及“MyService-->onStartCommand()”的Log输出信息,表明MyService已经启动。同时,我们也可以点击Menu,退出MainActivity,并依次选择Settings/Applications/Running Services,可以看到如图2所示的界面。

图2 MyService运行在后台

  由图2可以看出,即使退出了MainActivity(MyService的启动者),MyService也不会停止,它仍然会在后台运行。

  当我们再次运行程序,并再次点击Button按钮button_startService启动MyService时,可以看到“MyService-->onStartCommand()”的Log输出信息,表明当Service已经启动时,如果再次调用startService()方法,将只会调用Service类的onStartCommand()方法。

  最后,当我们Button按钮button_stopService时,可以看到“MyService-->onDestroy()”的Log输出信息,表明MyService调用了onDestroy()方法停止了Service。

1.3通过bindService()方法启动Service

  可以看到,通过startService()方式启动Service时,即使启动该Service的Context对象(比如Activity)关闭了,Service仍然会一直运行在后台(如果没有调用stopService()方法的话)。有时我们希望当启动Service的Context对象关闭时,Service也随之关闭,这时我们就可以通过使用bindService()方法来启动Service。通过bindService()方法启动Service可以将Activity和Service绑定。

  下面同样以一个简单的实例来说明如何创建一个Service,并通过bindService()方法来启动该Service。

  首先,我们需要自定义一个Service,让其继承android.app.Service类,并实现其中的onBind()、onRebind()和onUnbind()方法。这里,我创建了一个名为“BinderService”的Service类,具体代码如下:

1   /* 2    * 自定义的Service类 3    * 博客园-依旧淡然 4    */ 5   public class BinderService extends Service { 6    7       private MyBinder myBinder = new MyBinder();        //MyBinder对象,用于获得BinderService对象 8       private static final String TAG = "BinderService"; 9       10       //成功绑定时调用该方法11       public IBinder onBind(Intent intent) {12           Log.i(TAG, "-->onBind()");13           return myBinder;14       }15       16       //重新绑定时调用该方法17       public void onRebind(Intent intent) {18           super.onRebind(intent);19           Log.i(TAG, "-->onRebind()");20       }21   22       //解除绑定时调用该方法23       public boolean onUnbind(Intent intent) {24           Log.i(TAG, "-->onUnbind()");25           return super.onUnbind(intent);26       }27   28       /*29        * MyBinder内部类,扩展自Binder类30        */31       public class MyBinder extends Binder {32           33           //获取BinderService对象34           public BinderService getBinderService() {35               return BinderService.this;36           }37           38       }39   40   }

  其中,当BinderService成功绑定时会调用onBind()方法;当BinderService重新绑定时会调用onRebind()方法;当BinderService解除绑定时会调用onUnbind()方法。MyBinder内部类继承自Binder类,并实现了getBinderService()方法,用于获取当前BinderService对象。

  实现了自定义的BinderService之后,我们便可以在Context对象(比如Activity)中通过调用bindService(Intent service, ServiceConnection conn, int flags)方法启动Service,或是通过调用unbindService(ServiceConnection conn)方法来终止Service。

  在bindService()方法中,第一个参数service表示与Service类相关联的Intent对象;第二个参数conn是一个ServiceConnection接口对象,负责连接Intent对象指定的Service,通过ServiceConnection对象可以获得连接Service成功或者失败的状态;第三个参数flags,一般设置为Context.BIND_AUTO_CREATE。

  创建ServiceConnection接口对象时,需要实现ServiceConnection接口中的抽象方法onServiceConnected()和onServiceDisconnected()。其中,onServiceConnected()方法在连接Service成功时会被调用,onServiceDisconnected()方法在连接Service失败时会被调用。如下的代码实现了这两个方法。

1     /* 2      * 实现ServiceConnection接口 3      * 博客园-依旧淡然 4      */ 5     private ServiceConnection serviceConnection = new ServiceConnection() { 6  7         //连接Service 8         public void onServiceConnected(ComponentName name, IBinder iBinder) { 9             isBinderServiceConnected = true;10             MyBinder myBinder = (MyBinder)iBinder;11             BinderService binderService = myBinder.getBinderService();//获得BinderService对象12             //binderService.xxx();        //调用BinderService中的自定义方法,进行Service相关操作13         }14 15         //断开Service16         public void onServiceDisconnected(ComponentName name) {17             isBinderServiceConnected = false;18         }19         20     };

  通过以上的代码可以看到,我们之前在BinderService类中定义的内部类MyBinder的作用就是获取BinderService对象的实例。获得了BinderService对象之后,便可以在Context对象中调用BinderService中的自定义方法,进行Service相关操作了。

  需要注意的一点是,在Context对象中通过调用bindService()方法启动Service时,onCreate()方法和onRebind()方法会被调用,在Context对象中通过调用unbindService()方法来终止Service时,onUnbind()方法和onDestroy()方法会被调用。

 

2.IntentService的使用

  上面分别介绍了如何通过startService()方式和bindService()方法来启动一个自定义的Service,在使用这两个方法时需要注意以下两个问题。

2.1 UI界面卡死

  因为Service和启动它的Context对象(比如Activity)是在同一个线程里面,所以当我们直接在onStartCommand()方法中进行Service操作时,将会导致UI界面卡死。

  我们可以通过Thread.currentThread().getId()方法来获得主线程以及Service线程的线程Id号,输出Log信息如图3所示。

图3 Log输出信息(1)

  由图3可以看出,Service和启动它的Activity确实是在同一个线程里。这样所带来的直接后果就是,当Service进行比较耗时的操作时UI界面卡死。比如,点击Button启动一个Service进行文件下载,那么在文件下载期间,UI界面将无法和用户进行交互。

2.2多进程调用

  为了解决上面出现的问题,一种简单的解决方法就是在Service的onStartCommand()方法中重新启动一个线程,然后在新启动的线程中进行文件下载等耗时的操作。如下面的代码所示:

1     @Override 2     public int onStartCommand(Intent intent, int flags, int startId) { 3         new MyThread().start();            //启动一个新的线程 4         return super.onStartCommand(intent, flags, startId); 5     } 6  7     /* 8      * 内部类,用于启动一个新的线程 9      * 博客园-依旧淡然10      */    11   private class MyThread extends Thread {12 13         @Override14         public void run() {15             try {16                 Thread.sleep(2000);            //模拟文件下载等耗时的操作17                 Log.i(TAG, "MyService线程ID-->" + Thread.currentThread().getId());18             } catch (InterruptedException e) {19                 e.printStackTrace();20             }21         }22         23     }

  很显然,使用如上的方式,会比直接将文件下载等耗时的操作放在onStartCommand()方法中进行处理要好的多。

  但是,当我们多次启动Service时,onStartCommand()方法将多次被调用,从而导致启动多个线程,如图4所示。

图4 Log输出信息(2)

  由图4可以看出,使用改进后的方法,UI界面能够实时响应用户请求了,但是与此同时也引入了多线程调用的问题。

2.3 IntentService的使用

  为了避免多线程调用的问题,Android提供了IntentService。

  IntentService是Service的一个子类,主要用于处理异步请求。客户端通过startService(Intent intent)方法传递Intent请求给IntentService。在IntentService中,创建了一个与应用程序主线程分开的worker thread,用来处理所有传过来的Intent请求,当处理完所有的intent后停止Service。

  实现IntentService的方法也很简单,覆写IntentService类的构造方法以及onHandleIntent()方法即可,在onHandleIntent()方法中可以完成比较耗时的操作。

  如下的代码自定义了一个名为“MyIntentService”的类,该类继承自IntentService,并覆写IntentService类的构造方法以及onHandleIntent()方法。

1   /* 2    * MyIntentService类,继承自IntentService 3    * 博客园-依旧淡然 4    */ 5   public class MyIntentService extends IntentService { 6        7       private static final String TAG = "MyIntentService"; 8        9       //构造方法10       public MyIntentService(String name) {11           super("MyIntentService");12       }13   14       @Override15       protected void onHandleIntent(Intent intent) {16           try {17               Thread.sleep(2000);                //模拟文件下载等耗时的操作18               Log.i(TAG, "MyIntentService线程ID-->" + Thread.currentThread().getId());19           } catch (InterruptedException e) {20               e.printStackTrace();21           }            22       }23   24   }

  当我们多次启动MyIntentService时,可以看到如图5所示的Log输出信息。

图5 Log输出信息(3)

  由图5可以看出,使用IntentService确实可以避免多线程调用。这是因为,在IntentService中默认创建了一个work queue,当IntentService收到多个Intent请求时,IntentService会将这些Intent请求依次放入work queue中,然后一次只传递一个Intent请求到onHandleIntent()方法中进行处理,从而避免了多线程调用。

  需要注意的一点是,在IntentService中默认实现了onBind()方法,其返回值为null。同样,在IntentService中也默认实现了onStartCommand()方法,在这个方法中实现了将Intent请求放到work queue中。

转载于:https://www.cnblogs.com/menlsh/archive/2013/04/25/3043770.html

你可能感兴趣的文章
AJAX 状态值与状态码详解
查看>>
php面向对象编程(oop)基础知识示例解释
查看>>
1.在数组中找到与给定总和的配对
查看>>
树的子结构
查看>>
关于根据Build Platform或者OS 加载x86或者x64 dll的问题
查看>>
程序员高效开发的几个技巧
查看>>
js-权威指南学习笔记19.2
查看>>
hexo 搭建博客
查看>>
关于 UIWebView 几个高级用法
查看>>
maven创建的项目中无法创建src/main/java 解决方案
查看>>
华为软件开发云测评报告二:代码检查
查看>>
集合1
查看>>
js 原生 ajax
查看>>
关键词 virtual
查看>>
建造者模式(屌丝专用)
查看>>
UVALive 4730 Kingdom +段树和支票托收
查看>>
[APIO2010]特别行动队
查看>>
[SCOI2016]幸运数字
查看>>
SpringBoot 集成ehcache
查看>>
初步swift语言学习笔记2(可选类型?和隐式可选类型!)
查看>>