Android 四大组件——Service(服务)【基础篇1】

目录

[一、什么是 Android 服务?](#一、什么是 Android 服务?)

[1. 官方定义](#1. 官方定义)

[2. 服务 vs 线程:关键区别](#2. 服务 vs 线程:关键区别)

[二、Service 的生命周期](#二、Service 的生命周期)

[1. 两种启动模式的生命周期](#1. 两种启动模式的生命周期)

[2. 相关方法详解](#2. 相关方法详解)

[3. StartService启动Service](#3. StartService启动Service)

[3.1 启动服务(Started Service)的生命周期:](#3.1 启动服务(Started Service)的生命周期:)

[3.2 onStartCommand详解:](#3.2 onStartCommand详解:)

参数解析:

[3.3 深入理解 onStartCommand() 的 flags 参数](#3.3 深入理解 onStartCommand() 的 flags 参数)

**START_FLAG_REDELIVERY(重新投递标志)**

**START_FLAG_RETRY(重试标志)**

[flags 的总结要点](#flags 的总结要点)

[3.4 关于 startId 的核心机制](#3.4 关于 startId 的核心机制)

[关键区别:stopSelf() vs stopSelf(startId)](#关键区别:stopSelf() vs stopSelf(startId))

[3.5 onStartCommand()的返回值详解](#3.5 onStartCommand()的返回值详解)

[4. BindService启动Service](#4. BindService启动Service)

[4.1 启动服务(Started Service)的生命周期:](#4.1 启动服务(Started Service)的生命周期:)

[4.2 在 Activity 中绑定服务](#4.2 在 Activity 中绑定服务)

[ServiceConnection 详细解析](#ServiceConnection 详细解析)

[int flags - 常用标志组合](#int flags - 常用标志组合)

[4.3 创建 Service 类](#4.3 创建 Service 类)

[5. 两种启动方式混合开发(Android 8之前版本)](#5. 两种启动方式混合开发(Android 8之前版本))

[5.1 生命周期:](#5.1 生命周期:)

场景1:先启动后绑定的完整流程

场景2:先绑定后启动的流程


一、什么是 Android 服务?

1. 官方定义

服务(Service)是Android四大组件之一,是一个在后台执行长时间运行操作 的应用组件,不提供用户界面。服务可由其他应用组件启动,即使用户切换到其他应用,服务仍可在后台运行。

核心特征:

  • 无用户界面:不像Activity,服务没有可视化界面

  • 后台运行:独立于UI,适合长时间操作

  • 组件级别:是Android组件,有独立生命周期

  • 优先级较高:比普通后台线程更不容易被系统杀死

2. 服务 vs 线程:关键区别

对比维度 Service(服务) Thread(线程)
本质 Android组件 Java执行单元
生命周期 由系统管理,复杂 由程序员控制
运行线程 默认在主线程 在独立线程
优先级 较高,不易被杀死 较低,容易被回收
通信方式 Intent、Binder、Broadcast等 Handler、回调等
用途场景 音乐播放、下载、位置跟踪 计算密集型任务

默认是在主线程运行 的,所以最好不要直接在服务中执行耗时操作。

java 复制代码
// ❌ 错误示范:在服务中直接执行耗时操作
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // 这会阻塞主线程,导致ANR!
    downloadLargeFile(); // 耗时操作
    return START_STICKY;
}

// ✅ 正确做法:在服务中启动工作线程
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    new Thread(() -> {
        downloadLargeFile(); // 在子线程执行
    }).start();
    return START_STICKY;
}

二、Service 的生命周期

1. 两种启动模式的生命周期

从上图的生命周期,我们可以知道,Android中使用Service的方式有两种:

  1. StartService()启动Service
  2. BindService()启动Service

还有一种,就是启动Service后,绑定Service!两者结合的混合开发模式。后面会讲解各自的优势。

2. 相关方法详解

  • onCreate() :当Service第一次被创建后立即回调该方法,该方法在整个生命周期 中只会调用一次!如果服务已运行,再次启动不会调用此方法
  • onDestory() :当Service被关闭时会回调该方法,该方法只会回调一次!
  • onStartCommand(intent,flag,startId) :早期版本是onStart(intent,startId), 当客户端调用startService(Intent)方法时会回调,可多次调用StartService方法, 但不会再创建新的Service对象,而是继续复用前面产生的Service对象,但会继续回调 onStartCommand()方法
  • IBinder onOnbind(intent) :该方法是Service都必须实现的方法,该方法会返回一个 IBinder对象,app通过该对象与Service组件进行通信!
  • onUnbind(intent):当该Service上绑定的所有客户端都断开时会回调该方法!
  • onRebind(Intent intent) :当之前已解绑的客户端重新绑定到服务时回调该方法(前提是 onUnbind() 返回了 true)
特性 BindService StartService
生命周期 绑定后创建,所有客户端解绑后销毁 调用 startService() 创建,stopService() 或 stopSelf() 销毁
通信方式 双向通信(通过 IBinder) 单向通信(通过 Intent)
使用场景 长时间交互、RPC调用 后台执行任务、无需交互

接下来结合生命周期详细讲解上面的方法。

3. StartService启动Service

3.1 启动服务(Started Service)的生命周期:

java 复制代码
Context.startService() → onCreate() → onStartCommand() 
→ 运行中 → stopSelf()/stopService() → onDestroy()

首次启动会创建一个Service实例,依次调用onCreate()和onStartCommand()方法,此时Service 进入运行状态,如果再次调用StartService启动Service,将不会再创建新的Service对象, 系统会直接复用前面创建的Service对象,调用它的onStartCommand()方法!

但这样的Service与它的调用者无必然的联系,就是说当调用者结束了自己的生命周期, 但是只要不调用stopService,那么Service还是会继续运行的!

无论启动了多少次Service,只需调用一次StopService即可停掉Service

3.2 onStartCommand详解:

多次调用StartService方法,会反复调用onStartCommand。所以可以通过传入不同的intent来让服务执行不同的业务逻辑

java 复制代码
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d(TAG, "onStartCommand: 启动服务,startId=" + startId);
    
    // 获取传递的数据
    String action = intent.getAction();
    String url = intent.getStringExtra("download_url");
    
    // 处理具体业务逻辑
    if ("DOWNLOAD".equals(action)) {
        startDownload(url, startId);
    }else if(....){
        .......其他的逻辑.......
    }
    
    // 返回值决定服务被杀死后的行为
    return START_REDELIVER_INTENT;
}
参数解析:
  • Intent intent启动服务时传递的Intent

  • int flags启动标志位

    • START_FLAG_REDELIVERY:之前已传递过此Intent

    • START_FLAG_RETRY:之前启动失败重试

  • int startId每次启动的唯一ID,用于stopSelf(startId)

3.3 深入理解 onStartCommand() 的 flags 参数

flags 参数在 onStartCommand() 中是一个提示信息,告诉服务本次启动的特殊情况

**START_FLAG_REDELIVERY(重新投递标志)**
java 复制代码
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // 检查是否是重新投递
    if ((flags & Service.START_FLAG_REDELIVERY) != 0) {
        Log.d(TAG, "这是之前未完成的任务重新投递");
        // 这里可以重新处理未完成的任务
        recoverUnfinishedTask();
    } else {
        Log.d(TAG, "这是新的启动请求");
        // 正常处理新任务
        processNewTask();
    }
    
    // 注意:只有 START_REDELIVER_INTENT 返回值才会触发重新投递
    return START_REDELIVER_INTENT;
}

触发场景:

  • 服务在 onStartCommand() 返回 START_REDELIVER_INTENT

  • 服务被系统杀死前,任务未完成(未调用 stopSelf(startId))

  • 系统重新创建服务并重新传递同一个 Intent

java 复制代码
典型流程:

1. Activity 启动服务传递 Intent A
2. 服务 onStartCommand() 返回 START_REDELIVER_INTENT
3. 服务开始处理任务(但未完成)
4. 系统内存不足杀死服务
5. 稍后系统重新创建服务
6. onStartCommand() 被调用,flags 包含 START_FLAG_REDELIVERY
7. 服务知道这是未完成的任务,可以恢复处理

实际应用:文件下载服务

java 复制代码
public class DownloadService extends Service {
    private Map<Integer, DownloadTask> runningTasks = new HashMap<>();
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String url = intent.getStringExtra("url");
        int fileId = intent.getIntExtra("file_id", -1);
        
        // 检查是否是重新投递
        boolean isRedelivery = (flags & Service.START_FLAG_REDELIVERY) != 0;
        
        if (isRedelivery) {
            Log.i(TAG, "重新投递下载任务: fileId=" + fileId);
            // 从数据库或缓存中恢复下载进度
            long downloadedBytes = loadDownloadProgress(fileId);
            resumeDownload(url, fileId, downloadedBytes, startId);
        } else {
            Log.i(TAG, "新的下载任务: fileId=" + fileId);
            // 开始新的下载
            startNewDownload(url, fileId, startId);
        }
        
        // 重要:必须返回 START_REDELIVER_INTENT 才能触发重新投递
        return START_REDELIVER_INTENT;
    }
    
    private void downloadComplete(int startId) {
        // 任务完成,调用 stopSelf(startId)
        // 这样即使服务被杀死,也不会重新投递这个任务
        stopSelf(startId);
        runningTasks.remove(startId);
    }
}
**START_FLAG_RETRY(重试标志)**
java 复制代码
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // 检查是否是重试
    if ((flags & Service.START_FLAG_RETRY) != 0) {
        Log.w(TAG, "上次启动失败,系统正在重试");
        // 这里可以记录重试次数,防止无限重试
        int retryCount = intent.getIntExtra("retry_count", 0);
        if (retryCount > 3) {
            Log.e(TAG, "重试次数超过3次,放弃任务");
            stopSelf(startId);
            return START_NOT_STICKY;
        }
        // 增加重试次数
        intent.putExtra("retry_count", retryCount + 1);
    }
    
    // 尝试执行任务
    try {
        performTask(intent, startId);
        return START_STICKY;
    } catch (Exception e) {
        Log.e(TAG, "任务执行失败,等待系统重试", e);
        // 返回 START_STICKY,系统会重试
        return START_STICKY;
    }
}

触发场景:

  • 服务 onStartCommand() 返回后立即崩溃

  • 服务启动过程中发生异常

  • 系统会尝试重新启动服务

java 复制代码
典型流程:

1. Activity 启动服务
2. 服务 onStartCommand() 被调用
3. 服务立即崩溃(未返回或抛出异常)
4. 系统检测到崩溃
5. 系统尝试重新启动服务
6. onStartCommand() 再次被调用,flags 包含 START_FLAG_RETRY
flags 的总结要点
标志位 触发条件 典型应用 返回值要求
START_FLAG_REDELIVERY 1. 服务返回 START_REDELIVER_INTENT 2. 任务未完成服务被杀死 3. 系统重新创建服务 1. 恢复未完成任务 2. 防止重复处理 3. 状态恢复 START_REDELIVER_INTENT
START_FLAG_RETRY 1. 服务启动立即崩溃 2. onStartCommand() 抛出异常 1. 崩溃恢复 2. 重试机制 3. 错误处理 通常是 START_STICKY

关键理解:

  1. flags 是只读信息,告诉你本次启动的特殊情况

  2. 需要配合返回值使用,特别是 START_FLAG_REDELIVERY

  3. 合理使用 stopSelf(startId) 可以避免不必要的重新投递

  4. 在实际开发中,大多数情况下不需要特别处理 flags,除非你实现了需要恢复状态的复杂任务

一句话总结flags 参数是系统给服务的"小纸条",告诉服务"这次启动有点特别,上次可能没完成或者失败了",让服务可以做出相应的处理策略。

3.4 关于 startId 的核心机制

startId 是每次调用 startService() 时系统生成的唯一递增ID,✅ 每次调用 startService() ,无论来自哪个上下文,都会获得唯一且递增的 startId。用于:

  1. 标识不同的启动请求

  2. 配合 stopSelf(startId) 精确停止服务

  3. 跟踪任务执行状态

当多个任务执行的时候,需要知道哪个任务是否完成。startId就起了很关键的作用。

java 复制代码
public class TaskService extends Service {
    private Map<Integer, TaskInfo> currentTasks = new ConcurrentHashMap<>();
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // 创建任务信息
        TaskInfo task = new TaskInfo(startId, intent, System.currentTimeMillis());
        currentTasks.put(startId, task);
        
        // 在子线程执行任务
        executor.execute(() -> {
            try {
                // 执行任务
                processTask(intent);
                
                // 任务完成,精确停止
                task.completed = true;
                stopSelf(startId);
                
            } catch (Exception e) {
                Log.e(TAG, "任务失败: " + startId, e);
                task.failed = true;
            } finally {
                currentTasks.remove(startId);
            }
        });
        
        return START_REDELIVER_INTENT;
    }
    
    // 精确停止服务
    public void stopSelf(int startId) {
        currentTasks.remove(startId);
        
        // 检查是否还有其他任务在运行
        if (currentTasks.isEmpty()) {
            // 没有任务了,真正停止服务
            super.stopSelf();
        } else {
            Log.i(TAG, "还有 " + currentTasks.size() + " 个任务在运行,不停止服务");
        }
    }
}

当服务处理多个并发请求时,需要区分不同的处理上下文。startId 作为每次 startService() 调用的唯一标识,配合 stopSelf(startId) 可以确保只有在某个特定请求处理完毕后,才停止对应的服务实例,而不会影响其他正在处理中的请求。

关键区别:stopSelf() vs stopSelf(startId)
方法 行为 使用场景
stopSelf() 立即停止服务 只有一个任务,或确定没有其他请求
stopSelf(startId) 只停止匹配这个 startId 的请求 服务可能同时处理多个请求

3.5 onStartCommand()的返回值详解

java 复制代码
// 1. START_STICKY(粘性)
// 服务被杀死后,系统会重新创建服务,但不会重新传递最后一个Intent
// 简单理解就是会执行onCreate() 但不会执行onStartCommand()
// 适合音乐播放、下载等需要自动恢复的服务
return START_STICKY;

// 2. START_NOT_STICKY(非粘性)
// 服务被杀死后,除非有新的Intent传递,否则不会重新创建
// 适合一次性任务,如处理即时消息
return START_NOT_STICKY;

// 3. START_REDELIVER_INTENT(重新传递)
// 服务被杀死后,系统会重新创建服务并重新传递最后一个Intent
// 适合必须完成的任务,如下载文件
return START_REDELIVER_INTENT;

// 4. START_STICKY_COMPATIBILITY(兼容性粘性)
// START_STICKY的兼容版本,不保证onStartCommand会被调用
return Service.START_STICKY_COMPATIBILITY;

对比表格:

返回值 服务被杀死后 重新传递Intent 适用场景
START_STICKY 重新创建 ❌ 不传递 音乐播放、后台运行
START_NOT_STICKY 不自动重建 ❌ 不传递 即时消息处理
START_REDELIVER_INTENT 重新创建 ✅ 重新传递 文件下载、必须完成的任务
START_STICKY_COMPATIBILITY 可能重建 ❌ 不传递 兼容旧版本

4. BindService启动Service

BindService(绑定服务)是一种客户端-服务器 接口,允许组件(如 Activity)与服务进行双向通信。服务在绑定时创建,所有客户端解绑时销毁。

4.1 启动服务(Started Service)的生命周期:

java 复制代码
Context.bindService() → onCreate() → onBind() → 客户端通过IBinder交互 
→ 所有客户端unbind() → onUnbind() → onDestroy()

4.2 在 Activity 中绑定服务

java 复制代码
public class MainActivity extends AppCompatActivity {
    
    private MyBindService myService;
    private boolean isBound = false;
    
    // ServiceConnection 用于监听绑定状态
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            // 绑定成功
            MyBindService.LocalBinder binder = (MyBindService.LocalBinder) service;
            myService = binder.getService();
            isBound = true;
            
            // 调用服务方法
            int data = myService.getData();
            myService.performAction("start");
        }
        
        @Override
        public void onServiceDisconnected(ComponentName name) {
            // 意外断开连接(如服务崩溃)
            isBound = false;
        }
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 绑定服务
        Intent intent = new Intent(this, MyBindService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 解绑服务
        if (isBound) {
            unbindService(connection);
            isBound = false;
        }
    }
}
java 复制代码
public abstract boolean bindService(
    Intent service,           // 要绑定的服务意图
    ServiceConnection conn,   // 绑定状态监听器
    int flags                // 绑定标志
);
ServiceConnection 详细解析
java 复制代码
private ServiceConnection connection = new ServiceConnection() {
    
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        // 成功绑定时的回调
        // name 就是绑定的服务的 ComponentName,包含包名和类名
        Log.d(TAG, "服务连接成功: " + name);
        
        // 获取服务实例(根据 Binder 类型不同)
        if (service instanceof MyService.LocalBinder) {
            MyService.LocalBinder binder = (MyService.LocalBinder) service;
            myService = binder.getService();
            isBound = true;
            
            // 初始化工作
            initializeAfterBinding();
        }
    }
    
    @Override
    public void onServiceDisconnected(ComponentName name) {
        // 非正常断开连接(服务崩溃或被杀死)
        Log.w(TAG, "服务异常断开: " + name);
        isBound = false;
        myService = null;
        
        // 重新连接逻辑
        scheduleReconnection();
    }
    
    @Override
    public void onBindingDied(ComponentName name) {
        // Android 8.0+,服务所在进程死亡
        Log.e(TAG, "绑定死亡: " + name);
        // 需要清理资源并重新绑定
        cleanupAndRebind();
    }
    
    @Override
    public void onNullBinding(ComponentName name) {
        // 服务返回 null(服务端 onBind() 返回 null)
        Log.e(TAG, "服务返回 null 绑定: " + name);
        // 可能是服务配置错误
    }
};

如果Service所在的宿主由于异常终止或者其他原因终止,导致Service与访问者间断开 连接时调用onServiceDisconnected (CompanentName)方法。注意: 主动通过unBindService() 方法断开并不会调用上述方法!

int flags - 常用标志组合
java 复制代码
// 组合1:标准绑定 + 自动创建
int flags = Context.BIND_AUTO_CREATE;

// 组合2:重要服务(提高优先级)
flags = Context.BIND_AUTO_CREATE | 
        Context.BIND_IMPORTANT;

// 组合3:前台服务绑定
flags = Context.BIND_AUTO_CREATE |
        Context.BIND_FOREGROUND_SERVICE;

// 组合4:调试模式
flags = Context.BIND_AUTO_CREATE |
        Context.BIND_DEBUG_UNBIND |
        Context.BIND_ALLOW_OOM_MANAGEMENT;
标志组合 优先级 内存不足时 特点
BIND_AUTO_CREATE 默认 可能被杀 标准模式
+ BIND_IMPORTANT 较高 较难被杀 提升进程优先级
+ BIND_FOREGROUND_SERVICE 前台级 很难被杀 前台服务优先级
+ BIND_ALLOW_OOM_MANAGEMENT 较低 更容易被杀 允许系统回收
标志 说明
BIND_AUTO_CREATE 1 服务不存在时自动创建
BIND_DEBUG_UNBIND 2 调试解绑问题,会减慢解绑速度
BIND_NOT_FOREGROUND 4 不将服务提升到前台优先级
BIND_ABOVE_CLIENT 8 服务优先级高于客户端
BIND_ALLOW_OOM_MANAGEMENT 16 允许系统在内存不足时杀死服务
BIND_WAIVE_PRIORITY 32 放弃优先级提升
BIND_IMPORTANT 64 重要绑定(提高优先级)
BIND_ADJUST_WITH_ACTIVITY 128 根据Activity状态调整优先级
BIND_FOREGROUND_SERVICE 4096 绑定到前台服务
BIND_FOREGROUND_SERVICE_WHILE_AWAKE 8192 设备唤醒时绑定到前台服务
BIND_INCLUDE_CAPABILITIES 4096 包含功能信息

4.3 创建 Service 类

java 复制代码
public class MyBindService extends Service {
    
    private final IBinder binder = new LocalBinder();
    
    // 自定义 Binder 类
    public class LocalBinder extends Binder {
        // 可以定义各种提供外界交互的方法
        // 下面是提供方法的两种方式
        MyBindService getService() {
            // 返回自身,以供外界调用public方法
            return MyBindService.this;
        }

        // 直接提供一个方法
        public int getCount()  
        {  
            return 11;  
        }  
    }
    
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }
    
    @Override
    public boolean onUnbind(Intent intent) {
        // 所有客户端解绑时调用
        // 返回父类的值 默认返回false(根据父类的实际情况而定)
        return super.onUnbind(intent);
    }

    @Override
    public void onRebind(Intent intent) {
        // onRebind 通常用于:
        // 1. 恢复连接状态
        // 2. 重新注册监听器
        // 3. 恢复数据同步
        
        // 调用父类实现
        super.onRebind(intent);
    }
    
    // 服务提供的公共方法
    public int getData() {
        return 42;
    }
    
    public void performAction(String action) {
        // 执行具体操作
    }
}

如果我们再绑定后直接关掉Activity的话会报错, 然后会自动调用onUnbind和onDestory方法!

onRebind() 是 Android Service 生命周期中的一个特殊回调方法。它并不是每次绑定服务时都会调用,而是在特定的"重新绑定"场景下触发。

onRebind() 的调用需要满足 四个严格条件

  1. 曾经有客户端绑定过服务:服务至少被绑定过一次

  2. 所有客户端都已解绑:当前没有任何客户端绑定到该服务

  3. 上次解绑时返回了 true:在 onUnbind() 方法中明确返回了 true

  4. 有新的客户端重新绑定:在服务未被销毁的情况下,又有新的客户端请求绑定

如果这四个条件同时满足,系统就会调用 onRebind(),而不是通常的 onCreate() → onBind() 流程。

java 复制代码
首次绑定:
onCreate() → onBind()

客户端解绑:
onUnbind() → (如果返回true,服务保持运行,不销毁)

重新绑定:
onRebind()  ← 直接调用,不经过 onCreate()

最终销毁:
(当服务真正需要销毁时) → onDestroy()

5. 两种启动方式混合开发(Android 8之前版本)

混合模式服务是一种既可以启动(start) 又可以绑定(bind) 的服务。在这种模式下,服务的生命周期会变得更复杂,需要同时满足两种模式的要求才会被销毁

5.1 生命周期:

复制代码
混合模式(同时启动和绑定):
startService() → onCreate() → onStartCommand()
           ↓
       bindService() → onBind()
           ↓
       运行中(既有启动又有绑定)
           ↓
       unbindService() → onUnbind()  // 服务继续运行
           ↓
       stopService()/stopSelf() → onDestroy()
  • 启动服务 :通过startService()启动,即使Activity销毁,服务仍可继续运行(Android 8之前)

  • 绑定服务 :通过bindService()绑定,可以与Activity进行通信

  • 生命周期 :服务只有在既没有绑定也没有启动时才会被销毁

场景1:先启动后绑定的完整流程
java 复制代码
1. startService()           → onCreate() → onStartCommand()
2. bindService()            → onBind()
3. unbindService()          → onUnbind()    // 服务继续运行
4. stopService()/stopSelf() → onDestroy()
场景2:先绑定后启动的流程
java 复制代码
1. bindService()            → onCreate() → onBind()
2. startService()           → onStartCommand()
3. unbindService()          → onUnbind()    // 服务继续运行
4. stopService()/stopSelf() → onDestroy()

对于 Android 8+(API Level 26+),混合模式服务的行为有很大变化 ,特别是对于后台服务的限制。这个在后面章节会详细讲解。

相关推荐
峥嵘life2 小时前
Android16 EDLA 认证测试BTS过程介绍
android·java·linux
茶憶3 小时前
UniApp 安卓端实现文件的生成,写入,获取文件大小以及压缩功能
android·javascript·vue.js·uni-app
2501_915921433 小时前
uni-app 的 iOS 打包与上架流程,多工具协作
android·ios·小程序·uni-app·cocoa·iphone·webview
Lei活在当下10 小时前
【Perfetto从入门到精通】4.使用 heapprofd 工具采样追踪 Java/Native 内存分配
android·性能优化·架构
alexhilton11 小时前
学会在Jetpack Compose中加载Lottie动画资源
android·kotlin·android jetpack
summerkissyou198712 小时前
Android-Camera-为啥不移到packages/module
android·相机
liang_jy12 小时前
Android UID
android·面试
nono牛15 小时前
安卓/MTK平台日志关键词详解
android
TimeFine16 小时前
Android AI解放生产力(四)实战:解放绘制UI的繁琐工作
android