"目前流行的阅读 android 系统源码的工具主要有以下几种: source insight, understand, vscode, vim 等。也有同学不用导工程直接 find + grep + 文本编辑器的, 这些工具我也用过很长一段时间, 但总有不满意的地方。 最初的时候是用 source ...."

android-studio 导入 Android 系统源码

目前流行的阅读 android 系统源码的工具主要有以下几种: source insight, understand, vscode, vim 等。也有同学不用导工程直接 find + grep + 文本编辑器的, 这些工具我也用过很长一段时间, 但总有不满意的地方。

最初的时候是用 source insight 的, 后来慢慢的由于受不了它的各种缺点,就抛弃了。先来说说 source insight 的缺点:

  • 只有 windows 版本, 而我不用 windows,虽说可以 wine。

  • 4.0之前的版本不能设置编码为UTF-8, 4.0之后的版本不能对单个文件设置编码。团队里面有同学用 source insight 的,经常会提交中文乱码注释上来。出现这种情况,十之八九是因为 source insight 的编码问题。

  • 文件搜索功能垃圾, 不能模糊匹配。

  • 没有多标签功能, 像浏览器那样同时打开多个文件时以标签的形式打开。

  • 没有语法错误提示, 经常改了代码,也不知道语法对没对,只能编译一次发现一个语法错误,再修改回来。

  • 代码编辑功能菜鸡。

    • 没有自动缩进,有些同学会人工空格或者TAB缩进, 表面上看起来是缩进好了,但用其他文本编辑工具打开的时候就会发现乱七八糟的。有些同学甚至都懒得人工缩进,直接就乱七八糟的格式提交了。
    • 没有代码格式化,当我看到一坨没有缩进的代码,想要格式化一下,却没有对应的功能。

关于source insight的缺点,我还能列一箩筐出来,但光是上面这些问题就已经让人很难受了,可能很多同学用多了也就习惯了,将就了, 也有些同学想方设法去折腾各种插件来解决这些问题, 我一开始也是折腾各种插件的,但怎么都折腾不出满意的效果。于是开始另外寻找工具。

source insight 唯一的好处是一个工程可以选择性的导入某些代码目录。这对于代码模块比较多,零散的工程来说,还是有点方便的。

understand 是跨平台的工具,这点做的比 source insight 好, 也解决了上面提到的一些 source insight 的缺点, 比如多标签, UTF-8 编码。 understand 还有一些比较有特色的功能,比如函数调用链图,其他工具都没有这个功能。这个功能在追查函数的调用关系时还是很方便的。整体来说个人觉得 understand 是比 source insight 用得更顺手一些。也用了大半年,后面也由于很多功能不满意,也抛弃了。

其他的 vscode, vim 等工具就不多说了。折腾起来也会有各种各样不满意的地方。

当然网上搜索一下,也有很多人使用 eclipse, android-studio 来阅读android系统源码的。但网上的教程只是能阅读 java 的代码而已。而做系统层的开发,经常是要写 c/c++ 代码的。有一段时间我是两个工具同时用, android-studio 用来看 java, understand 用来看c++。

后来 android-studio 发布了 3.0 版本, 开始支持 NDK 开发了,体验了一下之后,就觉得这功能不就是我想要的嘛,同时写 java 和 c++ 代码。于是开始各种折腾,折腾了几个星期后,终于找到一种将 android系统源码导入到 android-studio 的方法,使用 gradle + cmake + NDK 支持同时解析 java 和 c++ 代码。理论上来说支持任何 java, c++ 工程或者混合工程。

下面先演示一下导入的过程。

0. 准备工作

  • 禁用 svn, git 等版本管理插件: file --> Settings --> Plugins , 把 CVS Integration, Git Integration, Subversion Integration 这三个插件禁用。因为 android-studio 检测到工程里面有 .svn 或者 .git 目录时,会去执行一些 svn, git 命令, 轻则会导致代码导入速度慢,重则会破坏版本管理数据库,得重新下代码。

  • 安装 cmake, ndk:打开 SDKManager 选择 SDK Tools, 勾选 cmake 和 NDK 然后 install 即可。

1. 配置 idea 插件

在 ~/android_source/android6.0 目录下新建一个 build.gradle 文件,内容如下

buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.1.2'
    }
}

allprojects {
    repositories {
        google()
        jcenter()
    }
}

apply plugin: "idea"
idea {
    module {
        excludeDirs = [
                file(".repo"),
                file("abi"),
                file("art"),
                file("bionic"),
                file("bootable"),
                file("build"),
                file("cts"),
                file("dalvik"),
                file("developers"),
                file("development"),
                file("docs"),
                file("external"),
                file("frameworks/av"),
                file("frameworks/base/api"),
                file("frameworks/base/cmds"),
                file("frameworks/base/data"),
                file("frameworks/base/docs"),
                file("frameworks/base/drm"),
                file("frameworks/base/graphics"),
                file("frameworks/base/include"),
                file("frameworks/base/keystore"),
                file("frameworks/base/libs"),
                file("frameworks/base/location"),
                file("frameworks/base/media"),
                file("frameworks/base/native"),
                file("frameworks/base/nfc-extras"),
                file("frameworks/base/obex"),
                file("frameworks/base/opengl"),
                file("frameworks/base/packages"),
                file("frameworks/base/rs"),
                file("frameworks/base/samples"),
                file("frameworks/base/sax"),
                file("frameworks/base/telecomm"),
                file("frameworks/base/telephony"),
                file("frameworks/base/test-runner"),
                file("frameworks/base/tests"),
                file("frameworks/base/tools"),
                file("frameworks/base/wifi"),
                file("frameworks/compile"),
                file("frameworks/data-binding"),
                file("frameworks/ex"),
                file("frameworks/mff"),
                file("frameworks/minikin"),
                file("frameworks/ml"),
                file("frameworks/multidex"),
                file("frameworks/native"),
                file("frameworks/opt"),
                file("frameworks/rs"),
                file("frameworks/support"),
                file("frameworks/volley"),
                file("frameworks/webview"),
                file("frameworks/wilhelm"),
                file("hardware"),
                file("libcore"),
                file("libnativehelper"),
                file("ndk"),
                file("out"),
                file("packages"),
                file("pdk"),
                file("platform_testing"),
                file("prebuilts"),
                file("sdk"),
                file("system"),
                file("tools")
        ]
    }
}

这个文件与普通的 android 应用的 build.gradle 文件的差别就是多加了一个 apply plugin: "idea", 然后在配置 idea 插件的 excludeDirs。即忽略哪些不目录。这些目录被忽略之后,android-studio 就不会扫描分析里面的文件了。因为 android 源码非常庞大, 如果没有配置 excludeDirs 的话,导入工程可能得导一整天了。一般来说我们只需要关注很少的一部分代码而已,我们只需要保留我们要关注的代码目录即可,其他目录都加到 excludeDirs 里面。

然后再增加一个空的 settings.gradle 文件。里面什么都不用配置,后面再配置。

2. open as a project。

使用 android-studio file --> open 打开 build.gradle 文件,选择 open as a project。如果网络好的话,这一步应该几分钟就OK了。网络慢的话,可能要卡在下载 gradle 依赖这里很久,耐心等待便是。

3. 导入 java 模块

假设我们关注的是 frameworks 的源码, 我们便可以在 frameworks 目录下创建一个 build.gradle 文件,配置一下源码路径就行。不过,其实 我们真正关注的只是 frameworks 目录下的 base/core 和 base/services 目录而已,其他目录不会太关注,为了避免资源的消耗, 我们把framework 目录下的其他目录也加入了 excludeDirs。然后在 frameworks/base/core 下创建一个 build.gradle 文件:

apply plugin: 'java'
sourceSets {
    main {
        java {
            srcDirs = [
                    'java',
            ]
        }
    }
}

然后在 frameworks/base/services 目录下也添加一个 build.gradle 文件:

apply plugin: 'java'
sourceSets {
    main {
        java {
            srcDirs = [
                    'accessibility/java',
                    'appwidget/java',
                    'backup/java',
                    'devicepolicy/java',
                    'java',
                    'core/java',
                    'midi/java',
                    'net/java',
                    'print/java',
                    'restrictions/java',
                    'usage/java',
                    'usb/java',
                    'voiceinteraction/java',

              ]
        }
    }
}

dependencies {
    compile project(':frameworks:base:core')
}

这两个都是纯 java 模块, 配置比较简单,只要把所需的 java 代码目录添加进来即可。

4. settings.gradle

前面虽然加了两个模块的 gradle 配置文件,但 studio 并不会解析这两个模块的代码,还需要我们在 settings.gradle 把这两个模块给 include 进来才行。

include ":frameworks:base:core"
include ":frameworks:base:services"

修改完后,应该会提示 sync, 我们点击一下 sync 等个一两分钟, 就解析完了。

经过以上步骤我们就把 framework 的代码导入完成了,后面我们需要导其他 java 模块的代码的话,按照上面的方法创建 build.gradle 文件并加到 settings.gradle 里面就行。

5. 导入 c++ 模块

上面的方法只能解析 java 代码而已, 做系统层的开发经常是要接触到 c++ 代码的, 若是有一个强大的 ide 的辅助便能省心不少。下面就演示一下导入 c++ 模块的方法。以 recovery 模块为例, 在 bootable/recovery 目录下新建一个 build.gradle 文件:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    defaultConfig {
        applicationId "com.example.deep.androidtest"
        minSdkVersion 17
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
    }
    externalNativeBuild {
        cmake {
            path "CMakeLists.txt"
        }
    }
}

再新建一个 CMakeLists.txt 文件:

cmake_minimum_required(VERSION 2.6)
project(recovery)
set(CMAKE_CXX_STANDARD 11)

FILE(GLOB_RECURSE recovery_header_list "./*.h")
FOREACH(src ${recovery_header_list})
    STRING( REGEX REPLACE "(.*)/.*h$" "\\1" dirName ${src} )
    LIST(APPEND recovery_include_dir ${dirName})
ENDFOREACH(src ${recovery_header_list})
LIST(REMOVE_DUPLICATES recovery_include_dir)
include_directories(${recovery_include_dir})

FILE(GLOB_RECURSE recovery_cpp_list "./*.cpp")
FILE(GLOB_RECURSE recovery_c_list "./*.c")
add_library(recovery ${recovery_c_list} ${recovery_cpp_list} ${recovery_header_list})
set_target_properties(recovery PROPERTIES LINKER_LANGUAGE CXX)

其中 cmake 的语法就不作解析了,有兴趣的同学可以研究一下。

再新建一个 src/main/AndroidManifest.xml 文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="com.example.deep.androidtest">

manifest>

这个文件对于我们来说并没有什么用, 但是 gradle 的 android 插件要求要有这么一个文件,不然会报错, 我们创建这么个文件,简单声明一下 package 信息即可,其他内容都不需要。

最后在 settings.gradle 中把这个模块 include 进来:

include ":frameworks:base:core"
include ":frameworks:base:services"
include ":bootable:recovery"

点击 gradle sync, 稍等一两分钟即可解析完成。再打开 recovery 的源文件,就发现可以跳转,补全了。

6. android-studio 的优势

  • 自动识别文件编码,用对应编码方式打开,识别错误的话,可以单独为某个文件设置编码。
  • 快速查找文件: ctrl + shilf + R, 可以模糊匹配搜索。
  • 查找当前文件的函数: ctrl + O, 可以默认匹配搜索。
  • search anything: 双击 shilf , 可以在工程中查找文件, 或者函数。当你知道一个函数名,但不知道它在哪个文件的时候, 用这个功能非常方便。
  • 查找函数引用:右键函数名, find usages
  • 查找函数定义:ctrl + 鼠标单击即可
  • 重构:函数名, 变量名上右击, refactor --> rename
  • 代码格式化:选中要格式化的代码 ctrl + shilf + f

总之 android-studio 的所有功能在这里都是可以使用的,你就把它当作一个 APK 工程就行。

0     0     0     0     0    
0 回帖