借鉴笔记

本文介绍了Android开发中的各种技术,包括电话靠近熄屏的实现、Log分析、aapt配置、patch合入方法、Android源码编译错误处理、禁止ODX、Android 9.0 Settings的定制、Preference使用、预置不可卸载APK的配置,以及静态库与共享库的区别。此外,还分享了如何处理编译过程中的冲突和错误,以及Android.mk的使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我的私人博客:www.mrloveqin.top 可以查看更多内容

电话靠近手机熄屏幕

在updateProximitySensorMode函数中,判断是否开启基于距离传感器的亮灭屏功能,设置screenOnImmediately。
如果是通话中,并且screenOnImmediately 为false,则开启距离传感器的亮灭屏功能。否则关闭距离传感器功能,则靠近屏幕不会息屏。

Log分析

系统根据log类型,分为main,radio,events,system,crash
frameworks/base/core/java/android/util/Log.java //用于记录main log
frameworks/base/core/java/android/util/Slog.java //用于记录framework log
frameworks/base/telephony/java/android/telephony/Rlog.java //用于记录radio log
frameworks/base/core/java/com/android/internal/os/RuntimeInit.java //内部记录了crash log

aapt linux配置

#Add Android build-tools AAPT variable

AAPT_HOME=/home/developer/android-sdk-linux/build-tools/26.0.2参考路径
export AAPT_HOME
export PATH=$PATH:$AAPT_HOME

合入patch

注意事项:要确定patch是属于公共目录还是订单私有目录,如果是其他订单的私有目录需要找到本订单的对应目录进行修改合入
根据给定的patch list清单
从gerrit 上查找patch

cherry-pick

1 identity tree 其他人已经合入不用处理

2 merge conflict 存在冲突需要本地解决冲突

3 找不到patch 需要从本地对应目录 切换分支 gitk . 进行查找

4 本地合入 可以选择右上角cherry-pick 通过 在对应目录打开终端执行复制的代码产生冲突然后本地解决.解决后cherry-pick --continue 不需要commit 可以直接push <<<<<< 旧 = ====== 新>>>>>>>

Android源码编译报错集合

build/core/base_rules.mk:238: error: external/googletest/googletest: MODULE.TARGET.STATIC_LIBRARIES.libgtest already defined by external/googletest/googletest. 16:57:43 ckati failed with: exit status 1
在编译过程中多次遇到这个问题,问题说的是某些东西重定义了,后来多方查找,原来是我在mac的环境变量中设置过NDK_ROOT和SDK_ROOT,也就是NDK和SDK的路径,然后后面编译Android源码时会与它们产生冲突,所以暂时把设置过的NDK和SDK环境变量取消掉,就可以解决这个问题了

禁止ODX

LOCAL_DEX_PREOPT := false

Android 9.0 Settings 定制

首先,最重要的一点要知道,Settings的布局文件解析是从AndroidManifest.xml中加载的,因此我们新加的子项要定义在Manifest中。

Settings 菜单追溯

打开Settings看到有一个Connected devices选项,那么我们去源码package下面的Settings目录下面搜索grep -nr “Connected devices”,然后会发现定义在strings.xml里面。values/strings.xml:6940:

Connected devices

接下来我们用同样的方法寻找connected_devices_dashboard_title。

xml/connected_devices.xml:21: android:title="@string/connected_devices_dashboard_title">

继续找引用该xml文件的java文件connected_devices.xml

src/com/android/settings/connecteddevice/ConnectedDeviceDashboardFragment.java:67: return R.xml.connected_devices;

接下来找到ConnectedDeviceDashboardFragment在哪里被引用

会发现AndroidManifest文件引用到了,然后是SettingGateway.java中引用了。

再看Settings的主java文件Settings.java,打开后发现它只做了一些内部类的空定义,然后继承于SettingsActivity.java。

打开SettingsActivity发现是在这里做的界面选择处理,加载不同的布局。

里面switchToFragment()方法中会判断AndroidManifest中的Fragment类型的子项是否在SettingGateWay中的两个数组中进行了定义,定义过才会加载。

并且如果采用Fragment加载子项布局,则需先在Settings.java中先定义内部类初始化。Activity加载子项布局则不需要。

现在我们得到关键的几个相关文件:Settings.java、SettingGateWay.java、SettingsActivity.java、AndroidManifest.xml。

接下来我们开始定义自己的子项。

首先打开AndroidManifest文件

<activity android:name=“Settings”

            android:taskAffinity="com.android.settings.root"

            android:label="@string/settings_label_launcher"

            android:launchMode="singleTask">

        <intent-filter android:priority="1">

            <action android:name="android.settings.SETTINGS" />

            <category android:name="android.intent.category.DEFAULT" />

        </intent-filter>

        <meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"

            android:value="true" />

    </activity>


    <!-- Alias for launcher activity only, as this belongs to each profile. -->

    <activity-alias android:name="Settings"

            android:taskAffinity="com.android.settings.root"

            android:label="@string/settings_label_launcher"

            android:launchMode="singleTask"

            android:targetActivity="Settings">

        <intent-filter>

            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.DEFAULT" />

            <category android:name="android.intent.category.LAUNCHER" />

        </intent-filter>

        <meta-data android:name="android.app.shortcuts" android:resource="@xml/shortcuts"/>

    </activity-alias>

activity-alias作为一个已存在Activity的别名,则应该可以通过该别名标签声明快速打开目标Activity。因此activity-alias可用来设置某个Activity的快捷入口,可以放在桌面上或者通过该别名被其他组件快速调起。该标签元素支持一些属性及intent-filter、meta-data等配置,因此可以触发一些跟目标Activity不同的功能逻辑,虽然打开的是同一个Activity。举个简单的例子,如之前需要先打开主界面,然后才能点击进入某个Activity,如果使用activity-alias为该Activity配置一个快捷入口,甚至可以为其在桌面生成一个图标,然后点击桌面图标可直接进入该Activity,该功能可满足某些需要快速到达功能界面的需求。

taskAffinity,可以翻译为任务相关性。这个参数标识了一个 Activity 所需要的任务栈的名字,默认情况下,所有 Activity 所需的任务栈的名字为应用的包名,当 Activity 设置了 taskAffinity 属性,那么这个 Activity 在被创建时就会运行在和 taskAffinity 名字相同的任务栈中,如果没有,则新建 taskAffinity 指定的任务栈,并将 Activity 放入该栈中。另外,taskAffinity 属性主要和 singleTask 或者 allowTaskReparenting 属性配对使用,在其他情况下没有意义。

与 singleTask 结合使用,调用 Activity 流程:A -> B -> B -> A -> B,设置 B 的启动模式为 singleTask ,并设置 taskAffinity

B 被创建时,因没有 com.will.testdemo.task1 的任务栈,于是新建任务栈,并把 B 放入栈内。继续创建 A,由于 A 没有设置启动模式,则放入 com.will.testdemo.task1 栈中。再一次启动 B,因栈内有 B 实例,所以系统就把 B 调到栈顶,由于 singleTask 默认有 clearTop 的效果,导致栈内所有在它上面的 Activity 全部出栈,所以最后 com.will.testdemo.task1 栈内只有 B 一个实例

android?定义子项的显示名字

         name:设置该Activity属于哪个应用,fragment使用Settings$testName

testName为定义在Settings中的内部类名称。Activity 则 类似下面的写法android:name=".fishing.FishingWarningActivity"

<meta-data android:name="com.android.settings.category"

     android:value="com.android.settings.category.ia.homepage"/>

表示该子项是Settings一级菜单子项,如果要定义为其他级别,如system的子项,则将homepage改为system,具体可参考已有的子项。 这个会设置你的第三方APK出现在具体哪一级的菜单目录中,category目录分级见源代码 /frameworks/base/packages/SettingsLib/src/com/android/settingslib/drawer/CategoryKey.java

	<meta-data android:name="com.android.settings.ACTIVITY_ACTION"

         android:value="com.android.settings.HardKey" />

表示该子项由Activity加载布局,不需要在Settings,SettingGateway中定义,如果要用fragment加载布局则将ACTIVITY_ACTION换成FRAGMENT_CLASS并且需要在Settings定义内部类,SettingGateway中进行数组数据的添加。

value表示该文件的路径。

这个必须要有,不然不会显示在设置菜单项中, priority:优先级

<intent-filter android:priority="9">

      <action android:name="com.android.settings.action.SETTINGS" />

</intent-filter>

添加新的类似功能时,对比已有功能实现,所以也加上这个

<meta-data android:name="com.android.settings.PRIMARY_PROFILE_CONTROLLED"android:value="true" />

Android Preference

Preference 布局属性

通用的xml 布局属性

在preference布局文件中,我们可以会经常用到下面的属性,这些属性都是preference控件相互通用的。

android:key这个属性相当于android:id的作用。用此key来唯一表示此Preference

android:titlePreference的标题,

android:summary表示Preference提示语,相对于android:title来说显示要小点,而且显示位置在android:title下面

android:defaultValue这个表示的是默认值,比如CheclPreference这种preference就可以指定默认是"true"还是“false”

android:enabled表示该Preference是否可用状态

CheckBoxPreference

android:summaryOn在选择显示的summary

android:summaryOff在不选择显示的summary

ListPreference属性

android:dialogTitle弹出对话框的标题

android:entries列表中显示的值。为一个数组,通读通过资源文件进行设置。

android:entryValues列表中实际保存的值,也entries对应。为一个数组,通读通过资源文件进行设置

RingtonePreference 属性分析

android:showDefault是否在列表中显示“默认”选项

android:showSilent是否显示“静音”选项

android:ringtoneType铃音类型,可能值包括 ringtone、notification、alarm和all

数据的保存与读取

Android系统会将Preference元素的值存储在sharedPreference文件中。该文件存放路径data/data/[packgename]/shared_prefs/文件下,命名约定为:packagename_preference.xml

那么我们要如何去获取这些已经保存的数据呢?首先我们要通过xml中的key获取数值,就是Preference控件中android:key所对应的属性(已一个键值对的形式保存,name为Preference的key值,value为Preference的value值),读取方法如下:

SharedPreferences
prefs =PreferenceManager.getDefaultSharedPreferences(this) ;//open_sound是给Preference元素设置的keyLog.i(TAG,“open_sound=”+prefs.getBoolean(“open_sound”,false));

使用的方法都很简单,代码注释都很清楚了。

关于Preference需要说明的是:

如果app是针对11以上的,推荐使用AppCompatActivity和PreferenceFragment来实现。

如果兼容更早的版本,需要借助AppCompatDelegate实现

LOCAL_CERTIFICATIOM

build/target/product/security目录中有四组默认签名供Android.mk在编译APK使用:

1、testkey:普通APK,默认情况下使用。

2、platform:该APK完成一些系统的核心功能。经过对系统中存在的文件夹的访问测试,这种方式编译出来的APK所在进程的UID为system。

3、shared:该APK需要和home/contacts进程共享数据。

4、media:该APK是media/download系统中的一环。

应用程序的Android.mk中有一个LOCAL_CERTIFICATE字段,由它指定用哪个key签名,未指定的默认用testkey.

Settings.apk就是platform级别的签名,系统级应用都应该使用这个签名

预置不可卸载APK

Android系统预制APK(不可卸载)

第一步:

在 “/vendor/…/packages/apps” 目录下创建一个对应名称的文件夹。

第二步:

将所需apk放入文件夹中。

第三步,编辑 Android.mk :

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

#设置apk的名字,XXX为apk名称
#Module name should match apk name to be installed.

LOCAL_MODULE := XXX

LOCAL_SRC_FILES := $(LOCAL_MODULE).apk

LOCAL_MODULE_TAGS := optional

LOCAL_MODULE_CLASS := APPS

LOCAL_MODULE_SUFFIX := $(COMMON_ANDROID_PACKAGE_SUFFIX)

#签名方式,如果是platform,代表签名成系统软件, 如果还是PRESIGND,表示用的是apk原本的签名。

LOCAL_CERTIFICATE := platform

#将apk编进“/system/priv-app/目录”,如果为false,或者不加这句话,就会编进“/system/app” 目录, 二者区别在于前者的权限要高于 后者

LOCAL_PRIVILEGED_MODULE := true

#(如果 LOCAL_MULTILIB 是32,意思是编译出32位的lib库,64异曲同工,如果是both,代表编译出两种库文件, 当然,首先要解压 apk,看看lib库是32的还是64的。 另外,如果手机系统是64位的,而lib库是32位的,则需要在 “/frameworks/base/services/core/java/com/android/serve/pm/PackageManagerService.java” 中进行配置, lineNumber : 6221)

LOCAL_MULTILIB := 32

include $(BUILD_PREBUILT)

补充: LOCAL_OVERRIDES_PACKAGES := Calculator#覆盖掉之前有的一个apk,名字叫Calculator

第四步:

在 /device/平台/…/项目目录下找到相应的版本,打开其中的 “项目名.mk” 文件, 添加:

#编译项目时,会在对应的版本中添加上这个apk。

PRODUCT_PACKAGES += XXX

第五步:

编译一下整个项目,运行调试,就会将apk编进 “/system/priv-app/” 或 " /system/app"。

JAVA_STATIC_LIBRARY(Java共享库和JAVA静态库的区别)

静态库与共享库的区别

简单来讲,静态库是在连接阶段直接拷贝到代码中使用的,而共享库是由加载器加载到内存,在运行时使用的。编译出来的静态库(这里指jar包)里每个java文件对应的class文件都单独存在,可以直接导入Eclipse等IDE使用,而编译出来的共享库(jar包),内部是Android字节码Dex格式的文件,

一般无法导入Eclipse等IDE使用。Android.mk中由BUILD_JAVA_LIBRARY指定生成共享库,BUILD_STATIC_JAVA_LIBRARY指定生成静态库。

编译静态库和共享库

下面是两个生成静态库和动态库的例子。

生成静态库的mk文件:

// 返回Android.mk所在的当前路径

LOCAL_PATH:= $(call my-dir)

// 清理除LOCAL_PATH变量以外的LOCAL_XXX

include $(CLEAR_VARS)

// 生成静态库的name

LOCAL_MODULE := simple_game

// 设置在那个版本下编译,user, eng, tests, optional(全版本编译)

LOCAL_MODULE_TAGS := optional

// 签名类型 testkey, media, platform, shared, 默认为testkey

LOCAL_CERTIFICATE := platform

// 生成静态库使用的源文件

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_SRC_FILES += $(call all-Iaidl-files-under, src)

// 设置生成静态库

include $(BUILD_STATIC_JAVA_LIBRARY)

生成共享库的mk文件:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := simple_game

LOCAL_MODULE_TAGS := optional

LOCAL_CERTIFICATE := platform

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_SRC_FILES += $(call all-Iaidl-files-under, src)

// 安装时需要一起安装的模块

LOCAL_REQUIRED_MODULES := simple_game.xml

// 设置生成共享库

include $(BUILD_JAVA_LIBRARY)

################################################################

include $(CLEAR_VARS)

LOCAL_MODULE := simple_game.xml

// 定制LOCAL_MODULE_PATH变量的值

LOCAL_MODULE_CLASS := ETC

// 设置安装的路径

LOCAL_MODULE_PATH := $(TARGET_OUT)/etc/permissions

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := simple_game.xml

// 将文件预置进系统

include $(BUILD_PREBUILT)

共享库需要一个xml文件配置安装的信息,LOCAL_REQUIRED_MODULES设置了该文件,当共享库安装时,LOCAL_REQUIRED_MODULES制定的模块也会安装到系统中。

安装的位置由LOCAL_MODULE_PATH指定,当LOCAL_MODULE_PATH没有设置时,系统将会根据LOCAL_MODULE_CLASS的值来判断安装的位置,LOCAL_MODULE_CLASS变量将通过/build/core/base_rules.mk文件设置默认的安装目录,常用的值有:
apk文件: APPS
so文件: SHARED_LIBRARIES
bin文件: EXECUTABLES
其他文件: ETC

当LOCAL_MODULE_PATH和LOCAL_MODULE_CLASS均没有设置时,系统会根据编译生成的不同模块(根据include$()判断)来自动设置LOCAL_MODULE_CLASS的值,

但遇到include $(BUILD_PREBUILT)编译选项时,系统不会设置模块的LOCAL_MODULE_CLASS,需要显示设置。

最后针对单一文件进行预编译需要设置BUILD_PREBUILT,对于多文件需要设置BUILD_MULTI_PREBUILT宏。

静态库和共享库的使用

完成了库的编译,接下来看看库的使用方法。

静态库很多情况来自第三方,包括jar包和第三方工程。首先,获得到jar后需要导入项目代码,一般放在libs目录下,接下来需要修改项目的mk,加入

编译静态库的代码,类似一下XML:

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

// 依赖静态库的名称,可以随便起名

LOCAL_STATIC_JAVA_LIBRARIES := simple_game

// 预编译静态库,冒号之前名称要和LOCAL_STATIC_JAVA_LIBRARIES一致,冒号后面是静态库路径

LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := simple_game:libs/simple_game.jar

include $(BUILD_MULTI_PREBUILT)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_SRC_FILES += $(call all-Iaidl-files-under, src)

// 定义生成apk名称

LOCAL_PACKAGE_NAME := game

// 定义app是否要放在/system/priv-app

LOCAL_PRIVILEGED_MODULE := true

LOCAL_CERTIFICATE := platform

// 定义是否需要代码混淆

LOCAL_PROGUARD_ENABLED := disabled

// 编译MODULE为apk

include $(BUILD_PACKAGE)

include $(BUILD_PACKAGE) 会结合LOCAL_PRIVILEGED_MODULE判断生成的apk是最终安装在/system/app下还是/system/priv-app下。

LOCAL_PROGUARD_ENABLED定义了编译apk时是否进行代码混淆,默认为full,将工程代码全部混淆,也可以用LOCAL_PROGUARD_FLAG_FILES配置混淆规则。

静态库的使用比较简单,下面来看看共享库的使用。

LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)

LOCAL_SRC_FILES := $(call all-java-files-under, src)

LOCAL_SRC_FILES += $(call all-Iaidl-files-under, src)

LOCAL_PACKAGE_NAME := game

LOCAL_PRIVILEGED_MODULE := true

LOCAL_MODULE_TAGS := optional

LOCAL_CERTIFICATE := platform

LOCAL_PROGUARD_ENABLED := disabled

// MODULE 依赖的共享库

LOCAL_JAVA_LIBRARIES += simple_game

// 安装时module时,共享库需要一起安装。

LOCAL_REQUIRED_MODULES := simple_game

include $(BUILD_PACKAGE)

上面是一个简单使用共享库的例子,LOCAL_JAVA_LIBRARIES定义了依赖的共享库,全编译时检查到该选项系统会先编译simple_game库,然后检查

LOCAL_REQUIRED_MODULES,决定是否安装该库(即是否在/system/framework下生成该库),注意,单编时mm, mm -B, mmm均不会编译module

使用的依赖文件,需要使用m+module(如: m game)来编译。

app在使用时千万别忘了在AndroidManifest.xml中添加该共享库:

有很多同学不注意就忘了,然后查了半天才恍然大悟,切记切记!

最后,类似$(call my-dir)宏函数的定义,可以在/build/core/definitions.mk中找到,其他变量定义可以在/build/core/base_rules.mk中找到。

Android.mk

要编译一个模块,只要在编译之前根据需要设置这些变量然后执行编译即可。它们包括:

LOCAL_SRC_FILES:当前模块包含的所有源代码文件。

LOCAL_MODULE:当前模块的名称,这个名称应当是唯一的,模块间的依赖关系就是通过这个名称来引用的。

LOCAL_C_INCLUDES:C 或 C++ 语言需要的头文件的路径。

LOCAL_STATIC_LIBRARIES:当前模块在静态链接时需要的库的名称。

LOCAL_SHARED_LIBRARIES:当前模块在运行时依赖的动态库的名称。

LOCAL_CFLAGS:提供给 C/C++ 编译器的额外编译参数。

LOCAL_JAVA_LIBRARIES:当前模块依赖的 Java 共享库。

LOCAL_STATIC_JAVA_LIBRARIES:当前模块依赖的 Java 静态库。

LOCAL_PACKAGE_NAME:当前 APK 应用的名称。

LOCAL_CERTIFICATE:签署当前应用的证书名称。

LOCAL_MODULE_TAGS:当前模块所包含的标签,一个模块可以包含多个标签。标签的值可能是 debug, eng, user,development 或者 optional。其中,optional 是默认标签。标签是提供给编译类型使用的。不同的编译类型会安装包含不同标签的模块,那么这些标签的值有代表什么呢?

这个问题问得好,这些标签代表编译类型,编译类型的各个标签分别如下(表格略吃藕,凑合看了 ):

名称 说明

eng 默认类型,该编译类型适用于开发阶段。 当选择这种类型时,编译结果将: 安装包含 eng, debug, user,development 标签的模块 安装所有没有标签的非 APK 模块 安装所有产品定义文件中指定的 APK 模块

user 该编译类型适合用于最终发布阶段。 当选择这种类型时,编译结果将: 安装所有带有 user 标签的模块 安装所有没有标签的非 APK 模块 安装所有产品定义文件中指定的 APK 模块,APK 模块的标签将被忽略

userdebug 该编译类型适合用于 debug 阶段。 该类型和 user 一样,除了: 会安装包含 debug 标签的模块 编译出的系统具有 root 访问权限

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值