Android 基础知识2:四大组件之 Service

2019-09-27  本文已影响0人  陈有余
a personal space for everyone

目录

目录

一、Service 的定义

Service 是 Android 中实现程序后台运行的解决方案,它非常适合用于去执行那些不需要和用户交互而且还要求长期运行的任务。但不要被“后台”二字迷惑,Service 默认并不会运行在子线程中,它也不会运行在一个独立的进程中,它同样执行在 UI 线程中,因此,不要在 Service 中执行耗时的操作,除非你在 Service 中创建子线程来完成耗时操作。

Service 的运行不依赖于任何用户界面,即使程序被切换到后台或者用户打开了另一个应用程序,Service 仍然能够保持正常运行,这也正是 Service 的使用场景。当某个应用程序进程被杀掉时,所有依赖于该进程的 Service 也会停止运行。

二、Service 的分类

(一)根据启动方式将 Service 分为:启动服务 Started Service 和绑定服务 Bound Service。

(二)根据 onStartCommand() 回调方法的返回值,将 Service 分为粘性 Service非粘性 Service
onStartCommand() 方法有三种返回值:

  1. START_STICKY(常量值:1):sticky的意思是“粘性的”。使用这个返回值时,我们启动的服务跟应用程序"粘"在一起,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务。当再次启动服务时,传入的第一个参数将为null;

  2. START_NOT_STICKY(常量值:2):“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统不会自动重启该服务;

  3. START_REDELIVER_INTENT(常量值:3):重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。

以上三种情况,可以理解为发生车祸后的人:
START_STICKY:(常量值:1)车祸后自己苏醒,但是失忆;
START_NOT_STICKY:(常量值:2)车祸后再也没有苏醒;
START_REDELIVER_INTENT:(常量值:3)车祸后自己苏醒,依然保持记忆。

(三)根据 Service 的使用范围还可以将其分为:本地服务 Local Service 和 远程服务 Remote Service。

三、Service 的生命周期

Service 的生命周期

(一)Started Service 的生命周期解析:

详细说明

(二)Bound Service 的生命周期解析:

详细说明

备注

  1. Service 是不能自己启动的,只有通过 Context 对象调用 startService() 和 bindService() 方法来启动。
    在 Service 每一次的开启关闭过程中,只有 onStartCommand() 可被多次调用(通过多次 startService 调用),其他 onCreate()、onBind()、onUnbind()、onDestory() 在一个生命周期中只能被调用一次。

  2. Service 可以在和多场合的应用中使用,比如播放多媒体的时候用户启动了其他 Activity 这个时候程序要在后台继续播放,比如检测 SD 卡上文件的变化,再或者在后台记录你地理信息位置的改变等等,总之服务总是藏在后头的。

(三)简单的代码示例

public class MyService extends Service {
    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // do something
        return super.onStartCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
    }
}

与 Activity 一样,Service 也需要在 AndroidManifest.xml 中进行注册:

<service android:name=".MyService" />

四、IntentService

由于 Service 默认运行在主线程,所以如果运行非常耗时的操作时,可能会出现 ANR 的错误。当然,可以在 Service 中开启一个工作线程来解决,但是这样做非常的麻烦。Android 为了省去这样的麻烦,提供了一个 IntentService 来完成这样的操作, IntentService 将用户的请求执行在一个子线程中,用户只需要覆写 onHandleIntent() 函数,并且在该函数中完成自己的耗时操作即可。同时,在任务执行完毕之后 IntentService 会调用 stopSelf 自我销毁。因此,它适合于完成一些短期的耗时操作。示例代码如下:

public class MyIntentService extends IntentService {

    public MyIntentService(String name) {
        super(name);
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        //do something
    }
}

Android 官方对于 IntentService 的说明:

IntentService 使用队列的方式将请求的 Intent 加入队列,然后开启一个 worker thread (线程)来处理队列中的 Intent,对于异步的 startService 请求,IntentService 会处理完成一个之后再处理第二个,每一个请求都会在一个单独的 worker thread 中处理,不会阻塞应用程序的主线程。

五、运行在前台的 Service

Service 默认是运行在后台的,因此,它的优先级相对比较低,当系统出现内存不足的时候,它就有可能被系统回收。如果希望 Service 可以一直保持运行状态,而不会由于系统内存不足被回收,可以将 Service 运行在前台。前台服务不仅不会被回收,它还会在通知栏显示一条消息,下拉状态栏后可以看到更加详细的信息。

public class WeatherService extends Service {
    private static final int NOTIFY_ID = 123;
    private static final String CHANNEL_ONE_ID = "com.cyy.cn";
    private static final String CHANNEL_ONE_NAME = "Channel One";

    @Override
    public void onCreate() {
        super.onCreate();
        showNotification();
    }

    /**
     * 在通知栏显示天气信息
     */
    private void showNotification() {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.drawable.ic_launcher_background)
                .setContentTitle(getText(R.string.the_day))
                .setContentText(getText(R.string.weather));

        Intent intent = new Intent(this, MainActivity.class);

        //创建任务栈
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(MainActivity.class);
        stackBuilder.addNextIntent(intent);

        PendingIntent resultPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(resultPendingIntent);

        NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);

        //android 8.0 后需要给 notification 设置一个 channelId
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ONE_ID,
                    CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_HIGH);
            notificationChannel.enableLights(true);
            notificationChannel.setLightColor(Color.RED);
            notificationChannel.setShowBadge(true);
            notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
            notificationManager.createNotificationChannel(notificationChannel);
            builder.setChannelId(CHANNEL_ONE_ID);
        }

        Notification notification = builder.build();
        notificationManager.notify(NOTIFY_ID, notification);
        startForeground(NOTIFY_ID, notification);
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

我们通过调用 startForeground 将服务设置成前台服务。同时也需要在 AndroidManifest.xml 中进行注册。

启动该服务:

Intent intent = new Intent();
intent.setClass(MainActivity.this, WeatherService.class);
  if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
    //android 8.0 以上通过 startForegroundService 启动 Service
    startForegroundService(intent);
  } else {
    startService(intent);
  }

运行效果:


补充知识点:Android进程优先级

Android操作系统尝试尽可能长时间保持应用的进程,但当可用内存很低时要移走一部分进程。哪些程序可以运行,哪些要被销毁?答案是:重要级别低的进程可能被淘汰。

按照重要性排列,一共可以分成5级:

1.前台运行进程
用户此时需要处理和显示的进程。符合下列条件任何一个,这个进程就被认为是前台运行进程:

销毁前台运行进程是系统万不得已的、最后的选择——当内存不够系统继续运行下去时,杀掉一些前台进程来保证能够响应用户的需求。

2.可见进程
能被用户看到,但不能根据根据用户的动作做出相应的反馈,

3.服务进程
服务进程是一个通过调用 startService() 方法启动的服务,并且不属于前两种情况。尽管服务进程没有直接被用户看到,但他们确实是用户所关心的,比如后台播放音乐或网络下载数据,所以系统保证他们的运行。

4.后台进程
一个后台进程就是非当前正在运行的 Activity(Activity 的 onStop() 方法已经被调用),他们不会对用户体验造成直接的影响,当没有足够内存来运行前台可见程序时,他们将会被终止。

通常,后台进程会有很多个在运行,LRU 最近使用程序列表来保证经常运行的 Activity 能最后一个被终止。

5.空进程
一个空进程没有运行任何可用应用程序,保留他们的唯一原因是为了设立一个缓存机制,来加快组件启动的时间。系统经常杀死这些内存来平衡系统的整个系统的资源,进程缓存和基本核心缓存之间的资源。

六、Android 系统级别的 Service

getSystemService(String name) 是 Android 很重要的一个方法,根据 NAME 来取得对应的 Object,然后转换成相应的服务对象,例如:

LayoutInflater layoutInflater =
                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

以下介绍系统相应的服务:

name 返回的对象 管理打开的窗口程序
WINDOW_SERVICE WindowManager 管理打开的窗口程序
LAYOUT_INFLATER_SERVICE LayoutInflater 取得xml里定义的view
ACTIVITY_SERVICE ActivityManager 管理应用程序的系统状态
POWER_SERVICE PowerManger 电源的服务
ALARM_SERVICE AlarmManager 闹钟的服务
NOTIFICATION_SERVICE NotificationManager 状态栏的服务
KEYGUARD_SERVICE KeyguardManager 键盘锁的服务
LOCATION_SERVICE LocationManager 位置的服务,如GPS
SEARCH_SERVICE SearchManager 搜索的服务
VIBRATOR_SERVICE Vibrator 手机震动的服务
CONNECTIVITY_SERVICE Connectivity 网络连接的服务
WIFI_SERVICE WifiManager Wi-Fi服务
TELEPHONY_SERVICE TeleponyManager 电话服务
SENSOR_SERVICE SensorManager 传感器服务

参考资料:

  1. 《Android开发进阶从小工到专家》何红辉
  2. Android进程优先级
上一篇下一篇

猜你喜欢

热点阅读