浅谈Android Service

Service 服务

什么是服务:

A service is not a separate process and A service is not thread; A Service is an application component that can perform long-running opreations in the background and doesnot provider a userinterface.

  • 服务是一个无界面化的应用程序组件
  • 服务一般用于后台进行耗时操作

Service的生命周期:

![Upload service_lifecycle.png failed. Please try again.]

startService方式开启服务:

  • startService(Intent service),通过intent值来指定启动哪个Service,可以直接指定目标Service的名,也可以通过Intent的action属性来启动设置了相应action属性的Service,使用这种方式启动的Service,当启动它的Activity被销毁时,是不会影响到它的运行的,这时它仍然继续在后台运行它的工作。直至调用StopService(Intent service)方法时或者是当系统资源非常紧缺时,这个服务才会调用onDestory()方法停止运行。所以这种Service一般可以用做,处理一些耗时的工作。
  • 四大组件默认都是和activity运行在同一个主线程中的,那就是说activity通过startservice方法启动一个服务后,被启动的服务和activity都是在同一个线程中的。所以当我主动销毁了这个activity,但是他所在的线程还是存在的,只不过是这个activity他所占用的资源被释放掉了,这个activity所在的主线程只有当android内存不足才会被杀死掉,否则一般的情况下这个activity所在的应用程序的线程始终存在,也就是这个activity所启动的服务也会一直运行下去。
Service

    public class LifeService extends Service {
        private static final String TAG = "LifeService";

        @Override
        public void onCreate() {
            Log.d(TAG, "服务------onCreate");
            super.onCreate();
    
        }
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.d(TAG, "服务------onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
        @Override
        public void onDestroy() {
            Log.d(TAG, "服务------onDestroy");
            super.onDestroy();
        }
    }
Activity

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
    
        public void startService(View view) {
            Intent intent = new Intent(this, LifeService.class);
            startService(intent);
        }
    
        public void stopService(View view) {
    
            Intent intent = new Intent(this, LifeService.class);
            stopService(intent);
        }
    }
运行结果:

![Upload start_service_lifecycle.png failed. Please try again.]

Service 可以被开启多次,但是只会创建一次.

bindService方式开启服务

  • bindService开启的服务,可以调用到服务中的方法.
  • 启动的LifeService是和MainActivity在同一个进程里的,因为在注册服务时,没有配置它的android:process = "xxxx" 属性。
Service

    public class LifeService extends Service {
        private static final String TAG = "LifeService";

        @Override
        public void onCreate() {
            Log.d(TAG, "服务------onCreate");
            super.onCreate();
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.d(TAG, "服务------onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public void onDestroy() {
            Log.d(TAG, "服务------onDestroy");
            super.onDestroy();
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            Log.d(TAG, "服务------onUnbind");
            return super.onUnbind(intent);
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Log.d(TAG, "服务------onBind");
            return new Mybind();
        }
    
        public class Mybind extends Binder {
    
            public void callMethodInService() {
                methodInService();
            }
    
        }
        
        public void methodInService() {
            Toast.makeText(this, "服务里的方法被调用了", Toast.LENGTH_SHORT).show();
            Log.d(TAG, "服务里的方法被调用了");
        }   
    }
Activity

    public class MainActivity extends AppCompatActivity {
    
        private ServiceConnection conn;
        private LifeService.Mybind mBinder;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
        
        //绑定服务
        public void bindService(View view) {
            Intent intent = new Intent(this, LifeService.class);
            conn = new MyServiceConnection();
            bindService(intent, conn, BIND_AUTO_CREATE);
        }
    
        private class MyServiceConnection implements ServiceConnection {
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mBinder = (LifeService.Mybind) service;
    
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        }
    
        //解绑服务
        public void unbindService(View view) {
            unbindService(conn);
        }
        //调用服务里的方法
        public void callMethodInService(View view) {
            mBinder.callMethodInService();
        }
    }
运行结果

![Upload bind_service_1.png failed. Please try again.]

出现一个有意思的现象: 当解绑服务后,service已经onDestroy(),但是还是能调用服务中的方法.运行图如下:

![Upload bind_service_2.png failed. Please try again.]

服务虽然是onDestroy了,但是MainActivity中还保留LifeService.Binder的引用,服务中的方法也保留了Service自身的引用,所以即便是Service onDestroy()了,但是还是可以调用到服务中的方法.

混合方式开启服务

  • startService开启服务: 服务能在后台长期运行,不能调用服务中方法.
  • bindService开启服务: 能调用服务中的方法,但是不能在后台长期运行.
  • 混合方式开启服务: 保证服务后台长期运行, 还能调用服务中的方法.

![Upload service_binding_tree_lifecycle.png failed. Please try again.]

Service

    public class LifeService extends Service {
        private static final String TAG = "LifeService";
    
    
        @Override
        public void onCreate() {
            Log.d(TAG, "服务------onCreate");
            super.onCreate();
    
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            Log.d(TAG, "服务------onStartCommand");
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Override
        public void onDestroy() {
            Log.d(TAG, "服务------onDestroy");
            super.onDestroy();
        }
    
        @Override
        public boolean onUnbind(Intent intent) {
            Log.d(TAG, "服务------onUnbind");
            return super.onUnbind(intent);
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Log.d(TAG, "服务------onBind");
            return new Mybind();
        }
    
        public class Mybind extends Binder {
    
            public void callMethodInService() {
                methodInService();
            }
    
        }
    
    
        public void methodInService() {
            Toast.makeText(this, "服务里的方法被调用了", Toast.LENGTH_SHORT).show();
            Log.d(TAG, "服务里的方法被调用了");
        }
    
    }
Activity

    public class MainActivity extends AppCompatActivity {
        private ServiceConnection conn;
        private LifeService.Mybind mBinder;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
        }
        //开启服务
        public void startService(View view) {
            Intent intent = new Intent(this, LifeService.class);
            startService(intent);
        }
        //停止服务
        public void stopService(View view) {
            Intent intent = new Intent(this, LifeService.class);
            stopService(intent);
        }
    
        //绑定服务
        public void bindService(View view) {
            Intent intent = new Intent(this, LifeService.class);
            conn = new MyServiceConnection();
            bindService(intent, conn, BIND_AUTO_CREATE);
        }
        //解绑服务
        public void unbindService(View view) {
            unbindService(conn);
        }
    
        private class MyServiceConnection implements ServiceConnection {
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mBinder = (LifeService.Mybind) service;
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
    
            }
        }
        //调用服务中的方法
        public void callMethodInService(View view) {
            mBinder.callMethodInService();
        }
    }
运行结果

![Upload mix_start_service.png failed. Please try again.]

注意几点:

  • 以startService方式开启的服务, 解绑服务,并不能使服务onDestroy
  • IBinder: the communication channel to the service,may return null if clients not connect to services.
  • unlike other application components, calls on to the IBinder interface returned here may not happen on the main thread of the process

接口

利用接口屏蔽方法内部实现的细节, 只暴露需要暴露的方法.

IService

    public interface IService {
        void callMethodInService();
    }
Service

    private class Mybind extends Binder implements IService {
        public void callMethodInService() {
                methodInService();
        }
    
    }
Activity

    private class MyServiceConnection implements ServiceConnection {
    
         @Override
         public void onServiceConnected(ComponentName name, IBinder service) {
            //转化Iservice对象
             mIService = (IService) service;
         }
          @Override
          public void onServiceDisconnected(ComponentName name) {
    
          }
     }

IntentService 类

Service 的子类,它使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现 onHandleIntent() 方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。由于大多数启动服务都不必同时处理多个请求(实际上,这种多线程情况可能很危险),因此使用 IntentService 类实现服务也许是最好的选择。

IntentService 执行以下操作:

  • 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。
  • 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。
  • 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。
  • 提供 onBind() 的默认实现(返回 null)。
  • 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。

综上所述,您只需实现 onHandleIntent() 来完成客户端提供的工作即可。(不过,您还需要为服务提供小型构造函数。)


    public class HelloIntentService extends IntentService {
    
      /**
       * A constructor is required, and must call the super IntentService(String)
       * constructor with a name for the worker thread.
       */
      public HelloIntentService() {
          super("HelloIntentService");
      }
    
      /**
       * The IntentService calls this method from the default worker thread with
       * the intent that started the service. When this method returns, IntentService
       * stops the service, as appropriate.
       */
      @Override
      protected void onHandleIntent(Intent intent) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              // Restore interrupt status.
              Thread.currentThread().interrupt();
          }
      }
    }

您只需要一个构造函数和一个 onHandleIntent() 实现即可。如果您决定还重写其他回调方法(如 onCreate()、onStartCommand() 或 onDestroy()),请确保调用超类实现,以便 IntentService 能够妥善处理工作线程的生命周期。

执行多线程耗时操作Service

正如上一部分中所述,使用 IntentService 显著简化了启动服务的实现。但是,若要求服务执行多线程(而不是通过工作队列处理启动请求),则可扩展 Service 类来处理每个 Intent。

为了便于比较,以下提供了 Service 类实现的代码示例,该类执行的工作与上述使用 IntentService 的示例完全相同。也就是说,对于每个启动请求,它均使用工作线程执行作业,且每次仅处理一个请求。


    public class HelloService extends Service {
      private Looper mServiceLooper;
      private ServiceHandler mServiceHandler;
    
      // Handler that receives messages from the thread
      private final class ServiceHandler extends Handler {
          public ServiceHandler(Looper looper) {
              super(looper);
          }
          @Override
          public void handleMessage(Message msg) {
              // Normally we would do some work here, like download a file.
              // For our sample, we just sleep for 5 seconds.
              try {
                  Thread.sleep(5000);
              } catch (InterruptedException e) {
                  // Restore interrupt status.
                  Thread.currentThread().interrupt();
              }
              // Stop the service using the startId, so that we don't stop
              // the service in the middle of handling another job
              stopSelf(msg.arg1);
          }
      }
    
      @Override
      public void onCreate() {
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread thread = new HandlerThread("ServiceStartArguments",
                Process.THREAD_PRIORITY_BACKGROUND);
        thread.start();
    
        // Get the HandlerThread's Looper and use it for our Handler
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
      }
    
      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
          Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
    
          // For each start request, send a message to start a job and deliver the
          // start ID so we know which request we're stopping when we finish the job
          Message msg = mServiceHandler.obtainMessage();
          msg.arg1 = startId;
          mServiceHandler.sendMessage(msg);
    
          // If we get killed, after returning from here, restart
          return START_STICKY;
      }
    
      @Override
      public IBinder onBind(Intent intent) {
          // We don't provide binding, so return null
          return null;
      }
    
      @Override
      public void onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
      }
    }

正如您所见,与使用 IntentService 相比,这需要执行更多工作。

但是,因为是由您自己处理对 onStartCommand() 的每个调用,因此可以同时执行多个请求。此示例并未这样做,但如果您希望如此,则可为每个请求创建一个新线程,然后立即运行这些线程(而不是等待上一个请求完成)。

请注意,onStartCommand() 方法必须返回整型数。整型数是一个值,用于描述系统应该如何在服务终止的情况下继续运行服务(如上所述,IntentService 的默认实现将为您处理这种情况,不过您可以对其进行修改)。从 onStartCommand() 返回的值必须是以下常量之一:

  • START_NOT_STICKY

    如果系统在 onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。

  • START_STICKY

    如果系统在 onStartCommand() 返回后终止服务,则会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用 onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。

  • START_REDELIVER_INTENT

    如果系统在 onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

前台服务


    public class ForegroundService extends Service {

        @Override
        public void onCreate() {
            super.onCreate();
    
            showNotification();
        }
    
        private void showNotification() {
            
            //创建点击跳转Intent
            Intent inten = new Intent(this, MainActivity.class);
            //创建任务栈Builder
            TaskStackBuilder taskStackBuilder = TaskStackBuilder.create(this);
            taskStackBuilder.addParentStack(MainActivity.class);
            taskStackBuilder.addNextIntent(inten);
            PendingIntent pendingIntent = taskStackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
    
            //创建通知详细信息
            Notification notification = new NotificationCompat.Builder(this)
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setContentTitle("foreground service")
                    .setContentText("show details news")
                    .setWhen(System.currentTimeMillis())
                    .setContentIntent(pendingIntent)
                    .build();
            
            NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            nm.notify(0, notification);
            startForeground(0,notification);
    
        }
    }

系统服务

系统服务提供了很多便捷服务,可以查询Wifi、网络状态、查询电量、查询音量、查询包名、查询Application信息等等等相关多的服务,具体大家可以自信查询文档,这里举例几个常见的服务

  1. 判断Wifi是否开启

    
        WifiManager wm = (WifiManager) getSystemService(WIFI_SERVICE);
        boolean enabled = wm.isWifiEnabled();
    
  2. 获取系统最大音量

```java

    AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
    int max = am.getStreamMaxVolume(AudioManager.STREAM_SYSTEM);
```
  1. 获取当前音量

    
        AudioManager am = (AudioManager) getSystemService(AUDIO_SERVICE);
        int current = am.getStreamMaxVolume(AudioManager.STREAM_RING);
    
  2. 判断网络是否有连接

    
        ConnectivityManager cm = (ConnectivityManager) getSystemService(CONNECTIVITY_SERVICE);
        NetworkInfo info = cm.getActiveNetworkInfo();
        boolean isAvailable = info.isAvailable();
    
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 198,932评论 5 466
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,554评论 2 375
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 145,894评论 0 328
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,442评论 1 268
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,347评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 47,899评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,325评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,980评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,196评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,163评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,085评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,826评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,389评论 3 302
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,501评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,753评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,171评论 2 344
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,616评论 2 339

推荐阅读更多精彩内容

  • 前言:本文所写的是博主的个人见解,如有错误或者不恰当之处,欢迎私信博主,加以改正!原文链接,demo链接 Serv...
    PassersHowe阅读 1,396评论 0 5
  • 参考: 服务|Android Developers 一. 什么是服务 服务是一个可以在后台执行长时间运行操作而不提...
    NickelFox阅读 538评论 0 3
  • 本篇文章是继续上篇android图片压缩上传系列-基础篇文章的续篇。主要目的是:通过Service来执行图片压缩任...
    laogui阅读 4,416评论 5 62
  • 平时说的十句话都比不上生气的时候说的一句话~
    百里春风阅读 181评论 0 0
  • 记得那时候,姥姥家所在的村庄是我所在的小县城水最多的地方,每到春天,大路上就开始渗出水来,四处散落的小水洼成...
    来日缘何方长阅读 279评论 0 1