启动Service有如下两种方式。
- 通过Context的startService()方法:通过该方法启动Service,访问者与Service之间没有关联,即使访问者退出了,Service也仍然运行。
- 通过Context的bindService()方法:使用该方法启动Service,访问者与Service绑定在一起,访问者一旦退出,Service也就终止了。
从Android5.0开始,Google要求必须使用显式Intent启动Service组件。
每当Service被创建时会回调onCreate()方法,每次Service被startService()启动时都会回调onStartCommand()方法--多次启动一个已有的Service组件将不会再回调onCreate()方法,但每次启动时都会回调onStartCommand()方法。
当程序通过startService()和stopService()启动、关闭Service时,Service与访问者之间基本上不存在太多的关联,因此Service和访问者之间也无法进行通信信、交换数据。
如果Service和访问者之间需要进行方法调用或交换数据,则应该使用bindService()和unbindService()方法启动、关闭Service。
bindService方法的参数ServiceConnection对象的onServiceConnected()方法中有一个IBinder对象,该对象即可实现与被绑定Service之间的通信。
开发Service类时,该Service类必须提供一个IBinder onBind(Intent intent)方法,在绑定本地Service的情况下,onBind(Intent intent)方法所返回的IBinder对象将会传给ServiceConnection对象里onServiceConnected(ComponentName name, IBinder bind)方法的bind参数,这样访问者就可通过该IBinder对象与Service进行通信了。
IBinder对象相当于Service组件的内部钩子关联到绑定的Service组件,当其他程序组件绑定该Service时,Service将会把IBinder对象返回给其他程序组件,其他程序组件通过该IBinder对象即可与Service组件进行实时通信。
Service本身存在两个问题:
- Service不会专门启动一个单独的进程,Service与它所在应用位于同一个进程中。
- Service不是一条新的线程,它依赖于主线程,不应该在Service中直接处理耗时的任务。
IntentService
IntentService用来方便的处理一些耗时任务。
如果需要在Service中处理耗时任务,建议在Service中另外启动一条新线程来处理该耗时任务。如果直接在其他程序组件中启动子线程来处理耗时任务并不可靠,由于Activity可能会被用户退出,而BroadcastReceiver的生命周期本身就很短。可能出现的情况是:在子线程还没有结束的情况下,Activity已经被用户退出了,或者BroadcastReceiver已经结束了。在Activity已经退出、BroadcastReceiver已经结束的情况下,此时它们所在的进程就变成了空进程(没有任何活动组件的进程),系统需要内存时可能会优先终止该进程。如果宿主进程被终止,那么该进程内的所有子线程也会被中止,这样就可能导致子线程无法执行完成。
IntentService使用队列来管理请求Intent,每当客户端代码通过Intent请求启动IntentService时,IntentService会将该Intent加入队列中,然后开启一条新的worker线程来处理该Intent。对于异步的startService()请求,IntentService会按次序依次处理队列中的Intent,该线程保证同一时刻只处理一个Intent。由于IntentService使用新的worker线程处理Intent请求,因此IntentService不会阻塞主线程,所以IntentService自己就可以处理耗时任务。
IntentService具有如下特征:
- IntentService会创建单独的worker线程来处理所有的Intent请求。
- IntentService会创建单独的worker线程来处理onHandleIntent方法实现的代码,因此开发者无须处理多线程问题。
- 当所有请求处理完成后,IntentService会自动停止,开发者无须调用stopSelf()方法来停止该Service。
- 为Service的onBind()方法提供了默认实现,默认实现的onBind()方法返回null
- 为Service的onStartCommand()方法提供了默认实现,该实现会将请求Intent添加到队列中。
Foreground Service
前台服务是用户可以在Android系统的通知栏中看到的服务,即使它在后台执行,也会有一个通知提醒用户。
跨进程调用Service (AIDL Service)
在Android系统中,各应用程序都运行在自己的进程中,进程之间一般无法直接进行数据交换。为了实现跨进程通信(Interprocess Communication,简称IPC),Android提供了AIDL Service。AIDLService与传统技术Corba、Java中的RMI(远程方法调用)之间存在一定的相似之处。Android的远程Service调用与Java的RMI基本相似,都是先定义一个远程调用接口,然后为该接口提供一个实现类。
系统Service
Android系统本身提供了大量的系统Service,开发者只要在程序中调用Context的如下方法即可获
取这些系统Service。getSystemService(String name):根据Service名称来获取系统Service。
电话管理器(TelephonyManager)
TelephonyManager是一个管理手机通话状态、电话网络信息的的服务类,该类提供了大量的
getXxx()方法来获取电话网络的相关信息。在程序中获取TelephonyManager十分简单,只要调用如下代码即可:TelephonyManager tManager = (TelephonyManager)getSystemService (Context.TELEPHONY SERVICE);接下来就可以通过TelephonyManager获取相关信息或者进行相关操作了。通过TelephonyManager提供的一系列方法即可获取手机网络、SIM卡的相关信息。TelephonyManager除提供一系列的getXxx()方法来获取网络状态和sSIM卡信息之外,还提供了一个listen(PhoneStateListener listener,int events)方法来监听通话状态。
短信管理器(SmsManager)
SmsManager是Android提供的另一个非常常见的服务,SmsManager提供了一系列sendXxxMessage()方法用于发送短信,不过就现在实际应用来看,短信通常都是普通的文本内容,也就是调用sendTextMessage()方法进行发送即可。
PendingIntent是对Intent的包装,一般通过调用PendingIntent 的getActivity()、getService()、getBroadcastReceiver()静态方法来获取PendingIntent对象。与Intent对象不同的是,PendingIntent通常会传给其他应用组件从而由其他应用程序来执行PendingIntent所包装的"Intent"。
音频管理器(AudioManager)
在某些时候,程序需要管理系统音量,或者直接让系统静音,这就可借助于Android提供的AudioManager来实现。
震动器 Vibrate
振动是除视频、声音之外的另一种"多媒体",充分利用系统的振动器会带给用户更好的体验。系统获取Vibrator也是调用Context的getSystemService()方法即可,接下来就可调用Vibrator的方法来控制手机振动了。
手机闹钟服务(AlarmManager)
AlarmManager是一个全局定时器,允许在指定的时间执行任务。它可以精确到毫秒,适用于需要精确时间控制的任务,如闹钟应用。
从Android 6.0(API 23)开始,AlarmManager的精确度受到限制,例如在设备处于深度休眠状态时。对于需要高精度时间控制的任务,可以考虑使用setExact()方法(仅限API 23及以上),但这仍然不保证在所有情况下都能达到预期的精确度。
JobScheduler
JobScheduler是Android 5.0 (API 21) 引入的,仅适用于 Android 5.0 及以上版本,它用于在满足特定条件时执行后台任务,处理不需要精确到秒的任务(例如,当设备空闲时执行清理任务)。
WorkManager
WorkManager是jetpack库中后台任务管理组件,用于替代传统的Service进行后台任务处理。
WorkManager 使用 工作线程 来执行任务,不是在主线程(UI 线程)上执行。工作线程是专门用于执行后台任务的线程池,使用Executor执行。
主要特点:
- 可脱离应用执行:任务可以在应用退出后继续执行。
- 灵活触发:可以根据需要设置不同的触发器,如定时任务、网络状态变化等。
- 自动重试:WorkManager会自动管理任务的执行和重试,减少了开发者的工作量。
- 全版本兼容:根据设备 API 级别自动选择最佳执行方式(JobScheduler、AlarmManager 等),并适配 Doze 模式等电源管理特性。
安卓应用开发中 WorkManager 周期性任务未按预期执行问题详解_jobinfo.builder的setrequiresdeviceidle(true)不起作用-CSDN博客
Android Jetpack 系列(六)WorkManager 任务调度_android workmanager-CSDN博客
墓碑机制
Android 系统并无官方称为“墓碑机制”的功能;该术语源自 iOS 的后台管理策略,Android 对应的是 LMK(Low Memory Killer)与“暂停执行已缓存的应用”(开发者选项)。
“墓碑机制”通俗的讲就是伪多任务,当一个应用被切换到后台时,系统就会自动杀死,并把该应用切换后台时的状态自动记录,当再切换回来时呈现的是杀死应用时的瞬间状态,系统会把应用还原成被杀死前的状态,就好像它从来没被杀死过一样,通过系统动画进行视觉弥补,让察觉不到。
Doze模式
Doze模式可以称作“活埋机制”,Android(6.0、7.0)对于一些非社交的APP采用无视的方式,屏蔽网络,唤醒和定时任务也被忽略或推迟。这些APP可以继续在后台运行,但是啥也干不了。而且Doze模式启动条件也比较苛刻,首先需要一个小时的等待期,在屏幕关闭半小时后开始进行『大幅度运动监测』,接下来半小时内无大幅度运动才会进入Doze模式。Doze模式并不是墓碑机制,不会强制杀死应用,而是加强了后台唤醒的管制,在长时间待机状态的晚上才是Doze模式发挥作用的时候。
前台服务(ForegroundService)的线程在后台不执行
主要因系统省电策略(如 OriginOS)主动冻结后台进程,而非 Android 原生限制;需结合系统设置与开发适配双重解决。例如 vivo(OriginOS)默认“暂停未使用应用的活动”并限制后台高耗电行为,即使有前台服务,也可能被杀或线程休眠。需手动在 设置 → 电池 → 后台高耗电管理 中为应用开启“允许后台高耗电”,并在 自启动管理 中开启自启动,同时在多任务界面 锁定应用卡片,关闭“暂停未使用应用的活动”对该应用的限制。
Handler 和 Timer
- 简单的前台实时任务(如倒计时、动画控制)。
- 不需要持久化的短期任务。
优点
- 轻量化:适用于简单、短期、实时性较高的任务。
- 实时性好:可以精确控制任务的触发时间。
缺点
- 不适合后台任务:应用进程被杀后任务会终止。
- 不支持持久化:无法跨设备重启或进程重启保存任务状态。
- 电量优化不足:频繁执行任务可能增加电量消耗。
Timer
Android 中的 java.util.Timer 运行在后台子线程,非 UI 线程(主线程)。
- 线程机制:每个
Timer实例创建时会启动一个独立的后台线程(默认命名为 "Timer-0"),所有调度的TimerTask均在此线程中串行执行。 - UI 操作限制:严禁在
TimerTask的run()方法中直接更新 UI,否则会抛出异常或导致崩溃。 - 正确用法:若需更新界面,必须通过
Handler、runOnUiThread()或LiveData等机制将结果切换回主线程处理。 - 替代建议:由于
Timer存在单线程阻塞和异常终止风险,现代 Android 开发更推荐替代Timer使用Handler.postDelayed()、Coroutine或ScheduledThreadPoolExecutor。
监听网络状态
使用ConnectivityManager来监听网络状态的变化
AsyncTask
3.0 之前的 AsyncTask 可以同时有 5 个任务在执行,而 3.0 之后的 AsyncTask 同时只能有 1 个任务在执行。为什么升级之后可以同时执行的任务数反而变少了呢?这是因为更新后的AsyncTask变得更加灵活,如果不想使用默认的线程池,还可以自由地进行配置。比如使用如下的代码来启动任务:
Executor exec = new ThreadPoolExecutor(5, 20, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
new DownloadTask().executeOnExecutor(exec);
这样就可以使用自定义 Executor 来执行任务,而不是使用 SerialExecutor。上述代码的效果允许在同一时刻有 5 个Task同时执行,并且最多能够存储 20 个任务。

233

被折叠的 条评论
为什么被折叠?



