service常见用法及相关知识

Service组件

身为四大组件之一的Service在程序中使用频率是比较高的,主要用来处理一些不需要UI的耗时的后台操作,或者在后台对外提供接口,供其他组件调用。Service的实现是比较典型的C/S模式,后文介绍用法时会有体会。

两种常见的Service

  • IntentService:适合同一时间只处理一个任务,代码少,使用简单

    是Service类的子类,默认会开启一个工作线程,你需要覆盖onHandleIntent方法,用来处理startService传过来的Intent,在一个生命周期内,如果多次调用startService只会进一次onCreate(),但是会进对应次数的onHandleIntent,在同一时间只能处理一个Intent,也就是说不管调用多少次startService只有一个工作线程在工作,其余的排队等待,一旦所有的Intent处理完成,自动调用onDestroy方法,无须手动调用stopService或者stopSelf

    IntentService的示例

    public class MyIntentService extends IntentService {
    
        private static final String TAG = "MyIntentService";
    
        public static final String ACTION_ONE = "action_one";
        public static final String ACTION_TWO = "action_two";
    
        /**
         * A constructor is required, and must call the super IntentService(String)
         * constructor with a name for the worker thread.
         */
        public MyIntentService() {
            super("MyIntentService");
        }
    
        @Override
        protected void onHandleIntent(Intent intent) {
            if (intent != null) {
                final String action = intent.getAction();
                if (ACTION_ONE.equals(action)) {
                    handleActionOne();
                } else if (ACTION_TWO.equals(action)) {
                    handleActionTwo();
                }
            }
        }
    
        private void handleActionOne(){
            for (int i = 0;i<3;i++){
                Log.i(TAG,"handleActionOne");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        private void handleActionTwo(){
            for (int i = 0;i<3;i++){
                Log.i(TAG,"handleActionTwo");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i(TAG,"onCreate");
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.i(TAG,"onDestroy");
        }
    }

    可以通过如下方法调用多次同时调用

    Intent intent = new Intent(this,MyIntentService.class);
    intent.setAction(MyIntentService.ACTION_ONE);
    //intent.setAction(MyIntentService.ACTION_TWO);
    startService(intent);

    调用后我们会发现IntentService不需要我们另起线程来执行耗时的操作,同时你无论触发多少次ACTION_ONE或者ACTION_TWO动作,它们都会按触发的顺序顺序执行,且任务一旦执行完成无需我们调用stopService或者stopSelf就会自己进入onDestory

  • Service:适合并发请求,代码较多,较复杂,更灵活

    需要手动调用stopService或者执行完任务后调用stopSelf ,即使Service销毁了,其中的线程如果没有执行完会继续执行,如果不掉用的话即使启动该Service的activity销毁了,该Service仍然存在,系统内存不够时会销毁后台的service,service存在时间越长,被销毁的可能性越大

    Service的示例

    public class MyService extends Service {
    
        private static final String TAG = "MyService";
    
        public MyService() {
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            //暂时用不到
            return null;
        }
    
        @Override
        public void onCreate() {
            super.onCreate();
            Log.i(TAG,"onCreate");
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
    
            MyServiceThread thread = new MyServiceThread(startId);
            thread.start();
    
            // If we get killed, after returning from here, restart
            return START_STICKY;
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            Log.i(TAG,"onDestroy");
        }
    
        class MyServiceThread extends Thread{
    
            int startId = 0;
    
            public MyServiceThread(int startId){
                this.startId = startId;
            }
    
            @Override
            public void run() {
                for(int i = 0;i<3;i++){
                    Log.i(TAG,"my startId:"+startId);
                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                //根据startId停止,能保证最近一次的请求执行完才销毁
                stopSelf(startId);
            }
        }
    }

    我们可以通过startService多次调用,但运行完需要显式的调用stopSelf来结束自己,在这里例子中我们可以看到在Service中需要我们自己启动线程来执行耗时的任务,可以同时并行的执行多个任务,还能看到startId的用处,可以用来保证最近的一次请求执行完才onDestory

service使用方式

  • startService

    可以直接看上面的两个例子

  • bindService
    • 继承binder类

      如果只是想利用Service作为你应用的一个后台工作线程的话,这是首选的实现方式,除非你的Service需要被其他应用使用或者跨进程通讯。

      先看Service代码

          public class MyLocalService extends Service {
      
          private static final String TAG = "MyLocalService";
      
          // Binder given to clients
          private final IBinder mBinder = new LocalBinder();
          // Random number generator
          private final Random mGenerator = new Random();
      
          //实现Binder类,是用来在onBind返回,客户端可以在
          //onServiceConnected()回调方法内获取该对象的实例,
          //进而通过该实例的getService()方法获得Service的引用
          //最后就可以通过调用Service的Public方法来和Service通讯
          public class LocalBinder extends Binder{
              MyLocalService getService(){
                  // Return this instance of LocalService so clients can call public methods
                  return MyLocalService.this;
              }
          }
      
          @Override
          public IBinder onBind(Intent intent) {
              return mBinder;
          }
      
          /** method for clients */
          public int getRandomNumber(){
              return mGenerator.nextInt(100);
          }
      
          @Override
          public int onStartCommand(Intent intent, int flags, int startId) {
              Log.i(TAG,"onStartCommand");
              return super.onStartCommand(intent, flags, startId);
          }
      
          @Override
          public boolean onUnbind(Intent intent) {
              Log.i(TAG,"onUnbind");
              return super.onUnbind(intent);
          }
      
          @Override
          public void onRebind(Intent intent) {
              Log.i(TAG,"onRebind");
              super.onRebind(intent);
          }
      
          @Override
          public void onCreate() {
              Log.i(TAG,"onCreate");
              super.onCreate();
          }
      
          @Override
          public void onDestroy() {
              Log.i(TAG,"onDestroy");
              super.onDestroy();
          }
      }

      再看activity代码

          public class ServiceMainActivity extends Activity implements View.OnClickListener{
      
      private final static String TAG = "ServiceMainActivity";
      
      //报存获取的Service的引用
      MyLocalService mService;
      boolean mBound = false;
      
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_service_main);
      
          btnStartBind.setOnClickListener(this);
          btnStopBind.setOnClickListener(this);
          btnGetData.setOnClickListener(this);
      }
      
      @Override
      public void onClick(View v) {
          switch (v.getId()){
              case R.id.btnStartBind:
                  startBind();
                  return;
              case R.id.btnStopBind:
                  stopBind();
                  return;
              case R.id.btnGetBindData:
                  getBindData();
                  return;
          }
      }
      
      /** 定义Service绑定的回调方法,就是通过这个回调方法来实现与Service的绑定的 */
      private ServiceConnection mConnection = new ServiceConnection() {
          @Override
          public void onServiceConnected(ComponentName name, IBinder service) {
              // We‘ve bound to LocalService, cast the IBinder and get LocalService instance
              Log.i(TAG,"onServiceConnected");
              MyLocalService.LocalBinder binder = (MyLocalService.LocalBinder)service;
              mService = binder.getService();
              mBound = true;
          }
      
          @Override
          public void onServiceDisconnected(ComponentName name) {
              Log.i(TAG,"onServiceDisconnected");
              mBound = false;
          }
      };
      
      private void startBind(){
          Intent intent = new Intent(this,MyLocalService.class);
          bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
      }
      
      private void stopBind(){
          if (mBound){
              unbindService(mConnection);
              mBound = false;
          }
      }
      
      private void getBindData(){
          if(mBound){
              // 调用Service的方法
              // 这里注意要避免调用Service里可能导致挂起的方法
              //如果要调用的话需要在独立的线程里调用,避免影响UI线程的性能
              int random = mService.getRandomNumber();
              Toast.makeText(this,"random number:"+random,Toast.LENGTH_SHORT).show();
          }
      }
      }

      使用的步骤总结

      1. 在你的Service中创建一个Binder的实现,包含一个可以被客户端调用的公共方法,这个公共方法需要返回这个Service的实例或者Service中的其他内部类的实例
      2. onBind 方法中返回你实现的Binder的实例
      3. 客户端通过onServiceConnected() 回调方法获取你实现的Binder实例后就可以调用在Service中提供的方法了
    • 使用Messenger

      这是一种简单的实现跨进程通讯方式,因为使用 Messenger queues来处理所有的请求,而所有的请求都在一个线程中,所以你不需要关心Service的线程安全的问题。但是这所有的请求也都是顺序被执行,这种方式应该能满足绝大部分的需求。

      先看Service的代码

      public class MessengerService extends Service {
      
          private static final String TAG = "MessengerService";
      
          /** Command to the service to display a message */
          static final int MSG_SAY_HELLO = 1;
      
          /**
           * 用于存放客户端的Messenger,可以通过这个实例响应客户端,这里只响应一个客户端
           * 可以用集合存储,响应多个客户端
           */
          Messenger clientMessenger;
      
          /**
           * Command to the service to register a client, receiving callbacks from the
           * service. The Message‘s replyTo field must be a Messenger of the client
           * where callbacks should be sent.
           */
          static final int MSG_REGISTER_CLIENT = 2;
      
          /**
           * Handler of incoming messages from clients.
           */
          class IncomingHandler extends Handler{
      
              @Override
              public void handleMessage(Message msg) {
      
                  switch (msg.what){
                      case MSG_SAY_HELLO:
      //                    Toast.makeText(getApplicationContext(),"Hello!",Toast.LENGTH_SHORT).show();
                          Log.i(TAG,"Hello!");
                          if(clientMessenger != null){
                              Message message = Message.obtain(null,MSG_SAY_HELLO);
                              try {
                                  clientMessenger.send(message);
                              } catch (RemoteException e) {
                                  e.printStackTrace();
                              }
                          }
      
                          break;
                      case MSG_REGISTER_CLIENT:
                          clientMessenger = msg.replyTo;
                          break;
                      default:
                          super.handleMessage(msg);
                  }
              }
          }
      
          /**
           * Target we publish for clients to send messages to IncomingHandler.
           */
          final Messenger mMessenger = new Messenger(new IncomingHandler());
      
          /**
           * When binding to the service, we return an interface to our messenger
           * for sending messages to the service.
           */
          @Override
          public IBinder onBind(Intent intent) {
              Log.i(TAG,"binding");
              return mMessenger.getBinder();
          }
      }

      activity的代码

      public class MessengerActivity extends ActionBarActivity implements View.OnClickListener{
      
          private final static String TAG = "MessengerActivity";
      
          /** Messenger for communicating with the service. */
          Messenger mService = null;
      
          /** Flag indicating whether we have called bind on the service. */
          boolean mBound;
      
          class IncomingHandler extends Handler{
      
              @Override
              public void handleMessage(Message msg) {
                  switch (msg.what){
                      case MessengerService.MSG_SAY_HELLO:
                          Log.i(TAG,"response Hello!");
                          break;
                      default:
                          super.handleMessage(msg);
                  }
              }
          }
      
          final Messenger mMessenger = new Messenger(new IncomingHandler());
      
          /**
           * Class for interacting with the main interface of the service.
           */
          private ServiceConnection mConnection = new ServiceConnection() {
              @Override
              public void onServiceConnected(ComponentName name, IBinder service) {
                  // This is called when the connection with the service has been
                  // established, giving us the object we can use to
                  // interact with the service.  We are communicating with the
                  // service using a Messenger, so here we get a client-side
                  // representation of that from the raw IBinder object.
                  mService = new Messenger(service);
                  mBound = true;
      
                  Message msg = Message.obtain(null,MessengerService.MSG_REGISTER_CLIENT);
                  msg.replyTo = mMessenger;
                  try {
                      mService.send(msg);
                  } catch (RemoteException e) {
                      e.printStackTrace();
                  }
              }
      
              @Override
              public void onServiceDisconnected(ComponentName name) {
                  mService = null;
                  mBound = false;
              }
          };
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_messenger);
      
              Button btnBindService = (Button)findViewById(R.id.btnBindService);
              Button btnSayHello = (Button) findViewById(R.id.btnSayHello);
              Button btnUnbindService = (Button) findViewById(R.id.btnUnbindService);
      
              btnBindService.setOnClickListener(this);
              btnSayHello.setOnClickListener(this);
              btnUnbindService.setOnClickListener(this);
      
          }
      
          @Override
          public boolean onCreateOptionsMenu(Menu menu) {
              // Inflate the menu; this adds items to the action bar if it is present.
              getMenuInflater().inflate(R.menu.menu_messenger, menu);
              return true;
          }
      
          @Override
          public boolean onOptionsItemSelected(MenuItem item) {
              // Handle action bar item clicks here. The action bar will
              // automatically handle clicks on the Home/Up button, so long
              // as you specify a parent activity in AndroidManifest.xml.
              int id = item.getItemId();
      
              //noinspection SimplifiableIfStatement
              if (id == R.id.action_settings) {
                  return true;
              }
      
              return super.onOptionsItemSelected(item);
          }
      
          @Override
          public void onClick(View v) {
      
              switch (v.getId()){
                  case R.id.btnBindService:
                      doBindService();
                      break;
                  case R.id.btnSayHello:
                      sayHello();
                      break;
                  case R.id.btnUnbindService:
                      doUnbindService();
                      break;
              }
      
          }
      
          private void sayHello(){
              if(!mBound) return;
      
              // Create and send a message to the service, using a supported ‘what‘ value
              Message msg = Message.obtain(null,MessengerService.MSG_SAY_HELLO);
      
              try {
                  mService.send(msg);
              } catch (RemoteException e) {
                  e.printStackTrace();
              }
          }
      
          private void doBindService(){
              Intent intent = new Intent(this,MessengerService.class);
              bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
              mBound = true;
          }
      
          private void doUnbindService(){
              if(mBound){
                  unbindService(mConnection);
                  mBound = false;
              }
          }
      }

      这里实现了activity和service通过Messenger来进行通讯,在Manifest对应的Service中添加android:process=”:remote”,发现依然可以通讯,证明了这是一种可以跨进程的通讯方式。

      使用步骤总结

      1. Service实现一个Handler,接收客户端发过来的Message
      2. 上面实现的Handler被用来创建一个Messenger对象,这个对象其实就是Handler的引用
      3. 这个Messenger对象可以用来在onBind() 方法中创建一个IBinder对象用于return。
      4. 客户端在onServiceConnected() 回调方法中利用IBinder来实例化客户端中的Messenger,然后就可以调用Messenger实例的send方法,来发送Message到绑定的Service了
      5. Service可以在实现的Handler类中的handleMessage() 方法更具msg.what 来处理客户端不同类型的Message了
      6. 以上只是客户端->Service单向的通讯,如果想让客户端接收Service响应的消息,可以在客户端中创建一个Handler,并用这个Handler创建Messenger对象,在onServiceConnected() 回调方法中通过Message的replyTo发送给Service,Service保留收到的客户端的Messenger,利用该对象的send方法给客户端发送Message,客户端可以在Handler的回调方法handleMessage() 中根据msg.what来不处理不同类型的消息

      需要注意的是:只有activity,service和content provider能够绑定服务,而broadcast receiver 不能够绑定

      客户端使用步骤总结

      1. 实现ServiceConnection类,覆写其中的两个回调方法

        • onServiceConnected() 当和Service绑定后会回调这个方法,需要注意的是bindService() 方法是异步的,Service中的onBind() 方法return的IBinder是通着 onServiceConnected() 传递到客户端的
        • onServiceDisconnected() 这个方法是当Service意外的丢失了而由android系统调用的,比如Service崩溃了或者被系统kill掉了,当客户端调用unbindService()方法该回调方法是不会被调用的
      2. 调用bindService() 方法
      3. onServiceConnected() 方法调用后你就与Service建立了联系,可以调用Service提供的接口跟Service进行通讯了
      4. 调用unbindService() 完成通讯

      以上代码大部分是参照的google提供的示例,自己修改部分代码及注释。个人感觉先看代码,能知道每个方法是做什么用的,最后再来总结一下实现方式,能加深理解记忆

    • 使用AIDL

      其实上面的Messenger底层也是基于AIDL实现的,使用这种方式就更灵活,适合于处理多个需要同时运行的请求,但也带来了复杂性,需要你自己维护Service的线程安全。这里先不详细介绍该用法了。

  • startService and bindService

    就是Service即实现了onBind() 又实现了onStartCommand() 方法,使用的话把上面的两个例子合起来就行了。这里唯一要注意的就是Service的生命周期,下文中会提到。

Service和Thread

  • 使用场景

    • Service通常是运行在后台的,即使用户与应用没有任何交互。且Service可以供多个客户端使用,甚至是其他进程的其他应用。
    • 如果你仅仅是在用户与应用交互的时候需要在主线程外执行一些操作, 这个时候可以用Thread。比如说,只有当你的activity运行的时候才需要播放一段音乐,这个时候你就可以在onCreate() 方法中创建线程,onStop()方法中关闭线程,在Android一般不使用原生的Thread,因为android给我们准备了更好用的 AsyncTask 或者 HandlerThread类供我们使用

你启动的Service默认是运行在应用进程的主线程中的(remote service是运行在自己的进程中),所以不要直接在service中运行资源密集或者阻塞操作,而是需要在service中启动一个线程去执行这些操作

生命周期

  • 先引用一下官网的图

    可以看到不管是startService还是bindService,通用的都有onCreate()onDestory 且在一个生命周期内只会调用一次onCreate()onDestory

  • 以表格形式总结一下
service onCreate onStartCommand onbind onDestory
start Service startService触发,只调用一次 startService触发,多次启动调用多次 / stopService或stopSelf或被系统kill触发,只调用一次
bind Service bindService触发,只调用一次 / bindService触发,一个客户端只触发一次 所有bind的客户端都调用了unbindService或者所有的客户端都不存在了触发,只触发一次
start&bind Service bindService或startService触发,只调用一次 startService触发,多次启动调用多次 bindService触发,一个客户端只触发一次 调用stopService且所有bind的客户端都调用了unbindService或者所有的客户端都不存在了才触发,只触发一次
  • bindService更详细的生命周期

    如果你想让客户端下次再绑定Service的时候调用 onRebind() 方法的话,可以覆写onUnbind()方法,使其返回true,这样下次客户端再调用bindService的时候就会进 onUnbind()方法,而不再进onBind() 方法了,虽然onUnbind()方法是renrun void的,但是客户端仍然能够通过onServiceConnected()回调方法获取到IBinder对象。在官网上有更详细的生命周期配图,如下

参考

MessengerService示例代码

谷歌Service Guide

时间: 2024-10-01 02:21:56

service常见用法及相关知识的相关文章

Spring注解@Component、@Repository、@Service、@Controller的相关知识

Spring注解@Component.@Repository.@Service.@Controller区别 spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository.@Service 和 @Controller.在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层.业务层和控制层(Web 层)相对应.虽然目前这 3 个注释和 @Comp

深入浅出安卓学习相关知识,如何从零学好移动开发

原文发表自我的个人主页,欢迎大家访问 http://purplesword.info/mobile-develop 由于近几年来互联网的飞速发展,安卓和iOS平台的大量普及推广,移动开发在当前是非常热门的一个方向. 有不少同学问我如何学习安卓,要学些什么,难不难学.之前一直没有想好应该怎么回答这个问题,只是简单的说安卓自身门槛不高,并不难学.因为我觉得准确回答一个类似这样的问题往往需要灵感.现在根据我的学习体验,做个大概的总结. 1.我为什么学安卓 我从刚开始接触安卓开发到现在也有两三年的时间了

(整理)ubuntu 的 相关知识(来自 鸟哥的私房菜)

1. Linux 文件权限概念 $ ls 察看文件的指令 $ ls -al 出所有的文件详细的权限与属性 (包含隐藏档,就是文件名第一个字符为『 . 』的文件) 在你第一次以root身份登入Linux时, 如果你输入上述指令后,应该有上列的几个东西,先解释一下上面七个字段个别的意思: 图2.1.1.文件属性的示意图 第一栏代表这个文件的类型与权限(permission): 这个地方最需要注意了!仔细看的话,你应该可以发现这一栏其实共有十个字符:(图2.1.1及图2.1.2内的权限并无关系) 图2

Sox语音转换的相关知识

SoX-linux 里操作音频的瑞士军刀 Sox是最为著名的Open Source声音文件 格式转换工具.已经被广泛移植到Dos.windows.OS2.Sun.Next.Unix.Linux等多个操作系统 平台.Sox项目是由Lance Norskog创立的,后来被众多的开发 者逐步完善,现在已经能够支持很多种声音文件格式和声音处理效果.基本上常见的声音格式都能够支持.更加有用的是,Sox能够进行声音滤波.采样频率转换,这对那些从事声讯平台开发或维护的朋友非常有用.当然,Sox里面也包括一些D

spring事务管理及相关知识

最近在项目中遇到了spring事务的注解及相关知识,突然间感觉自己对于这部分知识只停留在表面的理解层次上,于是乎花些时间上网搜索了一些文章,以及对于源码的解读,整理如下: 一.既然谈到事务,那就先搞清到底什么是事务,或者说,Spring事务管理中的事务到底是指什么? 1.事务(Transaction),通常是指数据库的事务,在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit),例如insert .update.delete等,事务是恢复和并发控制的基本单位. 2.事务

HTML入门基础教程相关知识

HTML入门基础教程 html是什么,什么是html通俗解答: html是hypertext markup language的缩写,即超文本标记语言.html是用于创建可从一个平台移植到另一平台的超文本文档的一种简单标记语言,经常用来创建web页面.html文件是带有格式标识符和超文本链接的内嵌代码的ascii 文本文件——html结构了解. html文本是由 html命令组成的描述性文本,html 命令可以说明文字. 图形.动画.声音.表格.链接等. html网页结构包括头部 (head).主

[转帖]xserver相关知识汇总

xserver相关知识汇总 https://blog.csdn.net/QTVLC/article/details/81739984 本文主要是从以下几个方面介绍xorg-xserver 相关的知识 1.linux系统图形界面框架 2.xserver 和x client启动过程 3.图形2d,3d加速原理简介 4.xserver主分支代码解析. 5.xserver,xclient协议简介 6.一个基于Xlib的简单例子解析 7.radeon驱动初始化代码解析. 1.linux图形界面框架 参考至

php学习day7--函数的相关知识

今天我们主要学了函数的相关知识,是个比较基础的知识,但也是很重要的. 一.函数 函数就类似于一个工具,我们写好函数之后可以直接进行调用,可以很大的减少代码的从用性,提高页面性能和可读性. 1.函数的定义 在php中函数的定义方式为: function  name($形参1,$形参2.....){ 要执行的代码 return  123: } 在上方的函数定义式中,name代表函数名,小括号内是形参,是用来传递参数,花括号中的就是调用时需要执行的代码. 函数的调用方式: name(实参1,实参2,.

linux 服务器分区格式化相关知识 -mount

关于linux 系统mount和mkfs 的相关知识: 使用mount 1)  Mount的相关格式:mount [-t 文件类型][-o  选项] devicedir 详解: -t 文件类型,通常默认mount会自动选择正确的类型,通常类型ext2/ext3/ext4之类的. 常用的类型有:                  光盘或光盘镜像:iso9660 DOS fat16文件系统:msdos Windows 9x fat32文件系统:vfat Windows NT ntfs文件系统:ntf