【Android】四大组件Service

0 阅读5分钟

Service基本概念

Android Service 是一种可以在后台执行长时间运行操作而没有用户界面的应用组件。服务可以由其他应用组件启动(如Activity),一旦启动将在后台一直运行,即使启动服务的组件(Activity)已销毁也不受影响。此外,组件可以绑定到服务,以与之进行交互,甚至是执行进程间通信(IPC)。

Service形式

在 Android 中,Service 主要有三种形式,每种形式适用于不同的场景。

1. Started Service(启动式服务)

  • 特点
    • 通过startService()启动,独立运行,与启动组件无直接关联。
    • 需手动调用stopService()stopSelf()终止。
    • 适合执行独立任务(如文件下载、音乐播放)。
  • 生命周期
onCreate() → onStartCommand() → [运行中] → onDestroy()
  • 关键方法

    • onCreate()
      Service 首次创建时调用(类似 Activity 的 onCreate),用于初始化资源(如线程池、广播接收器)。

    • onStartCommand(Intent intent, int flags, int startId)
      每次调用startService()时触发,传入启动 Intent。返回值决定 Service 被系统终止后的重启策略:

      • START_STICKY:重启 Service,intent为 null(适用于媒体播放)。
      • START_NOT_STICKY:不重启(适用于一次性任务,如文件下载)。
      • START_REDELIVER_INTENT:重启并重新传递最后一个intent
    • onDestroy()
      Service 销毁前调用,需释放资源(如停止线程、注销广播接收器)。

  • 代码示例

public class MyService extends Service {

    private volatile boolean isRunning = true;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0; i < 10; i++){
                    if(!isRunning){
                        break;
                    }
                    Log.d("MyService", "Service is running: " + i);
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }

                }
                // 打印完成后停止服务
                stopSelf();
            }
        }).start();
        return START_STICKY;
    }

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

    @Override
    public void onDestroy() {
        super.onDestroy();
        isRunning = false;
        Log.d("MyService", "Service is destroyed");
    }
}

image.png

2. Bound Service(绑定式服务)

  • 特点

    • 通过bindService()绑定到组件(如 Activity),允许组件与 Service 交互。
    • 多个组件可同时绑定,最后一个组件解绑时 Service 销毁。
    • 适合提供跨组件功能(如获取系统服务、数据共享)。
  • 生命周期

onCreate() → onBind() → [绑定中] → onUnbind() → onDestroy()
  • 关键方法
    • onBind(Intent intent)
      首次绑定时调用,返回IBinder接口供组件通信。
    • onUnbind(Intent intent)
      最后一个组件解绑时触发,默认返回false。若返回true,下次绑定时会触发onRebind()
    • onRebind(Intent intent)
      onUnbind()返回true后,再次绑定时调用。
  • 代码示例
public class MyBoundService extends Service {

    private final IBinder binder = new MyLocalBinder();
    private int count = 0;

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


    public class MyLocalBinder extends Binder {
        MyBoundService getService() {
            return MyBoundService.this;
        }
    }

    public void incrementCount() {

        count++;
        Log.d("MyBoundService", "Current count: " + count);
    }

    public int getCount() {
        return count;
    }
}
public class MainActivity extends AppCompatActivity {
    private MyBoundService myBoundService;
    private boolean isServiceBound = false;

    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            MyBoundService.MyLocalBinder binder = (MyBoundService.MyLocalBinder) service;
            myBoundService = binder.getService();
            isServiceBound = true;
            // 显示当前计数
            TextView countTextView = findViewById(R.id.count_text_view);
            countTextView.setText("Count: " + myBoundService.getCount());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isServiceBound = false;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button incrementButton = findViewById(R.id.increment_button);
        Button unbindButton = findViewById(R.id.unbind_button);
        TextView countTextView = findViewById(R.id.count_text_view);

        incrementButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (isServiceBound) {
                    myBoundService.incrementCount();
                    countTextView.setText("Count: " + myBoundService.getCount());
                }
            }
        });

        unbindButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(serviceConnection);
                isServiceBound = false;
            }
        });
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, MyBoundService.class);
        bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (isServiceBound) {
            unbindService(serviceConnection);
            isServiceBound = false;
        }
    }
}

3. Foreground Service(前台服务)

  • 特点

    • 执行用户可感知的任务,必须显示通知(如音乐播放器、导航)。
    • 优先级高,系统不易回收,适合持续运行的任务。
  • 启动步骤

    1. 创建通知渠道(Android 8.0+)。
    2. 构建通知并调用startForeground(NOTIFICATION_ID, notification)

4. 混合模式(Started + Bound)

  • 特点

    • 同时支持startService()bindService()
    • 需同时管理两种生命周期,适合既需要独立运行又需要交互的场景。
  • 典型场景

    • 启动服务执行后台任务,同时允许 Activity 绑定获取进度。

5. 总结对比

类型启动方式生命周期通信方式典型场景
Started ServicestartService()独立运行,需手动停止广播、回调音乐播放、文件下载
Bound ServicebindService()依赖绑定组件IBinder接口数据共享、系统服务代理
Foreground ServicestartForeground()高优先级,必须显示通知同 Started/Bound导航、实时位置跟踪

IntentService

IntentService 是 Android 提供的一个抽象类,继承自 Service,用于简化异步任务处理。它在单独的工作线程中执行任务,并在所有请求处理完成后自动停止,适合处理一次性的后台任务(如网络请求、文件操作)。

  1. 自动创建工作线程:所有 onHandleIntent() 中的代码在后台线程执行,避免 ANR。
  2. 任务队列处理:通过 HandlerThread 和 Looper 实现任务排队,按顺序执行。
  3. 自动停止服务:所有任务完成后,IntentService 会自动调用 stopSelf()
  4. 简化的生命周期:只需实现 onHandleIntent(),无需手动管理线程和停止逻辑。

1. 基本用法

创建子类并实现 onHandleIntent()


public class MyIntentService extends IntentService {

    public MyIntentService() {
        super("MyIntentService"); // 工作线程名称,用于调试
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        // 在后台线程执行耗时任务
        if (intent != null) {
            String action = intent.getAction();
            if ("ACTION_DOWNLOAD".equals(action)) {
                downloadFile(intent.getStringExtra("url"));
            }
        }
    }

    private void downloadFile(String url) {
        // 文件下载逻辑(已在后台线程,无需额外线程)
    }
}

在 Manifest 中注册:

<service android:name=".MyIntentService" 
    android:enabled="true" 
    android:exported="false"/>

启动服务

Intent intent = new Intent(this, MyIntentService.class); 
intent.setAction("ACTION_DOWNLOAD"); 
intent.putExtra("url", "https://example.com/file.zip"); 
startService(intent);

2. 核心实现

  1. 工作线程与消息循环
    IntentService 在 onCreate() 中创建 HandlerThread 和 ServiceHandler,用于处理任务:

    @Override
    public void onCreate() {
        super.onCreate();
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        mServiceLooper = thread.getLooper();
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
    
  2. 任务处理流程

    • startService(intent) 触发 onStartCommand(),将 intent 封装为消息发送到工作线程。
    • 工作线程通过 ServiceHandler 接收消息,调用 onHandleIntent() 处理任务。
    • 所有任务完成后,自动调用 stopSelf(msg.arg1) 停止服务。
  3. 任务队列
    多个 intent 会按顺序在工作线程执行,同一时间只处理一个任务。

3. 总结对比

特性IntentService普通 Service
线程环境自动在后台线程执行任务默认在主线程,需手动创建子线程
任务处理按顺序处理多个任务需手动管理多线程并发
自动停止任务完成后自动停止需手动调用 stopSelf() 或 stopService()
适用场景独立、顺序执行的后台任务(如文件下载)持续运行的服务(如音乐播放)
  1. 不支持并发IntentService 同一时间只处理一个任务,若需并发,可使用 Service + ExecutorService

  2. 无法直接通信:任务执行结果无法直接返回给 Activity,需通过广播或回调。

    java

    // 在 onHandleIntent() 中发送广播
    Intent resultIntent = new Intent("DOWNLOAD_COMPLETED");
    resultIntent.putExtra("result", "success");
    sendBroadcast(resultIntent);
    
  3. 已过时(Android 8.0+) :推荐使用 WorkManager 替代,支持任务调度、重试和电量优化。