Android语言基础教程(232)Android创建Started Service之继承Service类:你的Android应用里,藏着一个“永生”的打工人?——深度解剖Service类的正确养成方

  • 时间:2025-11-20 20:41 作者: 来源: 阅读:0
  • 扫一扫,手机访问
摘要:嘿,各位Android开发者们,有没有遇到过这种场景?你精心设计的App,用户听着你App里的音乐,切出去回个微信,结果音乐戛然而止…… 那一刻,用户想卸载App的心都有了吧? 别慌,这往往是因为你没请对“后台打工人”——今天的主角:Started Service。 想象一下,你的App是一个公司: Activity:就是公司的前台和接待室,光鲜亮丽,直接跟用户(顾客)打交道。但用户一走,前

嘿,各位Android开发者们,有没有遇到过这种场景?你精心设计的App,用户听着你App里的音乐,切出去回个微信,结果音乐戛然而止…… 那一刻,用户想卸载App的心都有了吧?

别慌,这往往是因为你没请对“后台打工人”——今天的主角:Started Service

想象一下,你的App是一个公司:

Activity:就是公司的前台和接待室,光鲜亮丽,直接跟用户(顾客)打交道。但用户一走,前台可能就下班了。Service:则是公司里默默无闻的技术部、后勤部。他们不需要直接面对用户,但公司核心的业务逻辑(比如播放音乐、下载文件、同步数据)全靠他们支撑。即使用户离开了App(前台),这些部门依然可以继续加班干活。

Started Service,就是一种“一次吩咐,长期干活”的打工人。你(Activity)给它一个指令,它就开始工作,即使你离开了,它也能持续运行,直到任务完成或你明确让它下班。

今天,咱们就抛开枯燥的官方文档,用一场“人力资源部”的视角,深度分析如何“招聘”并“管理”好这位名叫 “继承Service类” 的王牌员工。

第一幕:招聘启事——认识Service类

首先,想招聘,得知道这个岗位的基本要求。在Android世界里,你想创建一个Started Service,继承 android.app.Service 类是你的不二法门

这就像你想招个程序员,他必须会写代码一样基础且重要。

这个 Service 类本身是个“抽象”的领导,它定义了一些基本行为规范(方法),但具体怎么干活,需要你这个“老板”来重写。

它的核心生命周期方法,就是这位打工人的“工作流程”:

onCreate(): 员工入职培训。 当Service第一次被创建时,系统会自动调用。这只会在它“生命”的开始发生一次。适合做什么:初始化一些耗时的资源,比如开启线程、初始化音乐播放器、绑定数据库连接等。就像给新员工配电脑、开通账号。 onStartCommand(Intent intent, int flags, int startId): 接到具体工作指令。 这是Started Service的灵魂所在! 每次你的Activity(或其他组件)通过 startService(intent) 方法启动这个Service时,这个方法都会被调用。参数解读 Intent intent: 老板派活时给的“工作单”,里面可以携带数据,比如“要下载的文件URL”、“要播放的音乐ID”。 int flags: 系统给的“派活标志”,通常不用太关心。 int startId: 本次启动的唯一ID。就像一个工单号,如果你连续派了多个活,可以用这个ID来区分和管理。 这个方法的返回值,决定了这位打工人的“抗压能力”和“工作态度”! 我们稍后重点讲。 onDestroy(): 员工离职清算。 当Service不再被使用并被销毁时调用。可能是你(老板)主动调用 stopSelf() stopService(Intent),也可能是系统在资源极度紧张时“强制裁员”。适合做什么:释放所有资源,比如停止音乐播放、关闭网络连接、终止线程等。避免造成内存泄漏,做个有素质的前雇主。
第二幕:核心技能培训——读懂onStartCommand的返回值

刚才说了, onStartCommand 的返回值是重中之重。它告诉系统:“如果我这个打工人不幸被系统‘杀掉’了,系统你后面应该怎么处理他?”

它主要有三个选择,对应三种“工作态度”:

START_NOT_STICKY (非粘性员工)态度:“如果系统你把我干掉了,除非老板你重新明确叫我,否则我就不回来了。”场景:适用于那些“中断了也无所谓”的任务。比如定时从网络拉取一些非关键性的新闻资讯。如果中途系统资源不足杀了Service,那就等下次App需要时再启动就好。这是最常用、最省心的选项。 START_STICKY (粘性员工)态度:“系统你杀了我?没关系,等系统资源够了,你得主动把我重新‘复活’!不过,之前老板给我的那个工作单(Intent)我忘了,你得给我个空的。”场景:适用于需要长期运行,但不依赖具体指令的Service,比如后台音乐播放器。系统杀掉后,音乐停了,但资源足够时Service会被重新创建,虽然不记得之前具体播哪首,但App的界面可以重新发指令让它播放。 START_REDELIVER_INTENT (负责任粘性员工)态度:“系统你杀了我?不仅要把我复活,还得把之前没干完的那个活(Intent)原封不动地还给我,我得继续干!”场景:适用于绝对不能中断的任务,比如文件下载。系统杀掉后,一有机会就会重新创建Service,并且把上次那个包含下载URL的Intent再传给它,确保下载任务能继续或重新开始。

选择建议:除非你的任务真的非常重要(如文件下载),否则优先使用 START_NOT_STICKY,做一个不給系统添乱的好公民。

第三幕:实战!打造一个“摸鱼”提醒打工人

光说不练假把式。现在,我们来亲手创建一个Started Service,它的功能很“实用”:在后台默默运行,每隔几秒就在Logcat里打印一条“摸鱼提醒”,提醒你该起来活动了!

步骤一:创建MyBackgroundService类



// MyBackgroundService.java
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
 
public class MyBackgroundService extends Service {
 
    private static final String TAG = "MyBackgroundService";
    private boolean isRunning = false;
    private Thread backgroundThread;
 
    @Override
    public void onCreate() {
        super.onCreate();
        Log.d(TAG, "打工人已入职,开始摸鱼监测任务!");
        isRunning = true;
    }
 
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "老板发来指令:开始摸鱼监测!工单号:" + startId);
 
        // 在后台开启一个线程执行任务,避免阻塞主线程(ANR!)
        if (backgroundThread == null) {
            backgroundThread = new Thread(new Runnable() {
                @Override
                public void run() {
                    int count = 0;
                    while (isRunning) {
                        try {
                            // 每隔3秒打印一次日志
                            Thread.sleep(3000);
                            count++;
                            Log.d(TAG, "【摸鱼提醒 #" + count + "】 你已经盯着屏幕3秒了,起来喝口水,扭扭脖子吧!");
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            break; // 如果线程被中断,就退出循环
                        }
                    }
                    Log.d(TAG, "打工人收到下班信号,摸鱼监测任务结束。");
                }
            });
            backgroundThread.start(); // 启动线程
        }
 
        // 我们选择当Service被系统杀死后,不自动重新启动(非粘性)
        return START_NOT_STICKY;
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "老板辞退了打工人,开始办理离职手续...");
        isRunning = false; // 通知线程停止循环
 
        // 中断线程,确保资源释放
        if (backgroundThread != null) {
            backgroundThread.interrupt();
            backgroundThread = null;
        }
    }
 
    // Started Service可以不需要绑定,所以这个返回null即可
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

代码深度解析:

isRunning 标志位:这是一个非常重要的“开关”。我们通过它来控制后台线程的循环。在 onDestroy() 时将其设为 false,线程就会优雅地结束,而不是被暴力中断。开新线程!开新线程!开新线程!:重要的事情说三遍。所有耗时操作绝对不能 onStartCommand 的主线程中直接进行!否则必然导致ANR(Application Not Responding),你的App会卡死并被系统强制关闭。我们这里用 Thread 是最简单的演示,在实际项目中,你可能会使用 HandlerThread ExecutorService Kotlin协程 等更优雅的方式。 onBind 返回null:因为我们创建的是纯粹的Started Service,不需要与Activity绑定通信,所以直接返回 null

步骤二:在AndroidManifest.xml中声明你的Service

就像新员工入职需要在HR系统里登记一样,Service也必须在清单文件中注册。



<!-- 在 <application> 标签内添加 -->
<service
    android:name=".MyBackgroundService"
    android:enabled="true"
    android:exported="false" />
exported="false" 表示这个Service只供自己的App内部使用,其他App不能来启动它,更安全。

步骤三:在Activity中启动和停止Service

现在,老板(Activity)可以给打工人(Service)派活和叫停了。



// MainActivity.java
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
 
public class MainActivity extends AppCompatActivity {
 
    private Button startButton, stopButton;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        startButton = findViewById(R.id.btn_start_service);
        stopButton = findViewById(R.id.btn_stop_service);
 
        startButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 创建Intent,明确指定要启动哪个Service
                Intent startIntent = new Intent(MainActivity.this, MyBackgroundService.class);
                // 可以携带数据
                startIntent.putExtra("EXTRA_DATA", "来自前台的指令");
                // 启动Service!
                startService(startIntent);
            }
        });
 
        stopButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 停止Service
                Intent stopIntent = new Intent(MainActivity.this, MyBackgroundService.class);
                stopService(stopIntent);
            }
        });
    }
}

对应的布局文件 activity_main.xml 很简单,就是两个按钮。

最终幕:运行与思考

现在,运行你的App吧!点击“启动服务”后,即使你退出App回到桌面,打开Logcat,你依然能看到每隔3秒就出现的“摸鱼提醒”。这位打工人正在后台兢兢业业地工作着。

但是!请冷静思考一下:

这个“打工人”虽然能干,但它有点“耗电”。在Android O(8.0)及之后的版本中,Google为了控制后台耗电,对后台Service的限制越来越严格。一个App在后台随意启动Service的行为会被系统无情地限制。

所以,在现代Android开发中,对于需要长期在后台执行的任务,我们有了更优的替代方案:

JobIntentService / JobService: 结合JobScheduler,让系统在合适的时机(比如充电且连接Wi-Fi时)帮你执行任务。 WorkManager: 这是目前官方推荐的、用于处理延迟性和可保证执行的后台任务的终极解决方案。它能兼容所有API级别,自动处理系统的后台限制。

结论:

继承 Service 类来创建Started Service,是Android后台开发的基石。它让你理解了后台任务的基本生命周期和运行机制。虽然在某些场景下已被更先进的组件替代,但这份“管理打工人”的核心思想是相通的。

先把这位“原始”的打工人摸透,你才能更好地驾驭 WorkManager 这样的“智能机器人管家”。现在,你不仅学会了如何创建它,更深度理解了它的工作方式和现代开发的演进方向。快去你的App里,试试培养一个得力的后台助手吧!

  • 全部评论(0)
手机二维码手机访问领取大礼包
返回顶部