首页 | 我的简历 |
Repo简介
Repo是Google开发的用于管理Android版本库的一个工具,repo是使用Python对git进行了一定的封装,并不是用于取代git,它简化了对多个Git版本库的管理。用repo管理的版本库都需要使用git命令来进行操作。
Repo 可以在必要时整合多个 Git 代码库,将相关内容上传到 Gerrit 修订版本控制系统,并自动执行 Android 开发工作流程的部分环节。
Repo 启动器会提供一个 Python 脚本,该脚本可以初始化检出,并可以下载第二部分,即完整的 Repo 工具。完整的 Repo 工具包含在 Android 源代码检出中。该工具默认位于 $SRCDIR/.repo/repo/… 中,它可以从下载的 Repo 启动器接收转发的命令。Repo 使用清单文件将 Git 项目汇总到 Android 超级项目中。
Repo安装
1 | 安装git |
Repo命令
1 | server@dev: $ repo help |
1 | 初始化一个新客户端 |
repo init在当前目录中安装 Repo,这会创建一个 .repo/ 目录,其中包含用于 Repo 源代码和标准 Android 清单文件的 Git 代码库。该 .repo/ 目录中还包含 manifest.xml,这是一个指向 .repo/manifests/ 目录中所选清单的符号链接。
清单文件是以xml的格式组织的,一个清单库可以包含多个清单文件和多个分支,每个清单文件和分支都有对应的版本。
repo目录下有manifest、project、repo文件夹等信息
dumpsys
是一个用于在 Android 系统中获取各种信息的命令。它提供了系统状态、应用程序、服务、进程、内存使用情况、网络连接等方面的详细信息。
dumpsys命令的语法结构如下:
1 | dumpsys [options] [service | name] |
dumpsys命令用法
可通过dumpsys命令查询系统服务的运行状态(对象的成员变量属性值),命令格式:dumpsys 服务名, 例如:
1 | dumpsys activity //查询AMS服务相关信息 |
可查询的服务有很多,可通过下面任一命令查看当前系统所支持的dump服务:
1 | adb shell dumpsys -l |
系统服务
表一:
服务名 | 类名 | 功能 |
---|---|---|
activity | ActivityManagerService | AMS相关信息 |
package | PackageManagerService | PMS相关信息 |
window | WindowManagerService | WMS相关信息 |
input | InputManagerService | IMS相关信息 |
power | PowerManagerService | PMS相关信息 |
batterystats | BatterystatsService | 电池统计信息 |
battery | BatteryService | 电池信息 |
alarm | AlarmManagerService | 闹钟信息 |
dropbox | DropboxManagerService | 调试相关 |
procstats | ProcessStatsService | 进程统计 |
cpuinfo | CpuBinder | CPU |
meminfo | MemBinder | 内存 |
gfxinfo | GraphicsBinder | 图像 |
dbinfo | DbBinder | 数据库 |
表二:
服务名 | 功能 |
---|---|
SurfaceFlinger | 图像相关 |
appops | app使用情况 |
permission | 权限 |
processinfo | 进程服务 |
batteryproperties | 电池相关 |
audio | 查看声音信息 |
netstats | 查看网络统计信息 |
diskstats | 查看空间free状态 |
jobscheduler | 查看任务计划 |
wifi | wifi信息 |
diskstats | 磁盘情况 |
usagestats | 用户使用情况 |
devicestoragemonitor | 设备信息 |
… | … |
获取2次代码改动的对应代码及行数信息
patch介绍
1 | -p, --patch |
使用:
1 | git add -p |
输入 git add -p
进入 patch mode
, 此时 git
会自动将改动切分成多个片段,并展示第一个片段,提示你进行选择。
提示语句是 Stage this hunk [y,n,q,a,d,/,s,e,?]?
这些字母都是什么意思呢? 输入?
回车,可以查看详细的帮助信息:
1 | y - 暂存此区块 |
常见的AMS、PWS、WMS等等都是系统服务,运行于system_server进程,并且向servicemanager进程注册其Binder以便其他进程获取binder与对应的服务进行通信。为了新增自定义系统服务,我们可以参考AMS等原生系统服务编写如下文件:
1、AIDL文件:生成Binder类,其中Stub即为Binder的服务端;
2、XXManagerService:系统服务类,继承自Stub;
3、XXManager:封装了AIDL接口方法的类,相当于Binder客户端(Proxy),其他进程通过此类完成与系统服务的通信。
在frameworks/base/core/java/com/enjoy/service中编写IEnjoyManager.aidl
1 | // IEnjoyManager.aidl |
在frameworks/base/Android.mk中声明AIDL文件:
1 | LOCAL_SRC_FILES += .... |
在frameworks/base/services/core/java/com/enjoy/service中编写EnjoyManagerService.java
1 | package com.enjoy.service; |
在frameworks/base/core/java/com/enjoy/service下编写EnjoyManager.java
1 | package com.enjoy.service; |
framework/base/Android.mk:
1 | packages_to_document := \ |
在frameworks/base/core/java/android/content/Context.java中加入常量:
1 | public static final String ENJOY_SERVICE="enjoy"; |
在frameworks/base/services/java/com/android/server/SystemServer.java中 注册系统服务
1 | import com.enjoy.service.EnjoyManagerService; |
在frameworks/base/core/java/android/app/SystemServiceRegistry.java注册服务获取器:
1 | import com.enjoy.service.IEnjoyManager; |
system/sepolicy/private/service_contexts
1 | activity u:object_r:activity_service:s0 |
system/sepolicy/public/service.te:
1 | type activity_service, app_api_service, ephemeral_app_api_service, system_server_service, service_manager_type; |
system/sepolicy/private/untrusted_app_all.te :
1 | allow untrusted_app enjoy_service:service_manager find; |
如果以当前案例的方式新增自定义系统服务,因为SystemServiceRegistry 中需要使用到com.enjoy
下的类,为了让其获取此包下类的引用,需要配置:build/core/tasks/check_boot_jars/package_whitelist.txt ,加入:com\.enjoy\..*
。否则会因为无法获取类引用而编译报错!
1 | dalvik\..* |
若新增的服务像AMS等原有系统服务的IActivityManager.aidl与ActivityManager一样放在
android.app
包下即可不用进行此步处理!
1 | 编译 |
在需要使用自定义服务的app中编写EnjoyManager(包名与framework中一致):
1 | package com.enjoy.service; |
此时由于类加载的双亲委托机制,app在运行时实际使用的是framework中的EnjoyManager。app中的EnjoyManager仅仅只是为了编译成功编写的空壳。
修改app使用的sdk,可以通过make sdk
将SDK完成编译出来。也可以直接在out/target/common/obj/JAVA_LIBRARIES/framework_intermediates中找到EnjoyManager类将其加入原生SDK中的android.jar
系统服务需要通过ServiceManager.addService("xx", new XXManagerService);
将自己(Binder Stub)注册进入SM才能够让其他进程利用Binder与之通信。而自定义系统服务如果需要根据系统启动的不同阶段进行不同的处理则需要注册生命周期回调。以AMS为例:
/frameworks/base/services/java/com/android/server/SystemService中启动AMS:
1 | private void startBootstrapServices() { |
AMS中的setSystemProcess方法的实现为:
1 | public void setSystemProcess() { |
setSystemProcess中会完成向SM注册AMS的实现。而在setSystemProcess之前有一段代码:
1 | mActivityManagerService = mSystemServiceManager.startService( |
则为注册生命周期监听,ActivityManagerService.Lifecycle就相当于生命周期的回调接口对象,它继承自:
/frameworks/base/services/core/java/com/android/server/SystemService。这个SystemService中主要需要实现两个方法:
onStart() :mSystemServiceManager.startService第一时间回调该函数。
onBootPhase(int phase) : 系统启动的各个阶段会回调该函数
比如AMS中的Lifecycle:
1 | public static final class Lifecycle extends SystemService { |
在AMS中会处理PHASE_ACTIVITY_MANAGER_READY
,经过这个阶段后,也就是需要在下一个阶段PHASE_THIRD_PARTY_APPS_CAN_START
才可以发送广播。
在修改了SDK之后,可能出现同步错误:
此时我们需要去修改AGP插件中的一个类:MockableJarGenerator
在我的工程当中当前使用的AGP版本为:7.2.1,从C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\com.android.tools.build\builder\7.2.1
目录下找到builder-7.2.1-sources.jar 并将其解压,找到com.android.builder.testing
中的MockableJarGenerator。
将此类放入Android Studio工程app模块的单元测试中,并在app的gradle中加入:
1 | dependencies { |
操作步骤为:
最后将C:\Users\Administrator\.gradle\caches\modules-2\files-2.1\com.android.tools.build\builder\7.2.1
中builder-7.2.1.jar的MockableJarGenerator.class
替换成刚刚编译的class文件。
MyAccessibilityService:
1 | package com.withub.android.cloudsharingcourt.util; |
1 | //清单文件中注册 |
1 | //检查是否已开启无障碍自动安装权限 |
1 | //跳转到设置开启无障碍模式页面; |
一、谈谈你对 JNI 和 NDK 的理解
JNI:
1 | JNI 是 `Java Native Interface` 的缩写,即 Java 的本地接口。 |
NDK:
1 | NDK 是 `Native Development Kit` 的缩写,是 Android 的工具开发包。 |
总结:
JNI 是实现的目的,NDK 是 Android 中实现 JNI 的手段。
二、谈谈你对 JNIEnv 和 JavaVM 理解
JavaVM
1 | JavaVM 是虚拟机在 JNI 层的代表。 |
JNIEnv
1 | JNIEnv 表示Java调用native语言的环境,封装了几乎全部 JNI 方法的指针。 |
注意:
1 | 在 native 环境下创建的线程,要想和 java 通信,即需要获取一个 JNIEnv 对象。我们通过 `AttachCurrentThread` 和 |
三、解释一下 JNI 中全局引用和局部引用的区别和使用
全局引用
1 | 通过 `NewGlobalRef` 和 `DeleteGlobalRef` 方法创建和释放一个全局引用。 |
局部引用
1 | 通过 `NewLocalRef` 和 `DeleteLocalRef` 方法创建和释放一个局部引用。 |
扩展:弱全局引用
1 | 通过 `NewWeakGlobalRef` 和 `DeleteWeakGlobalRef` 创建和释放一个弱全局引用。 |
四、JNI 线程间数据怎么互相访问
1 | 考察点和上体类似,线程本来就是共享内存区域的,因此我们需要使用 `全局引用`。 |
五、怎么定位 NDK 中的问题和错误
1 | 一般在开发阶段的话,我们可以通过 log 来定位和分析问题。 |
六、静态注册和动态注册
静态注册:
1 | 通过 `JNIEXPORT` 和 `JNICALL` 两个宏定义声明,`Java + 包名 + 类名 + 方法名` 形式的函数名。不好的地方就是方法名太长了。 |
动态注册:
1 | 通常在 `JNI_OnLoad` 方法中通过 `RegisterNatives` 方法注册,可以不再遵从固定的命名写法(当然为了代码容易理解,名称还是 |
七、API
有的变态题目还是会考验你一些 API 的运用,比如怎么在 JNI 里面调用 Java 的方法,怎么在 JNI 里面抛异常等等。所以一些 API 还是要熟悉一下的,大致都是什么功能,名字大致是啥呀,这个太多了,看链接介绍吧:
#framework学友提供
1 | # 声明一个我们使用的最小版本 |
1 | # 这个项目名称一般和生成的库名称相同 |
1 | include_directories(../../../include) |
1 | # 使用变量添加 |
1 | add_library( # Sets the name of the library. |
1 | find_library( # Sets the name of the path variable. |
1 | # 设置LIBDIR为 ${CMAKE_CURRENT_SOURCE_DIR}/../../../libs/${ANDROID_ABI} |
1 | # 导入静态库 |
1 | target_link_libraries( # Specifies the target library. |
1 | set_target_properties(test PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_LIBRARY_OUTPUT_DIRECTORY_${ARCH}}) |