RxJava——FlowableProcessor详解

FlowableProcessor详解

一、概述

核心概念:

FlowableProcessor​ 是支持背压的 Subject,它同时实现了:

  • Processor<T, R> 接口(Reactive Streams 规范)
  • Flowable 接口(可以被订阅)
  • FlowableSubscriber 接口(可以订阅其他Flowable)

继承关系:

java 复制代码
// 核心继承关系
public abstract class FlowableProcessor<T> 
    extends Flowable<T> 
    implements Processor<T, T>, FlowableSubscriber<T> {
    
    // 核心方法
    public abstract boolean hasSubscribers();
    public abstract Throwable getThrowable();
    public abstract boolean hasThrowable();
    public abstract boolean hasComplete();
    public abstract boolean hasSubscribers();
}

与Subject对比:

特性 FlowableProcessor Subject
背压支持 ✅ 完全支持 ❌ 不支持(Observable版本)
Reactive Streams兼容 ✅ Processor接口 ❌ 不兼容
订阅者数量限制 可配置 无限制
缓冲区策略 多种策略 无缓冲区
性能开销 较高 较低

二、PublishProcessor(实时发布处理器)

2.1、核心特性

  • 类似 PublishSubject,但支持背压
  • 只向当前订阅者发射数据
  • 不缓存任何数据
  • 支持背压策略
java 复制代码
@Test
public void testPublishProcessor() {
    //基本用法
    PublishProcessor<Integer> processor = PublishProcessor.create();

    //生产者
    Flowable.range(1, 1000)
            .subscribe(processor);
    //消费者1
    processor.onBackpressureBuffer(100)//缓冲区大小
            .subscribe(
                    data -> System.out.println("Consumer 1:" + data),
                    error -> System.out.println("Error 1:" + error),
                    () -> System.out.println("Completed 1")
            );

    //消费者2(稍后订阅,收不到之前的数据)
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    processor.subscribe(
            data -> System.out.println("Consumer 2:" + data),
            error -> System.out.println("Error 2:" + error),
            () -> System.out.println("Completed 2")
    );
}

背压处理示例:

java 复制代码
public class PublishProcessorBackpressure {
    
    public static void demoBackpressure() {
        PublishProcessor<Integer> processor = PublishProcessor.create();
        
        // 快速生产者
        Flowable.interval(10, TimeUnit.MILLISECONDS)
            .take(1000)
            .map(Long::intValue)
            .subscribe(processor);
        
        // 慢速消费者
        processor
            .onBackpressureBuffer(
                50,                     // 缓冲区大小
                () -> System.out.println("Buffer overflow"),
                BackpressureOverflowStrategy.DROP_OLDEST
            )
            .observeOn(Schedulers.io(), false, 16)  // 缓冲区16
            .subscribe(
                data -> {
                    // 慢速处理
                    Thread.sleep(100);
                    System.out.println("Processed: " + data);
                },
                error -> System.err.println("Error: " + error)
            );
    }
    
    // 使用不同的背压策略
    public static void differentStrategies() {
        PublishProcessor<Integer> processor = PublishProcessor.create();
        
        // 策略1: BUFFER - 无限缓冲(可能OOM)
        processor.onBackpressureBuffer();
        
        // 策略2: DROP - 丢弃溢出数据
        processor.onBackpressureDrop(dropped -> 
            System.out.println("Dropped: " + dropped)
        );
        
        // 策略3: LATEST - 保留最新数据
        processor.onBackpressureLatest();
        
        // 策略4: ERROR - 缓冲区满时抛异常
        processor.onBackpressureBuffer(100, 
            () -> {}, 
            BackpressureOverflowStrategy.ERROR
        );
    }
}

2.2、使用场景

java 复制代码
// 1. 实时数据流处理
public class RealTimeDataProcessor {
    private final PublishProcessor<SensorData> dataProcessor = PublishProcessor.create();
    private final CompositeDisposable disposables = new CompositeDisposable();
    
    public RealTimeDataProcessor() {
        // 设置数据处理管道
        disposables.add(
            dataProcessor
                .onBackpressureBuffer(1000)
                .observeOn(Schedulers.computation())
                .subscribe(this::processData)
        );
    }
    
    public void feedData(SensorData data) {
        dataProcessor.onNext(data);
    }
    
    public void addConsumer(Consumer<SensorData> consumer) {
        disposables.add(
            dataProcessor
                .onBackpressureBuffer(100)
                .subscribe(consumer::accept)
        );
    }
    
    private void processData(SensorData data) {
        // 复杂的数据处理逻辑
    }
}

// 2. 事件总线(支持背压)
public class BackpressureEventBus {
    private final PublishProcessor<Event> eventProcessor = PublishProcessor.create();
    
    public void postEvent(Event event) {
        eventProcessor.onNext(event);
    }
    
    public Flowable<Event> getEventStream() {
        return eventProcessor
            .onBackpressureBuffer(1000)
            .filter(event -> event != null)
            .share();  // 多播
    }
    
    public Flowable<Event> getEventStream(String eventType) {
        return getEventStream()
            .filter(event -> event.getType().equals(eventType));
    }
}

// 3. 消息队列桥接
public class MessageQueueBridge {
    private final PublishProcessor<Message> messageProcessor = PublishProcessor.create();
    private final MessageQueue queue;
    
    public MessageQueueBridge(MessageQueue queue) {
        this.queue = queue;
        
        // 从队列消费
        Flowable.generate(
            () -> queue.createConsumer(),
            (consumer, emitter) -> {
                Message msg = consumer.receive(1000, TimeUnit.MILLISECONDS);
                if (msg != null) {
                    emitter.onNext(msg);
                } else {
                    emitter.onComplete();
                }
            },
            Consumer::close
        )
        .subscribe(messageProcessor);
    }
    
    public void sendMessage(Message message) {
        messageProcessor.onNext(message);
    }
    
    public Flowable<Message> getMessageStream() {
        return messageProcessor
            .onBackpressureBuffer(100)
            .doOnNext(msg -> queue.acknowledge(msg.getId()));
    }
}

三、BehaviorProcessor(行为处理器)

3.1、核心特性

  • 类似 BehaviorSubject,但支持背压
  • 发射最近的一个值和后续值
  • 需要初始值(或默认值)
  • 支持背压策略
java 复制代码
@Test
public void testBehaviorProcessor() {
    //基本用法
    BehaviorProcessor<String> processor = BehaviorProcessor.createDefault("Initial");

    //订阅者1
    processor.subscribe(data -> System.out.println("Subscriber 1:" + data));

    processor.onNext("Update 1");
    processor.onNext("Update 2");

    //订阅者2(立即受到最近的值update 2)
    processor.subscribe(data -> System.out.println("Subscriber 2:" + data));

    processor.onNext("Update 3");
    processor.onComplete();

    //获取当前值
    String currentValue = processor.getValue();
}

Subscriber 1:Initial
Subscriber 1:Update 1
Subscriber 1:Update 2
Subscriber 2:Update 2
Subscriber 1:Update 3
Subscriber 2:Update 3

背压处理示例:

java 复制代码
public class BehaviorProcessorBackpressure {
    
    public static void demoWithBackpressure() {
        BehaviorProcessor<Integer> processor = BehaviorProcessor.create();
        
        // 快速生产者
        Flowable.interval(1, TimeUnit.MILLISECONDS)
            .take(10000)
            .map(Long::intValue)
            .subscribe(processor);
        
        // 慢速消费者
        processor
            .onBackpressureBuffer(
                100,
                () -> System.out.println("Buffer overflow"),
                BackpressureOverflowStrategy.DROP_OLDEST
            )
            .observeOn(Schedulers.io(), false, 16)
            .subscribe(
                value -> {
                    Thread.sleep(10);  // 慢速处理
                    System.out.println("Processed: " + value);
                }
            );
    }
    
    // 状态管理示例
    public static class StateManager<T> {
        private final BehaviorProcessor<T> stateProcessor;
        private final Flowable<T> stateStream;
        
        public StateManager(T initialState) {
            this.stateProcessor = BehaviorProcessor.createDefault(initialState);
            this.stateStream = stateProcessor
                .onBackpressureLatest()  // 状态只需要最新值
                .distinctUntilChanged()
                .share();
        }
        
        public void setState(T newState) {
            stateProcessor.onNext(newState);
        }
        
        public Flowable<T> getStateStream() {
            return stateStream;
        }
        
        public T getCurrentState() {
            return stateProcessor.getValue();
        }
        
        public boolean hasState() {
            return stateProcessor.hasValue();
        }
    }
}

3.2、使用场景

java 复制代码
// 1. 应用状态管理
public class AppStateManager {
    private final BehaviorProcessor<AppState> stateProcessor = 
        BehaviorProcessor.createDefault(AppState.IDLE);
    private final Flowable<AppState> stateStream;
    
    public AppStateManager() {
        this.stateStream = stateProcessor
            .onBackpressureLatest()
            .distinctUntilChanged()
            .share();
    }
    
    public void transitionTo(AppState newState) {
        if (isValidTransition(stateProcessor.getValue(), newState)) {
            stateProcessor.onNext(newState);
        } else {
            throw new IllegalStateException("Invalid state transition");
        }
    }
    
    public Flowable<AppState> getStateStream() {
        return stateStream;
    }
    
    public AppState getCurrentState() {
        return stateProcessor.getValue();
    }
    
    public Flowable<Boolean> isState(AppState targetState) {
        return stateStream.map(state -> state == targetState);
    }
}

// 2. 配置管理
public class ConfigManager {
    private final BehaviorProcessor<Config> configProcessor = 
        BehaviorProcessor.create();
    private final Flowable<Config> configStream;
    
    public ConfigManager(Config initialConfig) {
        configProcessor.onNext(initialConfig);
        this.configStream = configProcessor
            .onBackpressureLatest()
            .share();
    }
    
    public void updateConfig(Config newConfig) {
        configProcessor.onNext(newConfig);
    }
    
    public Flowable<Config> getConfigStream() {
        return configStream;
    }
    
    public Config getCurrentConfig() {
        return configProcessor.getValue();
    }
    
    public <T> Flowable<T> observeConfigValue(
        Function<Config, T> extractor, 
        Class<T> type
    ) {
        return configStream
            .map(extractor)
            .distinctUntilChanged()
            .filter(Objects::nonNull);
    }
}

// 3. 实时价格更新
public class StockPriceFeed {
    private final BehaviorProcessor<StockPrice> priceProcessor = 
        BehaviorProcessor.create();
    private final Map<String, Flowable<Double>> symbolStreams = new ConcurrentHashMap<>();
    
    public void updatePrice(String symbol, double price) {
        priceProcessor.onNext(new StockPrice(symbol, price, System.currentTimeMillis()));
    }
    
    public Flowable<Double> getPriceStream(String symbol) {
        return symbolStreams.computeIfAbsent(symbol, sym -> 
            priceProcessor
                .onBackpressureLatest()
                .filter(p -> p.getSymbol().equals(sym))
                .map(StockPrice::getPrice)
                .distinctUntilChanged()
                .share()
        );
    }
    
    public Double getCurrentPrice(String symbol) {
        StockPrice current = priceProcessor.getValue();
        if (current != null && current.getSymbol().equals(symbol)) {
            return current.getPrice();
        }
        return null;
    }
}

四、ReplayProcessor(重放处理器)

4.1、核心特性

  • 类似 ReplaySubject,但支持背压
  • 缓存所有发射的数据
  • 可配置缓冲区大小和时间窗口
  • 新订阅者立即收到所有缓存数据
java 复制代码
@Test
public void testReplayProcessor() throws InterruptedException {
    // 1. 无限缓存
    ReplayProcessor<String> unlimited = ReplayProcessor.create();
    unlimited.onNext("Data 1");
    unlimited.onNext("Data 2");
    unlimited.subscribe(data -> System.out.println("Subscriber: " + data));
    // 输出: Data 1, Data 2

    // 2. 限制缓存大小
    ReplayProcessor<Integer> sizeLimited = ReplayProcessor.create(2);
    sizeLimited.onNext(1);
    sizeLimited.onNext(2);
    sizeLimited.onNext(3);  // 1被丢弃
    sizeLimited.subscribe(System.out::println);
    // 输出: 2, 3

    // 3. 限制时间窗口
    ReplayProcessor<String> timeLimited = ReplayProcessor.createWithTime(
            1, TimeUnit.SECONDS, Schedulers.computation()
    );
    timeLimited.onNext("Data 1");
    Thread.sleep(500);
    timeLimited.onNext("Data 2");
    Thread.sleep(600);  // Data 1过期
    timeLimited.subscribe(System.out::println);
    // 输出: Data 2

    // 4. 限制大小和时间
    ReplayProcessor<String> bounded = ReplayProcessor.createWithTimeAndSize(
            1, TimeUnit.SECONDS,  // 时间窗口
            Schedulers.computation(),                  // 最大缓存数量
            100
    );
}


Subscriber: Data 1
Subscriber: Data 2
1
2
3
Data 2

背压处理示例:

java 复制代码
public class ReplayProcessorBackpressure {
    
    public static void demoWithBackpressure() {
        // 创建有界重放处理器
        ReplayProcessor<Integer> processor = ReplayProcessor.createWithSize(1000);
        
        // 快速生产者
        Flowable.interval(1, TimeUnit.MILLISECONDS)
            .take(10000)
            .map(Long::intValue)
            .subscribe(processor);
        
        // 慢速消费者
        processor
            .onBackpressureBuffer(100)
            .observeOn(Schedulers.io(), false, 16)
            .subscribe(
                value -> {
                    Thread.sleep(10);
                    System.out.println("Processed: " + value);
                }
            );
        
        // 新订阅者会收到最近1000个值
        Thread.sleep(1000);
        processor.subscribe(value -> 
            System.out.println("New subscriber: " + value)
        );
    }
    
    // 历史数据查询
    public static class HistoricalDataStore<T> {
        private final ReplayProcessor<T> dataProcessor;
        private final Flowable<T> liveStream;
        
        public HistoricalDataStore(int historySize) {
            this.dataProcessor = ReplayProcessor.createWithSize(historySize);
            this.liveStream = dataProcessor
                .onBackpressureBuffer(100)
                .share();
        }
        
        public void addData(T data) {
            dataProcessor.onNext(data);
        }
        
        public Flowable<T> getLiveStream() {
            return liveStream;
        }
        
        public Flowable<T> getHistory() {
            return dataProcessor.take(dataProcessor.getBufferSize());
        }
        
        public Flowable<T> getRecentData(int count) {
            return dataProcessor.takeLast(count);
        }
        
        public int getStoredCount() {
            return dataProcessor.getBufferSize();
        }
    }
}

4.2、使用场景

java 复制代码
// 1. 日志记录系统
public class LoggingSystem {
    private final ReplayProcessor<LogEntry> logProcessor = 
        ReplayProcessor.createWithTime(1, TimeUnit.HOURS, Schedulers.io());
    private final Flowable<LogEntry> logStream;
    
    public LoggingSystem() {
        this.logStream = logProcessor
            .onBackpressureBuffer(10000)
            .share();
    }
    
    public void log(Level level, String message, Throwable throwable) {
        LogEntry entry = new LogEntry(
            level, 
            message, 
            System.currentTimeMillis(), 
            Thread.currentThread().getName(),
            throwable
        );
        logProcessor.onNext(entry);
    }
    
    public Flowable<LogEntry> getLogStream() {
        return logStream;
    }
    
    public Flowable<LogEntry> getLogsByLevel(Level level) {
        return logStream.filter(entry -> entry.getLevel() == level);
    }
    
    public Flowable<LogEntry> getRecentLogs(int count) {
        return logProcessor.takeLast(count);
    }
    
    public Flowable<LogEntry> getLogsSince(long timestamp) {
        return logStream.filter(entry -> entry.getTimestamp() >= timestamp);
    }
}

// 2. 传感器数据历史
public class SensorDataRecorder {
    private final ReplayProcessor<SensorData> dataProcessor = 
        ReplayProcessor.createWithTimeAndSize(
            10, TimeUnit.MINUTES,
            10000,
            Schedulers.computation()
        );
    private final Flowable<SensorData> dataStream;
    
    public SensorDataRecorder() {
        this.dataStream = dataProcessor
            .onBackpressureBuffer(1000)
            .share();
    }
    
    public void recordData(SensorData data) {
        dataProcessor.onNext(data);
    }
    
    public Flowable<SensorData> getDataStream() {
        return dataStream;
    }
    
    public Flowable<SensorData> getDataBySensor(String sensorId) {
        return dataStream.filter(data -> data.getSensorId().equals(sensorId));
    }
    
    public Flowable<Double> getAverageValue(String sensorId, long duration, TimeUnit unit) {
        long window = System.currentTimeMillis() - unit.toMillis(duration);
        return dataStream
            .filter(data -> 
                data.getSensorId().equals(sensorId) && 
                data.getTimestamp() >= window
            )
            .map(SensorData::getValue)
            .reduce(0.0, (sum, val) -> sum + val)
            .toFlowable()
            .flatMap(sum -> 
                dataStream
                    .filter(data -> 
                        data.getSensorId().equals(sensorId) && 
                        data.getTimestamp() >= window
                    )
                    .count()
                    .map(count -> count > 0 ? sum / count : 0.0)
            );
    }
}

// 3. 聊天消息历史
public class ChatRoom {
    private final ReplayProcessor<Message> messageProcessor = 
        ReplayProcessor.createWithSize(1000);
    private final Flowable<Message> messageStream;
    
    public ChatRoom() {
        this.messageStream = messageProcessor
            .onBackpressureBuffer(100)
            .share();
    }
    
    public void sendMessage(User sender, String content) {
        Message message = new Message(
            sender,
            content,
            System.currentTimeMillis()
        );
        messageProcessor.onNext(message);
    }
    
    public Flowable<Message> getMessageStream() {
        return messageStream;
    }
    
    public Flowable<Message> getMessagesByUser(User user) {
        return messageStream.filter(msg -> msg.getSender().equals(user));
    }
    
    public void replayHistoryFor(User user, int messageCount) {
        messageProcessor
            .takeLast(messageCount)
            .subscribe(msg -> sendToUser(user, msg));
    }
}

五、UnicastProcessor(单播处理器)

5.1、核心特性

  • 类似 UnicastSubject,但支持背压
  • 只允许一个订阅者
  • 支持缓冲区
  • 如果没有订阅者,会缓存数据直到有订阅者
java 复制代码
// 基本用法
UnicastProcessor<String> processor = UnicastProcessor.create();

// 发射数据(还没有订阅者,数据被缓存)
processor.onNext("Cached 1");
processor.onNext("Cached 2");

// 订阅者(立即收到缓存的数据)
processor.subscribe(
    data -> System.out.println("Received: " + data),
    error -> System.err.println("Error: " + error),
    () -> System.out.println("Completed")
);

processor.onNext("Live 1");
processor.onComplete();

// 输出:
// Received: Cached 1
// Received: Cached 2
// Received: Live 1
// Completed

// 尝试第二个订阅者会抛异常
try {
    processor.subscribe(data -> System.out.println("Second: " + data));
} catch (IllegalStateException e) {
    System.out.println("只能有一个订阅者: " + e.getMessage());
}

背压处理示例:

java 复制代码
public class UnicastProcessorBackpressure {
    
    public static void demoWithBackpressure() {
        // 创建带缓冲区的单播处理器
        UnicastProcessor<Integer> processor = UnicastProcessor.create(100);
        
        // 快速生产者
        Flowable.interval(1, TimeUnit.MILLISECONDS)
            .take(1000)
            .map(Long::intValue)
            .subscribe(processor);
        
        // 慢速消费者
        processor
            .onBackpressureBuffer(50)
            .observeOn(Schedulers.io(), false, 16)
            .subscribe(
                value -> {
                    Thread.sleep(10);
                    System.out.println("Processed: " + value);
                }
            );
    }
    
    // 队列式处理器
    public static class QueueProcessor<T> {
        private final UnicastProcessor<T> processor;
        private final Flowable<T> outputStream;
        
        public QueueProcessor(int bufferSize) {
            this.processor = UnicastProcessor.create(bufferSize);
            this.outputStream = processor
                .onBackpressureBuffer(bufferSize)
                .share();
        }
        
        public void enqueue(T item) {
            processor.onNext(item);
        }
        
        public void complete() {
            processor.onComplete();
        }
        
        public void error(Throwable throwable) {
            processor.onError(throwable);
        }
        
        public Flowable<T> getOutputStream() {
            return outputStream;
        }
        
        public boolean hasSubscriber() {
            return processor.hasSubscribers();
        }
        
        public int getBufferSize() {
            return processor.getBufferSize();
        }
    }
}

5.2、使用场景

java 复制代码
// 1. 单消费者任务队列
public class TaskQueue {
    private final UnicastProcessor<Task> taskProcessor = UnicastProcessor.create(1000);
    private final Flowable<Task> taskStream;
    private Disposable workerDisposable;
    
    public TaskQueue() {
        this.taskStream = taskProcessor
            .onBackpressureBuffer(100)
            .observeOn(Schedulers.io(), false, 16);
        
        // 启动工作线程
        this.workerDisposable = taskStream.subscribe(this::processTask);
    }
    
    public void submitTask(Task task) {
        taskProcessor.onNext(task);
    }
    
    public void submitTasks(List<Task> tasks) {
        tasks.forEach(taskProcessor::onNext);
    }
    
    public void shutdown() {
        taskProcessor.onComplete();
        if (workerDisposable != null && !workerDisposable.isDisposed()) {
            workerDisposable.dispose();
        }
    }
    
    public boolean isActive() {
        return workerDisposable != null && !workerDisposable.isDisposed();
    }
    
    private void processTask(Task task) {
        try {
            task.execute();
        } catch (Exception e) {
            System.err.println("Task failed: " + e.getMessage());
        }
    }
}

// 2. 串行写入器
public class SerialWriter {
    private final UnicastProcessor<byte[]> writeProcessor = UnicastProcessor.create(100);
    private final OutputStream outputStream;
    private Disposable writeDisposable;
    
    public SerialWriter(OutputStream outputStream) {
        this.outputStream = outputStream;
        
        this.writeDisposable = writeProcessor
            .onBackpressureBuffer(50)
            .observeOn(Schedulers.io())
            .subscribe(
                data -> {
                    synchronized (outputStream) {
                        outputStream.write(data);
                        outputStream.flush();
                    }
                },
                error -> {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        error.addSuppressed(e);
                    }
                },
                () -> {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            );
    }
    
    public void write(byte[] data) {
        writeProcessor.onNext(data);
    }
    
    public void close() {
        writeProcessor.onComplete();
        if (writeDisposable != null && !writeDisposable.isDisposed()) {
            writeDisposable.dispose();
        }
    }
    
    public boolean isClosed() {
        return writeProcessor.hasComplete() || writeProcessor.hasThrowable();
    }
}

// 3. 命令处理器
public class CommandProcessor {
    private final UnicastProcessor<Command> commandProcessor = UnicastProcessor.create(100);
    private final Map<Class<? extends Command>, CommandHandler> handlers = new ConcurrentHashMap<>();
    private Disposable processingDisposable;
    
    public CommandProcessor() {
        this.processingDisposable = commandProcessor
            .onBackpressureBuffer(50)
            .observeOn(Schedulers.io())
            .subscribe(this::processCommand);
    }
    
    public void registerHandler(Class<? extends Command> commandType, CommandHandler handler) {
        handlers.put(commandType, handler);
    }
    
    public void execute(Command command) {
        commandProcessor.onNext(command);
    }
    
    public void shutdown() {
        commandProcessor.onComplete();
        if (processingDisposable != null && !processingDisposable.isDisposed()) {
            processingDisposable.dispose();
        }
    }
    
    private void processCommand(Command command) {
        CommandHandler handler = handlers.get(command.getClass());
        if (handler != null) {
            try {
                handler.handle(command);
            } catch (Exception e) {
                System.err.println("Command execution failed: " + e.getMessage());
            }
        } else {
            System.err.println("No handler for command: " + command.getClass());
        }
    }
}
相关推荐
冬奇Lab6 小时前
Android系统启动流程深度解析:从Bootloader到Zygote的完整旅程
android·源码阅读
泓博8 小时前
Android中仿照View selector自定义Compose Button
android·vue.js·elementui
zhangphil9 小时前
Android性能分析中trace上到的postAndWait
android
十里-9 小时前
vue2的web项目打包成安卓apk包
android·前端
p***19949 小时前
MySQL——内置函数
android·数据库·mysql
兆子龙10 小时前
我成了🤡, 因为不想看广告,花了40美元自己写了个鸡肋挂机脚本
android·javascript
儿歌八万首12 小时前
Android 全局监听神器:registerActivityLifecycleCallbacks 解析
android·kotlin·activity
弹幕教练宇宙起源12 小时前
cmake文件介绍及用法
android·linux·c++
&岁月不待人&12 小时前
一个Android高级开发的2025总结 【个人总结无大话】
android