zoukankan      html  css  js  c++  java
  • Android学习笔记39-Android四大组件之Service

    Android应用程序的四大组件分别是Activity、Service、BroadcastReceiver和ContentProvider。其中,有关Activity的介绍可以参阅博文《Android学习笔记38:Android四大组件之Activity》。有关ContentProvider的使用方法可以参阅博文《Android学习笔记37:使用Content Providers方式共享数据

      本文将主要对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       @Override
    10       public void onCreate() {
    11           super.onCreate();
    12           Log.i(TAG, "MyService-->onCreate()");
    13       }
    14 
    15       @Override
    16       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       @Override
    22       public IBinder onBind(Intent intent) {
    23           return null;
    24       }
    25       
    26       @Override
    27       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:                    //停止Service
    13                 Intent intent_stopService = new Intent(MainActivity.this, MyService.class);
    14                 stopService(intent_stopService);
    15                 break;
    16             }
    17         }
    18     };
    复制代码

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

    1   <!-- 注册MyService组件 -->
    2     <service 
    3         android:name=".MyService"    >
    4     </service>

      至此,我们便完成了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         //断开Service
    16         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         @Override
    14         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       @Override
    15       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中。

  • 相关阅读:
    Python3之random模块常用方法
    Go语言学习笔记(九)之数组
    Go语言学习笔记之简单的几个排序
    Go语言学习笔记(八)
    Python3之logging模块
    Go语言学习笔记(六)
    123. Best Time to Buy and Sell Stock III(js)
    122. Best Time to Buy and Sell Stock II(js)
    121. Best Time to Buy and Sell Stock(js)
    120. Triangle(js)
  • 原文地址:https://www.cnblogs.com/britalient/p/3173261.html
Copyright © 2011-2022 走看看