4、Car App Library入门:Car App Library概述、模板化开发、基础控件使用、生命周期管理

各位同学,今天我们来聊聊Car App Library。说实话,这个库是车载应用开发的基石。我刚开始接触车载开发时,也踩过不少坑,尤其是生命周期管理那块,差点让我加班到怀疑人生。今天我把这些经验都整理出来,希望能帮你少走弯路。

4.1 Car App Library是什么?

Car App Library,简称CAL,是Google为车载系统专门打造的一套UI框架。它跟手机上的Jetpack Compose有点像,但专门针对车载场景做了优化。

为什么要用CAL?你想想看,车载屏幕跟手机屏幕完全不一样。手机你拿在手里,距离眼睛30厘米;车载屏幕呢?至少50厘米开外。而且开车时注意力不能分散,所以UI必须大、清晰、易操作。

CAL的核心特点有三个:

  • 模板化开发:提供预设的页面模板,比如列表页、详情页、设置页
  • 安全驾驶优先:自动处理驾驶状态下的UI限制
  • 统一交互规范:所有应用遵循相同的操作逻辑

重要提示:CAL不是独立的库,它依赖于Android Automotive OS。如果你的项目不是基于AAOS,那CAL可能用不了。

4.2 模板化开发:快速搭建页面

我个人习惯用模板来开发车载应用。为什么?因为省事。CAL提供了几种常用模板,你只需要填充数据就行。

4.2.1 列表模板(ListTemplate)

这是最常用的模板。比如音乐列表、联系人列表、设置项列表,都用它。

// 创建列表模板
ListTemplate.Builder builder = new ListTemplate.Builder();
builder.setTitle("我的音乐");
builder.setHeaderAction(Action.BACK);

// 添加列表项
List<Row> rows = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    Row row = new Row.Builder()
        .setTitle("歌曲 " + i)
        .setSubtitle("艺术家")
        .addText("3:45")
        .build();
    rows.add(row);
}

// 设置列表
CarListAdapter adapter = new CarListAdapter(rows);
builder.setAdapter(adapter);

// 应用到Screen
setTemplate(builder.build());

我在项目中遇到过一个问题:列表项太多时,滑动会卡顿。后来发现是没复用ViewHolder。CAL的CarListAdapter其实已经做了复用,但如果你自定义了布局,记得要手动实现。

4.2.2 网格模板(GridTemplate)

网格模板适合展示图标类内容,比如应用列表、快捷开关。

GridTemplate.Builder builder = new GridTemplate.Builder();
builder.setTitle("应用列表");

// 每个网格项
List<GridItem> items = new ArrayList<>();
for (int i = 0; i < 8; i++) {
    GridItem item = new GridItem.Builder()
        .setTitle("应用" + i)
        .setImage(iconResId)
        .setOnClickListener(v -> {
            // 处理点击
        })
        .build();
    items.add(item);
}

builder.setItems(items);
setTemplate(builder.build());

小技巧:网格模板的行列数可以自适应。但建议每行不超过4列,否则在车载屏幕上会显得拥挤。

4.2.3 面板模板(PaneTemplate)

面板模板适合详情页。比如歌曲详情、联系人详情。

PaneTemplate.Builder builder = new PaneTemplate.Builder();
builder.setTitle("歌曲详情");

Pane pane = new Pane.Builder()
    .setTitle("晴天")
    .setSubtitle("周杰伦")
    .addText("专辑:叶惠美")
    .addText("时长:4:29")
    .setImage(albumArt)
    .build();

builder.setPane(pane);
setTemplate(builder.build());

4.3 基础控件使用

CAL的控件跟Android原生控件不太一样。它更注重触控和语音交互的兼容性。

控件名称 用途 注意事项
Row 列表中的一行 支持标题、副标题、图标、操作按钮
Action 操作按钮 有BACK、APP_ICON、CUSTOM三种类型
CarText 文本显示 自动适配字体大小,支持多行
CarButton 按钮 有PRIMARY、SECONDARY两种样式

举个例子,自定义一个带图标的按钮:

Action customAction = new Action.Builder()
    .setIcon(R.drawable.ic_settings)
    .setOnClickListener(v -> {
        // 打开设置
    })
    .build();

// 添加到模板
builder.setHeaderAction(customAction);

注意:CAL的控件尺寸是固定的,不要尝试用layoutParams去修改。我曾经试过强行改大小,结果在部分车型上显示异常。

4.4 生命周期管理

嗯,这里要注意。车载应用的生命周期跟手机应用有很大区别。手机应用可以自由地在前后台切换,但车载应用不行。

为什么?因为驾驶安全。当车辆行驶时,系统会限制某些操作。比如视频播放、长文本输入,在行驶中会被禁止。

4.4.1 Screen的生命周期

CAL中的Screen类似于Activity,但它有自己的生命周期回调:

public class MyScreen extends Screen {
    
    @Override
    public void onStart() {
        super.onStart();
        // 屏幕可见,开始更新UI
        // 比如开始播放动画
    }
    
    @Override
    public void onResume() {
        super.onResume();
        // 屏幕获得焦点
        // 比如开始语音播报
    }
    
    @Override
    public void onPause() {
        super.onPause();
        // 屏幕失去焦点
        // 比如暂停语音播报
    }
    
    @Override
    public void onStop() {
        super.onStop();
        // 屏幕不可见
        // 比如释放资源
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        // 屏幕被销毁
        // 清理所有资源
    }
}

4.4.2 驾驶状态监听

我曾经在项目中遇到一个坑:音乐播放器在行驶中不能显示歌词。后来发现需要监听驾驶状态。

// 获取驾驶状态管理器
CarDrivingStateManager manager = getCarContext()
    .getCarService(CarDrivingStateManager.class);

// 注册监听
manager.registerCallback(new CarDrivingStateManager.Callback() {
    @Override
    public void onDrivingStateChanged(CarDrivingStateEvent event) {
        int state = event.getStatus();
        if (state == CarDrivingStateEvent.STATUS_DRIVING) {
            // 车辆行驶中,隐藏歌词
            hideLyrics();
        } else {
            // 停车状态,显示歌词
            showLyrics();
        }
    }
});

核心要点:生命周期管理的关键在于「状态感知」。你不仅要管理Screen的生命周期,还要感知车辆状态的变化。两者结合,才能做出安全又流畅的车载应用。

4.4.3 资源释放的最佳实践

我建议在onStop()中释放所有非必要的资源。比如网络请求、传感器监听、动画等。为什么?因为车载系统的内存资源很紧张,后台应用随时可能被回收。

@Override
public void onStop() {
    super.onStop();
    // 取消网络请求
    if (call != null && !call.isCanceled()) {
        call.cancel();
    }
    // 停止动画
    if (animator != null && animator.isRunning()) {
        animator.cancel();
    }
    // 释放图片缓存
    imageLoader.clearCache();
}

说白了,车载开发就是「安全第一」。你写的每一行代码,都要考虑会不会影响驾驶安全。CAL帮我们做了很多限制,但最终的责任还在开发者身上。

好了,这一章的内容就到这里。下一章我们会深入讲解如何用CAL构建一个完整的音乐播放器应用。到时候我会分享更多实战经验,包括如何处理音频焦点、如何管理播放队列等。记得提前预习一下。