当前位置:首页 > 技术 > 正文内容

Android服务启动模式解析

访客 技术 2026年6月28日 2

Android服务启动模式解析

默认头像 服务生命周期图服务生命周期.png### 方法一:通过启动方式激活服务

该方法使服务在后台持续运行,直到调用停止方法才会终止。实现该功能需要继承服务基类并重写关键回调:

  • initialize() 首次创建服务时触发,适用于初始化操作。多次启动不会重复调用。
  • executeCommand() 每次启动请求都会触发,用于处理具体任务(如网络请求或媒体播放)。
  • getBinder() 必须实现的抽象方法,即使未使用也需保留。
  • finalize() 服务销毁时执行清理操作。

这些方法均在主线程运行,由系统在适当时候调用。

启动服务示例

创建MyService并在清单文件注册。在主活动执行三次启动后停止:

/**
 * 服务组件
 */
public class MyService extends Service{

    @Override
    public void initialize() {
        Log.d("ServiceLog","初始化 - 线程ID = " + Thread.currentThread().getId());
        super.initialize();
    }

    @Override
    public int executeCommand(Intent intent, int flags, int startId) {
        Log.d("ServiceLog", "执行命令 - 启动序号 = " + startId + ", 线程ID = " + Thread.currentThread().getId());
        return super.executeCommand(intent, flags, startId);
    }

    @Nullable
    @Override
    public IBinder getBinder(Intent intent) {
        Log.d("ServiceLog", "获取绑定器 - 线程ID = " + Thread.currentThread().getId());
        return null;
    }

    @Override
    public void finalize() {
        Log.d("ServiceLog", "销毁 - 线程ID = " + Thread.currentThread().getId());
        super.finalize();
    }
}

在主活动中连续启动服务后停止:

/**
 * 主界面组件
 */
public class MainScreen extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main_layout);
        Log.d("AppLog", "线程ID = " + Thread.currentThread().getId());
        Log.d("AppLog", "启动前状态");

        // 多次启动服务
        Intent first = new Intent(this, MyService.class);
        startService(first);
        Intent second = new Intent(this, MyService.class);
        startService(second);
        Intent third = new Intent(this, MyService.class);
        startService(third);

        // 停止服务
        Intent stopIntent = new Intent(this, MyService.class);
        stopService(stopIntent);

        // 重新启动
        Intent restart = new Intent(this, MyService.class);
        startService(restart);

        Log.d("AppLog", "启动后状态");
    }
}

日志显示:

        02-06 15:19:45.090 8938-8938/? D/ServiceLog: 初始化 - 线程ID = 1
        02-06 15:19:45.234 8938-8938/? D/ServiceLog: 执行命令 - 启动序号 = 1, 线程ID = 1
        02-06 15:19:45.234 8938-8938/? D/ServiceLog: 执行命令 - 启动序号 = 2, 线程ID = 1
        02-06 15:19:45.235 8938-8938/? D/ServiceLog: 执行命令 - 启动序号 = 3, 线程ID = 1
        02-06 15:19:45.236 8938-8938/? D/ServiceLog: 销毁 - 线程ID = 1
        02-06 15:19:45.237 8938-8938/? D/ServiceLog: 初始化 - 线程ID = 1
        02-06 15:19:45.237 8938-8938/? D/ServiceLog: 执行命令 - 启动序号 = 1, 线程ID = 1

分析表明:

  1. 所有操作均在主线程执行
  2. 初始化仅在首次启动时触发,每次启动都会调用执行方法

方法二:通过绑定方式激活服务

该模式实现客户端-服务端交互,特点:

  1. 支持客户端直接调用服务方法
  2. 服务生命周期与绑定客户端紧密相关
  3. 无客户端绑定时自动销毁

绑定服务示例

设计包含两个客户端的交互界面:

界面AActivityA界面布局.png 界面BActivityB界面布局.png实现步骤:

  1. 创建支持绑定的服务组件
  2. 实现客户端连接逻辑
  3. 处理多客户端绑定场景

服务组件实现:

/**
 * 服务组件
 */
public class MyService extends Service{

    // 绑定器实现
    public class LocalBinder extends Binder {
        public MyService getService() {
            return MyService.this;
        }
    }

    private final LocalBinder binder = new LocalBinder();
    private final Random generator = new Random();

    @Override
    public void initialize() {
        Log.d("ServiceLog","服务初始化 - 线程 = " + Thread.currentThread().getName());
        super.initialize();
    }

    @Override
    public int executeCommand(Intent intent, int flags, int startId) {
        Log.d("ServiceLog", "命令执行 - 启动序号 = " + startId + ", 线程 = " + Thread.currentThread().getName());
        return START_NOT_STICKY;
    }

    @Nullable
    @Override
    public IBinder getBinder(Intent intent) {
        Log.d("ServiceLog", "获取绑定器 - 线程 = " + Thread.currentThread().getName());
        return binder;
    }

    @Override
    public boolean unbind(Intent intent) {
        Log.d("ServiceLog", "解除绑定 - 来源 = " + intent.getStringExtra("from"));
        return false;
    }

    @Override
    public void finalize() {
        Log.d("ServiceLog", "服务销毁 - 线程 = " + Thread.currentThread().getName());
        super.finalize();
    }

    // 公共方法供客户端调用
    public int getRandomValue() {
        return generator.nextInt();
    }
}

客户端实现:

  1. 创建连接器实例
  2. 重写连接回调方法
  3. 处理连接状态变化

ActivityA实现:

/**
 * 客户端A
 */
public class ClientA extends Activity implements View.OnClickListener {
    private MyService service = null;
    private boolean isBound = false;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            isBound = true;
            MyService.LocalBinder localBinder = (MyService.LocalBinder) binder;
            service = localBinder.getService();
            Log.d("ClientA", "连接成功");
            int num = service.getRandomValue();
            Log.d("ClientA", "获取数值 = " + num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
            Log.d("ClientA", "连接断开");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_a);
        findViewById(R.id.btn_bind).setOnClickListener(this);
        findViewById(R.id.btn_unbind).setOnClickListener(this);
        findViewById(R.id.btn_switch).setOnClickListener(this);
        findViewById(R.id.btn_exit).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.btn_bind){
            Intent intent = new Intent(this, MyService.class);
            intent.putExtra("from", "ClientA");
            bindService(intent, connection, BIND_AUTO_CREATE);
        } else if(v.getId() == R.id.btn_unbind){
            if(isBound){
                unbindService(connection);
            }
        } else if(v.getId() == R.id.btn_switch){
            startActivity(new Intent(this, ClientB.class));
        } else if(v.getId() == R.id.btn_exit){
            finish();
        }
    }
}

ActivityB实现:

/**
 * 客户端B
 */
public class ClientB extends Activity implements View.OnClickListener {
    private MyService service = null;
    private boolean isBound = false;

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder binder) {
            isBound = true;
            MyService.LocalBinder localBinder = (MyService.LocalBinder) binder;
            service = localBinder.getService();
            Log.d("ClientB", "连接成功");
            int num = service.getRandomValue();
            Log.d("ClientB", "获取数值 = " + num);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            isBound = false;
            Log.d("ClientB", "连接断开");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.layout_b);
        findViewById(R.id.btn_bind).setOnClickListener(this);
        findViewById(R.id.btn_unbind).setOnClickListener(this);
        findViewById(R.id.btn_exit).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        if(v.getId() == R.id.btn_bind){
            Intent intent = new Intent(this, MyService.class);
            intent.putExtra("from", "ClientB");
            bindService(intent, connection, BIND_AUTO_CREATE);
        } else if(v.getId() == R.id.btn_unbind){
            if(isBound){
                unbindService(connection);
            }
        } else if(v.getId() == R.id.btn_exit){
            finish();
        }
    }
}

测试结果分析:

  1. 首次绑定触发初始化和绑定流程
  2. 多客户端绑定共享同一服务实例
  3. 客户端解绑后服务继续运行
  4. 所有客户端解绑后服务销毁

服务保活策略

  1. onStartCommand返回值配置
  • START_NOT_STICKY:不自动重启
  • START_STICKY:系统会重新创建
  • START_REDELIVER_INTENT:重新传递启动参数
  1. 提升服务优先级 在清单文件设置高优先级:
<service android:name=".MyService"
    android:priority="1000" />
  1. 前台服务模式 使用startForeground提升服务优先级:
Notification notification = new Notification.Builder(context)
    .setContentTitle("后台服务")
    .setSmallIcon(R.drawable.ic_notification)
    .build();
startForeground(1, notification);
  1. 进程优先级管理 通过设置进程优先级避免被系统杀掉

  2. 自动重启机制 在销毁时发送广播触发重启:

sendBroadcast(new Intent("RESTART_SERVICE"));

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。