应用预测框架-基于Android S

总括

本文基于Android S 应用预测框架,导入了一个tensorflow模型并运行成功,意在调研下Android系统的应用预测流程。

背景介绍

Android 12的应用预测功能是其核心创新之一,旨在通过智能化技术优化用户交互体验。以下是基于官方技术逻辑的详细说明:

一、功能核心机制

  1. 数据收集与行为建模

    Android 12通过机器学习模型持续分析用户行为数据,包括应用使用频率、时间段(如早晨查看新闻、通勤时使用娱乐应用)、场景(工作或休闲)等,构建动态的用户行为画像

    • 数据类型:涵盖应用启动顺序、地理位置、设备使用模式等维度。
    • 隐私保护:数据仅在本地处理,避免上传云端,符合Android隐私沙盒规范。
  2. 智能预测算法

    系统采用多维度关联分析算法,不仅预测单个应用的启动概率,还识别应用间的关联性(例如打开邮件后可能启动日历或文档编辑器)

    • 实时行为整合:结合用户当前操作(如插耳机可能触发音乐应用预加载)进行动态调整。
  3. 后台预加载技术

    预测到用户可能使用的应用后,系统会在后台提前加载其核心组件(如主界面、常用功能模块),但不会完全启动应用进程以节省资源

    • 资源调度优化:通过智能优先级管理,确保预加载不影响前台应用的流畅度或电池续航。

二、实际应用场景(官方示例)

  1. 高频场景预判

    • 早晨例行操作:系统识别用户起床后常打开社交媒体或新闻应用,提前预加载,实现"点击即启动"
    • 通勤娱乐需求:检测到用户连接蓝牙耳机或进入地铁区域时,自动预加载音乐、视频类应用。
  2. 工作流效率提升

    • 在用户启动会议软件后,系统可能预加载文档协作工具(如Google Docs)或邮件客户端,减少多任务切换的等待时间

三、用户体验优化效果

  1. 启动速度提升

    • 官方测试显示,预加载技术可将高频应用的启动时间缩短50%以上,部分场景下实现"瞬时响应"。
  2. 资源与能效平衡

    • 通过动态资源分配,预加载仅占用闲置内存,并在检测到电量不足时自动降低预加载强度,确保续航不受显著影响。

代码实现

关于这部分网上的资料很少,未能找到相关的资料,本着按图索骥的原则,下载一套android 12源码分析一下,以下的分析基于android-12 r3.

一、AppPredictionManagerService

想要实现应用预测的功能,必须有一个系统服务来承担数据采集和预测启动的能力,本着这个思路去检索,发现了"AppPredictionManagerService"

  • AppPredictionManagerService 在SystemServer的startOtherServices函数中启动
  • 需要配置AppPredictionManagerService 绑定的客户端,默认无配置,则不会启动改服务
less 复制代码
 private void startOtherServices(@NonNull TimingsTraceAndSlog t) {
   // 省略部分代码
   // App prediction manager service
   if (deviceHasConfigString(context, R.string.config_defaultAppPredictionService)) {
       t.traceBegin("StartAppPredictionService");
       mSystemServiceManager.startService(APP_PREDICTION_MANAGER_SERVICE_CLASS);
       t.traceEnd();
   } else {
       Slog.d(TAG, "AppPredictionService not defined by OEM");
  }
 }
xml 复制代码
    <!-- The package name for the system's app prediction service.
         This service must be trusted, as it can be activated without explicit consent of the user.
         Example: "com.android.intelligence/.AppPredictionService"
    -->
    <string name="config_defaultAppPredictionService" translatable="false"></string>

同样通过按图索骥的方式,检索到了客户端,模块名为AppPredictionService(com.android.apppredictionservice),这个模块默认不会编译到镜像里面,需要手动添加,代码修改如下:

bash 复制代码
/code/Android12/build$ vi make/target/product/handheld_system_ext.mk +25
PRODUCT_PACKAGES += \
    Launcher3QuickStep \
    Provision \
    Settings \
    StorageManager \
    SystemUI \
    WallpaperCropper \
    AppPredictionService \
xml 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.android.apppredictionservice">

    <uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />

    <application>
        <service
            android:name=".PredictionService"
            android:exported="true">
            <intent-filter>
                <!-- This constant must match AppPredictionService.SERVICE_INTERFACE -->
                <action android:name="android.service.appprediction.AppPredictionService" />
            </intent-filter>
        </service>

    </application>

</manifest>

将config_defaultAppPredictionService配置成对应的名字:

bash 复制代码
<string name="config_defaultAppPredictionService" translatable="false">com.android.apppredictionservice/.PredictionService</string>

好了所有的条件已经准备就绪,我们直接看下AppPredictionManagerService启动的时序图:

  • 服务启动只是把我们配置的客户端service名字记了下来并没有发起连接动作,这里我们需要继续调查服务是怎么绑定上的

二、Launcher3

通过分析log发现,应用和AppPredictionManagerService建立session连接的时候才会触发服务绑定,通过分析代码和增加trace,创建session 这个动作是在Launcher3中作的,AOSP Launcher3代码量很大,这里我们只涉及到感兴趣的部分,研究Launcher3不是目的。

  • 系统启动Launcher3时,Launcher3的onCreate方法中会调用到createPredictor
  • 创建Predictor时,会创建PredictorSession,这个方法的实现在AppPredictionManagerService中,这其中涉及到RemoteService(我想它就是"AppPredictionService"在fwk中的代号)
  • RemoteService会调用到binderservice最终拉起AppPredictionService(com.android.apppredictionservice)

三、AppPredictionService(com.android.apppredictionservice)

AppPredictionService模块就一个类,重点是这个框架,谷歌设计这个的本意是OEM可以自己扩展,目前基于AOSP释放的代码来分析。

  • PredictionService onCreate方法中默认DEFAULT_PACKAGES赋值了五个应用:日历、相册、map、email、浏览器;
  • 这五个应用就是预测应用的默认值,赋值完成后保存到本地sharedPreferences中;
xml 复制代码
emulator_x86_64:/data/data/com.android.apppredictionservice # cd shared_prefs/                                                                                                                                                                                                    
emulator_x86_64:/data/data/com.android.apppredictionservice/shared_prefs # cat mypref.xml                                                                                                                                                                                         
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
    <string name="third">com.android.camera2/com.android.camera.CameraLauncher</string>
    <string name="fifth">com.android.deskclock/com.android.deskclock.DeskClock</string>
    <string name="fourth">com.android.contacts/com.android.contacts.activities.PeopleActivity</string>
    <string name="first">com.android.settings/com.android.settings.Settings</string>
    <string name="second">com.android.quicksearchbox/com.android.quicksearchbox.SearchActivity</string>
</map>
  • 当Laucnher3监控到有app动作时,会触发回调到系统中,系统会调用到AppPredictionService模块来更新target,更新完之后,AppPredictionService将packagelist返回给client,最终经过系统在传回到Launcher3中;

注:此图是简化版本工作流程,实际代码流程复杂。

  • Launcher3会将更新的预测应用展示出来,并保存到本地sharedPreferences,如下图,第一行5个应用就是预测应用;

现有算法

AOSP释放出来的就是一个空壳子,这部分涉及到OEM定制不开源,所以自带的算法基本上就是个demo:

  • 当触发感兴趣的事件时,更新预测list,当前list如果包含点击的应用则返回
  • 如果不包含,直接删除最后一个,将该次的应用添加到第一个
ini 复制代码
        // Check if packageName already exists in existing list of appNames
        for (String packageName:appNames) {
            if (packageName.contains(target.getPackageName())) {
                found = true;
                break;
            }
        }

        if (!found) {
            appNames.remove(appNames.size() - 1);
            appNames.add(0, mostRecentComponent);

            for (int i = 0; i < appNameKeys.length; i++) {
                editor.putString(appNameKeys[i], appNames.get(i));
            }
            editor.apply();

            predictionList.remove(predictionList.size() - 1);
            predictionList.add(0, event.getTarget());

            Log.d(TAG, "onAppTargetEvent:: update predictions");
            postPredictionUpdateToAllClients();
        }

一、Try TensorFlow

why TensorFlow

AOSP 从2017年开始逐步支持tensorflow,到现在越来越完善。

  • 2017年Google I/O大会首次宣布TensorFlow Lite作为轻量级解决方案,并于2017年11月15日正式发布开发者预览版
  • 2022年,TensorFlow Lite通过Google Play服务集成,成为Android官方ML推理引擎,提供稳定的跨版本API支持

TensorFlow在Android上的核心优势

轻量化与高效性能
  • TensorFlow Lite专为移动端优化,模型体积小(通过量化、剪枝等技术压缩),推理速度快,内存占用低
  • 支持硬件加速(如Android Neural Networks API),在GPU或NPU上运行时性能提升显著
跨平台兼容性
  • 模型可无缝部署到Android、iOS及嵌入式设备,且支持多种格式
  • 通过Google Play服务内置二进制文件,减少APK体积,并保持与不同Android版本的兼容性
  • 提供简洁的Java/Kotlin API和丰富的文档,降低开发门
  • 支持异步加载模型,避免主线程阻塞,提升应用流畅度
隐私与离线能力
  • 本地化推理无需依赖云端服务器,保护用户隐私,且在网络不佳时仍可运行
  • 支持设备端训练(如通过Model Maker库),实现个性化模型微调
生态与工具链完善
  • 提供模型转换工具(TFLite Converter)、调试工具及预训练模型库(如MobileNet),简化开发流程
  • 与TensorFlow生态系统(如TF Hub、TF.js)深度集成,便于模型复用与扩展

二、导入尝试

我们本地通过tensorflow做一个模型,尝试将它部署到AppPredictionService模块中:

行为模式模拟与预测
  • 通过生成合成数据(当前应用+时间),预测用户在不同时间段可能打开的下一个应用
  • 支持预测的包名包括日历、相机、通讯录等10个常见Android系统应用。
模型训练与部署
  • 使用TensorFlow构建双输入神经网络,结合嵌入层处理应用名称的类别特征和时间数值特征。
  • 最终将模型转换为TFLite格式(app_predictor.tflite),便于移动端低资源环境部。
导入模型

在APP bp文件中增加对tensorflow的依赖,将模型文件放到assert路径下,并配置依赖

css 复制代码
android_app {
    name: "AppPredictionService",

    defaults: ["apppredictionservice_defaults"],

    manifest: "AndroidManifest.xml",

    srcs: [
        ":AppPredictionService-srcs",
    ],

    static_libs: [
        "tensorflowlite_java",
    ],

    jni_libs: ["libtensorflowlite_jni"],
    aaptflags: ["-0 .tflite"],
    asset_dirs: ["assets"],

    resource_dirs: [
    ],
}

在APP 中加载模型

ini 复制代码
    private static final String MODEL_FILE = "models/app_predictor.tflite";
    try {

            encoder = LabelEncoder.loadFromAssets(getAssets(), "models/label_encoder.txt");
            String[] files = getAssets().list("models");
            Log.d(TAG, "模型目录文件: " + Arrays.toString(files));
            // 从 assets 加载模型
            AssetFileDescriptor fileDescriptor = getAssets().openFd(MODEL_FILE);
            FileInputStream inputStream = new FileInputStream(fileDescriptor.getFileDescriptor());
            FileChannel fileChannel = inputStream.getChannel();
            long startOffset = fileDescriptor.getStartOffset();
            long declaredLength = fileDescriptor.getDeclaredLength();
            MappedByteBuffer buffer = fileChannel.map(FileChannel.MapMode.READ_ONLY, startOffset, declaredLength);

            // 初始化解释器
            Interpreter.Options options = new Interpreter.Options();
            options.setUseNNAPI(false);  // 禁用神经网络API
            options.setUseXNNPACK(false); //禁用XNNPACK优化
            tflite = new Interpreter(buffer, options);
            validateModelInputs();
        } catch (IOException e) {
            Log.e(TAG, "Error loading model", e);
        }

当launcher发生点击事件时,触发预测动作:

ini 复制代码
    private void predictNextApp(String currentApp, int hour) {
        try {
            // 1. 编码并验证输入
            int encodedApp = encoder.encode(currentApp);
            if (encodedApp < 0 || encodedApp >= encoder.getClassCount()) {
                Log.e(TAG, "编码值越界: " + encodedApp);
                return;
            }
            float normalizedHour = hour / 23.0f;

            // 2. 创建输入缓冲区(假设模型期望形状为 [1, 1])
            ByteBuffer appBuffer = createInt32Buffer(encodedApp);  // 形状 [1, 1]
            ByteBuffer hourBuffer = createFloat32Buffer(normalizedHour);  // 形状 [1, 1]

            // 3. 输入包装为 Object[]
            Object[] inputs = new Object[]{appBuffer, hourBuffer};

            // 4. 运行推理
            float[][] output = new float[1][encoder.getClassCount()];
            Map<Integer, Object> outputs = new HashMap<>();
            outputs.put(0, output);
            tflite.runForMultipleInputsOutputs(inputs, outputs);

            // 6. 解析结果
            int predictedId = argmax(output[0]);
            String predictedApp = encoder.decode(predictedId);
            Log.d(TAG, "预测结果: " + predictedApp);
        } catch (Exception e) {
            Log.e(TAG, "推理异常: " + e.getMessage(), e);
        }
    }

模拟器验验证可用:

yaml 复制代码
04-01 22:43:12.112  1507  1507 D PredictionService: onAppTargetEvent com.android.dialer
04-01 22:43:12.112  1507  1507 D PredictionService: 预测结果: com.android.deskclock

代码路径

本文涉及到的源码基于Android 12, 涉及到的源码路径如下:

  • App:

" packages/apps/OnDeviceAppPrediction "

" packages/apps/Launcher3 "

  • Framework:

" frameworks/base/core/java/android/service/appprediction/ "

" frameworks/base/services/appprediction/java/com/android/server/appprediction/ "

" frameworks/base/core/java/android/app/prediction/ "

  • Tensorflow

" external/tensorflow "

相关推荐
你们补药再卷啦10 分钟前
springboot 项目 jmeter简单测试流程
java·spring boot·后端
网安密谈19 分钟前
SM算法核心技术解析与工程实践指南
后端
bobz96524 分钟前
Keepalived 检查和通知脚本
后端
AKAMAI25 分钟前
教程:在Linode平台上用TrueNAS搭建大规模存储系统
后端·云原生·云计算
盘盘宝藏27 分钟前
idea搭建Python环境
后端·intellij idea
喵手31 分钟前
Spring Boot 项目基于责任链模式实现复杂接口的解耦和动态编排!
spring boot·后端·责任链模式
大鹏dapeng38 分钟前
使用gone v2 的 Provider 机制升级改造 goner/xorm 的过程记录
后端·设计模式·go
雷渊38 分钟前
介绍一下RocketMQ的几种集群模式
java·后端·面试
讳疾忌医_note40 分钟前
别再错用 C++ 线程池!正确姿势与常见误区大揭秘
后端
快乐源泉40 分钟前
【设计模式】参数校验逻辑复杂,代码长?用责任链
后端·设计模式·go