如何实现使用Android Studio编译Setting

背景

众所周之,Android的Setting source code是跟随Android整包source code一起编译的,主要原因是Setting作为系统应用需要获取大量权限和调用大量系统接口。
但这种方式给我们的开发效率造成很大影响:

  • Setting相关开发人员都需要有整包Android source code,浪费服务器空间
  • 没法使用Android Studio进行高效写code和调试
  • 没法灵活使用其它私有aar/jar和module(已搭建了内部Maven)

如何实现使用Android Studio编译Setting

第一目标: 编译通过

1. 代码整理

第一步是先参考现有的Android Studio apk项目,将gradle相关文件都准备好
根目录:
根目录结构
app目录:
app目录结构

第二步是参考现有apk项目,将Setting AndroidManifest.xml、源码和资源文件整理移到app\src\main\目录下
app\src\main\目录结构

2. 依赖包导入

除了frameworks.jar之外需要导入哪些取决于自己的项目功能,我们的项目需要导入以下依赖包:

namefrom path
frameworksout/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes-header.jar
libcoreout/target/common/obj/JAVA_LIBRARIES/core.platform.api.stubs_intermediates/classes-header.jar
droidlogicout/target/common/obj/JAVA_LIBRARIES/droidlogic_intermediates/classes-header.jar
settingslibout/target/common/obj/JAVA_LIBRARIES/SettingsLib_intermediates/classes.jar

settingslib.jar需要特别编译才能获取到:

mmma frameworks/base/packages/SettingsLib/

导入的方式:

  1. 可以是将jar包直接放到代码里
  2. 也可以将jar包上传到内部Maven,gradle编译的时候从内部maven下载

具体方法在网上可以查得到,我这里就不细说了,我们是采用方式2,下面是新增的修改:
app/build.gradle

configurations {
    androidsdk
    droidlogic
    libcore
    settingslib
}

gradle.projectsEvaluated {
    tasks.withType(JavaCompile) {
        List<File> newFileList = new ArrayList<>()
        newFileList.add(new File(configurations.androidsdk.asPath))
        newFileList.add(new File(configurations.droidlogic.asPath))
        newFileList.add(new File(configurations.libcore.asPath))
        newFileList.add(new File(configurations.settingslib.asPath))
        if (options.bootstrapClasspath != null) {
            newFileList.addAll(options.bootstrapClasspath.getFiles())
        }
        options.bootstrapClasspath = files(newFileList.toArray())
    }
}

dependencies {
    androidsdk "com.xxx.sdk:android-${android.compileSdk}:latest.release"
    droidlogic "com.xxx.sdk:droidlogic-${android.compileSdk}:latest.release"
    libcore "com.xxx.sdk:libcore-${android.compileSdk}:latest.release"
    compileOnly configurations.androidsdk.dependencies
    compileOnly configurations.droidlogic.dependencies
    compileOnly configurations.libcore.dependencies
    // SettingsLib must be implementation because it's not a runtime jar
    settingslib "com.xxx.sdk:settingslib-${android.compileSdk}:latest.release"
    implementation configurations.settingslib.dependencies

3. 接口反射

本以为导入了自己编译的frameworks.jar就可以随便调用系统接口,事实上对于那些所在类都不允许app访问的接口是可以直接调用,而所在类可以访问只是部分接口不能访问的,由于Android Studio会优先使用android sdk,所以总是编译不过。尝试了网上的方法将iml文件里的android sdk移到最后也是不行。
所以对于这种情况,就挨个实现反射,例如:

    private static Method gGetCurrentNetwork;

    @Nullable
    public static Network getCurrentNetwork(@NonNull WifiManager wifiManager) {
        if (gGetCurrentNetwork == null) {
            try {
                gGetCurrentNetwork = WifiManager.class.getMethod("getCurrentNetwork");
                gGetCurrentNetwork.setAccessible(true);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
                return null;
            }
        }
        try {
            return (Network) gGetCurrentNetwork.invoke(wifiManager);
        } catch (IllegalAccessException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return null;
    }

第二目标:正常运行

1. 系统签名和权限问题

按说会考虑将Setting改用Android Studio编译,应该是已经有用Android Studio开发系统应用的经验了,所以这里也不细说这部分,只是简单说明一下:

  • prebuilt_etc是将权限文件预置到指定分区etc目录下,如/system_ext/etc/
  • 根据自己的功能所需权限填写到配置文件com.xxx.setting.xml
  • 如果系统原生Setting还保留着,一定要配置overrides,否则启动的时候会弹出让用户选择哪个Setting

下面是我们项目将apk放到Android source code进行打包的Android.bp文件和权限配置文件,(com.xxx.setting是应用包名)
Android.bp

prebuilt_etc {
    name: "privapp_whitelist_com.xxx.setting",
    system_ext_specific: true,
    sub_dir: "permissions",
    src: "com.xxx.setting.xml",
    filename_from_src: true,
}

android_app_import {
    name: "XxxSetting",
    overrides: ["TvSettings"],
    privileged: true,
    system_ext_specific: true,
    apk: "XxxSetting.apk",
    presigned: true,
    required: ["privapp_whitelist_com.xxx.setting"],
}

com.xxx.setting.xml

<permissions>
    <privapp-permissions package="com.xxx.setting">
        <permission name="android.permission.BACKUP"/>
        <permission name="android.permission.DELETE_CACHE_FILES"/>
        <permission name="android.permission.DUMP"/>
        <permission name="android.permission.FORCE_STOP_PACKAGES"/>
        <permission name="android.permission.GET_ACCOUNTS_PRIVILEGED"/>
        <permission name="android.permission.MANAGE_DEBUGGING"/>
        <permission name="android.permission.MANAGE_USERS"/>
        <permission name="android.permission.OVERRIDE_WIFI_CONFIG"/>
        <permission name="android.permission.REBOOT"/>
        <permission name="android.permission.SET_TIME"/>
        <permission name="android.permission.SET_TIME_ZONE"/>
        <permission name="android.permission.USE_RESERVED_DISK"/>
        <permission name="android.permission.WRITE_MEDIA_STORAGE"/>
        <permission name="android.permission.WRITE_SECURE_SETTINGS"/>
        <permission name="android.permission.BLUETOOTH_PRIVILEGED"/>
        <permission name="android.permission.INTERACT_ACROSS_USERS"/>
        <permission name="android.permission.HDMI_CEC"/>
        <permission name="android.permission.CHANGE_CONFIGURATION"/>
        <permission name="android.permission.MODIFY_AUDIO_ROUTING"/>
        <permission name="android.permission.SUGGEST_MANUAL_TIME_AND_ZONE"/>
    </privapp-permissions>
</permissions>

2. 依赖包问题

细心的小伙伴可能已经发现了,对于settingslib这个依赖的方式与另外三个不同,是用的implementation,原因是正常编译出来的image并没有打包它,所以如果用compileOnly的话,运行的时候如何有跑到使用settingslib相关接口的,就会报如下错误:
settingslib运行报错
但是,直接用implementation的话,又出现了编译问题,原因是上面编译生成的settingslib.jar打包了frameworks的一些类,导致重复:
settingslib编译报错
我的解决方法是:

  1. 用解压缩工具打开settingslib.jar
  2. 只保留com目录,其它全部删除
    settingslib只保留com目录
  • 24
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值