android8.1源码从下载至本地到导入as可调试到编译源码并刷机到导入clion可调试一条龙

目录

前言

一:下载源码

二:导入代码到as

接下来配置as,使之可调试

三:编译源码

编译 aosp_x86_64-eng 版本

为了进一步检验我们编译产出的镜像是否可用

编译 nexus 6p ( aosp_angler-userdebug )版本

直接刷机

另外也可以使用线刷包刷机

四:导入native代码到clion

接下来是配置clion可调试native代码

其他


前言

提醒:南方用中科大源,北方用清华源

提前准备:
使用的vmware16,ubuntu18
安装python2
安装git,设置git用户名和邮箱
安装htop:sudo apt-get install htop -y,查看进程管理信息,命令行输入 htop 打开工具
安装openjdk1.8
安装as
安装clion

一:下载源码

1.在home下创建bin目录并添加到PATH环境变量中

mkdir ~/bin && vi ~/.bashrc

在末尾添加

export PATH=~/bin:$PATH

2.从中科大源下载repo工具,并赋予执行权限

curl -sSL  'https://gerrit-googlesource.proxy.ustclug.org/git-repo/+/master/repo?format=TEXT' |base64 -d > ~/bin/repo
chmod a+x ~/bin/repo

3.添加下载源到 ~/.bashrc,立即生效

echo "export REPO_URL='https://gerrit-googlesource.proxy.ustclug.org/git-repo'" >> ~/.bashrc
source ~/.bashrc

4.创建源码存放目录

mkdir ~/Desktop/MyAOSP && cd ~/Desktop/MyAOSP

 5.初始化 .repo 仓库有两种方式:一种是直接下载,另外一种是下载指定版本(不同的方式会影响后续的驱动下载(编译时))

        5.1 直接下载
                5.1.1
                repo init -u git://mirrors.ustc.edu.cn/aosp/platform/manifest 或者 
                从 https://mirrors.tuna.tsinghua.edu.cn/aosp-monthly/ 下载tar包
                。。。后续操作省略一万字。。。

        5.2 下载指定版本

                5.2.1 创建指定手机的指定版本目录

mkdir ~/Desktop/MyAOSP/nexus_6P_android-8.1.0_r52 && cd ~/Desktop/MyAOSP/nexus_6P_android-8.1.0_r52	

                5.2.2 下载.repo仓库,从 https://source.android.google.cn/setup/start/build-numbers

        选择你的手机支持的分支

repo init -u git://mirrors.ustc.edu.cn/aosp/platform/manifest -b android-8.1.0_r52 --depth=1

               --depth=1 表示只下载最近版本的代码,只保留最近的commit版本

                 5.2.3 同步当前分支并开启8线程同步

repo sync -j8 -c --no-tags --no-clone-bundle

                -c       表示只拉取当前分支代码

               --no-tags   表示不拉取tags

                --no-clone-bundle  表示不使用clone.bundle

                建议使用不死脚本:

1.在当前目录创建个 start_sync_cb.sh
vi start_sync_cb.sh

2.写入以下内容:
#!/bin/bash
echo “======= start repo sync =======”
repo sync -j8 -c --no-tags --no-clone-bundle
    while [ $? = 1 ]; do  
    echo “======sync failed, re-sync again======”  
    sleep 3  
    repo sync -j8 -c --no-tags --no-clone-bundle
done 

3.赋予执行权
chmod a+x start_sync_cb.sh

4.运行脚本,等待下载完成,完成之后有74.1个G
./start_sync_cb.sh

二:导入代码到as

1.cd 到源码根目录

cd ~/Desktop/MyAOSP/nexus_6P_android-8.1.0_r52

2.加载编译脚本

source build/envsetup.sh

2.1 另外看有些文章有写有些没写,都试了貌似对以后没啥影响,暂留
接着执行 lunch ,然后回车默认或者输入你想要的环境

3.编译idegen,会生成 out/host/linux-x86/framework/idegen.jar

如果不是第一次编译,清除之前编译的
make clean (make clobber)

make idegen  (有些文章写的使用 mmm development/tools/idegen)

4.执行idegen.sh脚本,之后会在源码根目录下会生成android.ipr(工程相关配置)和android.iml(模块相关配置)文件

./development/tools/idegen/idegen.sh

5.修改权限

sudo chmod 777 android.iml && sudo chmod 777 android.ipr

6.打开android.iml,搜索关键字"excludeFolder ",把不需要加载的模块添加到此处

sudo gedit android.iml 

添加以下内容,有些有重复没关系

<excludeFolder url="file://$MODULE_DIR$/.repo" />
<excludeFolder url="file://$MODULE_DIR$/art" />
<excludeFolder url="file://$MODULE_DIR$/bionic" />
<excludeFolder url="file://$MODULE_DIR$/bootable" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/compatibility" />
<excludeFolder url="file://$MODULE_DIR$/dalvik" />
<excludeFolder url="file://$MODULE_DIR$/cts" />
<excludeFolder url="file://$MODULE_DIR$/developers" />
<excludeFolder url="file://$MODULE_DIR$/development" />
<excludeFolder url="file://$MODULE_DIR$/device" />
<excludeFolder url="file://$MODULE_DIR$/docs" />
<excludeFolder url="file://$MODULE_DIR$/external" />
<excludeFolder url="file://$MODULE_DIR$/frameworks/base/docs" />
<excludeFolder url="file://$MODULE_DIR$/hardware" />
<excludeFolder url="file://$MODULE_DIR$/kernel" />
<excludeFolder url="file://$MODULE_DIR$/libcore" />
<excludeFolder url="file://$MODULE_DIR$/libnativehelper" />
<excludeFolder url="file://$MODULE_DIR$/out" />
<excludeFolder url="file://$MODULE_DIR$/pdk" />
<excludeFolder url="file://$MODULE_DIR$/platform_testing" />
<excludeFolder url="file://$MODULE_DIR$/prebuilts" />
<excludeFolder url="file://$MODULE_DIR$/sdk" />
<excludeFolder url="file://$MODULE_DIR$/system" />
<excludeFolder url="file://$MODULE_DIR$/test" />
<excludeFolder url="file://$MODULE_DIR$/toolchain" />
<excludeFolder url="file://$MODULE_DIR$/tools" />

6.1.当将源码导入AS后,在AS中导入或排除文件

点击 AS 的 File -> Project Structure –> Module –>Sources ,选择框中的文件,再点击 Mark as 中的 Sources是导入该文件夹,Excluded是排除该文件夹

7.打开as安装目录下的studio64.vmoptions修改:
-Xmx4096m

8. 打开as,点击File->Open,选择 android.ipr,等待加载完成即可

9.如提示:External file changes sync may be slow: The current inotify(7) watch limit is too low. More details. 解决如下:

    9.1
    vim /etc/sysctl.conf
    9.2.在最后一行添加代码
    fs.inotify.max_user_watches = 524288
    9.3.保存到只读文件
    :w !sudo tee %
    按 [O] ,之后按 [Enter],完成修改
    9.4.强制退出
    :q!
    9.5.
    cat /etc/sysctl.conf
    9.6.运行此命令将刚才的修改生效
    sudo sysctl -p
    9.7.重启 Android Studio

接下来配置as,使之可调试

10.

点击 AS 的 File -> Project Structure -> SDKs

11.

如果没下载一个和当前源码对应的SDK,则Setting -> Android SDK -> SDK Platform 下载对应的版本。

点击 + -> Add Android SDK..,选择SDK的安装目录,点击OK,选择 Java SDK 是 1.8(no libraries),Build target是Android API 30

12.

13.

14.

选择自己源码路径下的

/XXXX/frameworks/base/core/res/AndroidManifest.xml

/XXXX/frameworks/base/core/res/res

/XXXX/frameworks/base/core/res/assets

/XXXX/framework/native/libs

15.配置Run/Debug Configurations,添加一个Android App配置

16.

接入设备,点击 Attach Debugger to Android Process,点击show all processes,选择要调试的app,然后下断点,接着点击app执行相关的操作。

        16.1 断点调试分app启动后和app未启动时

统一在 android.app.Activity#attachBaseContext 的 super.attachBaseContext(newBase); 打断点

调试的app包名:com.my.test

        16.1.1 app启动后

                 1.启动app

                 2.附加进程到 com.my.test 

                 3.点击按钮跳转到其他界面,此时会停在断点处

        16.1.2 app未启动时

                1.以调试模式挂起app,两种命令二选一即可

使用这个命令后,需要手动点击该app,进入挂起界面
adb shell am set-debug-app -w com.my.test
使用这个命令后,无需手动点击,会自动进入挂起界面
adb shell am start -D -n com.my.test/.MainActivity

2.附加进程到 com.my.test ,附加完成后,会自动继续运行,直到触发断点

(也就是说这种调试可以调试app启动后的第一个界面)

三:编译源码

我这里是先编译的 aosp_x86_64-eng 版本之后再选择编译 nexus_6P 的版本

编译 aosp_x86_64-eng 版本

1.安装依赖库

sudo apt-get update
sudo apt-get install libx11-dev:i386 libreadline6-dev:i386 libgl1-mesa-dev g++-multilib
sudo apt-get install -y git flex bison gperf build-essential libncurses5-dev:i386
sudo apt-get install tofrodos python-markdown libxml2-utils xsltproc zlib1g-dev:i386
sudo apt-get install dpkg-dev libsdl1.2-dev libesd0-dev          
sudo apt-get install git-core gnupg flex bison gperf build-essential
sudo apt-get install zip curl zlib1g-dev gcc-multilib g++-multilib
sudo apt-get install libc6-dev-i386
sudo apt-get install lib32ncurses5-dev x11proto-core-dev libx11-dev
sudo apt-get install libgl1-mesa-dev libxml2-utils xsltproc unzip m4
sudo apt-get install lib32z-dev ccache
sudo apt-get install libssl-dev

我遇到的问题是:安装 libesd0-dev 时报错 Unable to locate package libesd0-dev

解决方案:

sudo vim /etc/apt/sources.list  
//在行尾添加如下两行的内容
deb http://us.archive.ubuntu.com/ubuntu/ xenial main universe
deb-src http://us.archive.ubuntu.com/ubuntu/ xenial main universe
然后
sudo apt-get update && sudo apt-get install libesd0-dev

2.设置相关配置

echo "export LC_ALL=C" >> ~/.bashrc
echo "export USE_CCACHE=1" >> ~/.bashrc
source ~/.bashrc
在源码目录执行以下命令,使用ccache编译(建议每次编译前手动设置一下)
cd ~/Desktop/MyAOSP/nexus_6P_android-8.1.0_r52 && prebuilts/misc/linux-x86/ccache/ccache -M 100G

3.进行编译

source build/envsetup.sh
lunch 6
make -j12

遇到的报错:

        1.报错如下:

/bin/bash -c "(prebuilts/sdk/tools/jack-admin install-server prebuilts/sdk/tools/jack-launcher.jar prebuilts/sdk/tools/jack-server-4.11.ALPHA.jar  2>&1 || (exit 0) ) && (JACK_SERVER_VM_ARGUMENTS=\"-Dfile.encoding=UTF-8 -XX:+TieredCompilation\" prebuilts/sdk/tools/jack-admin start-server 2>&1 || exit 0 ) && (prebuilts/sdk/tools/jack-admin update server prebuilts/sdk/tools/jack-server-4.11.ALPHA.jar 4.11.ALPHA 2>&1 || exit 0 ) && (prebuilts/sdk/tools/jack-admin update jack prebuilts/sdk/tools/jacks/jack-4.32.CANDIDATE.jar 4.32.CANDIDATE || exit 47 )"
Jack server already installed in "/home/hui/.jack-server"
Launching Jack server java -XX:MaxJavaStackTraceDepth=-1 -Djava.io.tmpdir=/tmp -Dfile.encoding=UTF-8 -XX:+TieredCompilation -cp /home/hui/.jack-server/launcher.jar com.android.jack.launcher.ServerLauncher
Jack server failed to (re)start, try 'jack-diagnose' or see Jack server log
No Jack server running. Try 'jack-admin start-server'
No Jack server running. Try 'jack-admin start-server'

        解决方案是 https://www.jianshu.com/p/62b82fb91b91,删除 ~/.jack-server文件夹 和 ~/.jack-settings文件,之后重新 make -j12

        2.报错如下:

SSL error when connecting to the Jack server. Try 'jack-diagnose'

解决方案是https://www.cnblogs.com/goolinli/p/14793289.html

        2.1 找到你的java安装目录下的java.security 

        2.2 打开java.security ,找到 jdk.tls.disabledAlgorithms=SSLv3, TLSv1, TLSv1.1, RC4这一行

        2.3 删掉 TLSv1, TLSv1.1,  保存

        2.4 在源码目录执行

prebuilts/sdk/tools/jack-admin kill-server
prebuilts/sdk/tools/jack-admin start-server
make -j12

4.编译成功,启动模拟器

emulator

注意一点:要在其他终端打开模拟器需要重新编译

source build/envsetup.sh
lunch 6
make -j12
emulator

遇到的报错:

        4.1 报错:CPU acceleration status: KVM requires a CPU that supports vmx or svm,解决如下:

 之后启动虚拟机,重新执行

source build/envsetup.sh
lunch 6 
make -j12

        4.2 报错: CPU acceleration status: This user doesn't have permissions to use KVM (/dev/kvm),解决如下:

sudo chmod -R 777 /dev/kvm

 之后执行

emulator

        4.3 成功启动模拟器后,打开设置app时崩溃,实际报错原因是 android.os.ServiceManager$ServiceNotFoundException: No service published for: wifip2p

解决如下:

打开源码,找到 packages/apps/Settings/src/com/android/settings/wfd/WifiDisplaySettings.java
在 WifiDisplaySettings.java 中,搜索 public static boolean isAvailable(Context context)
将返回值改为  return false

然后执行

make systemimage 

等待编译完成后,执行 emulator,打开设置app不再崩溃

为了进一步检验我们编译产出的镜像是否可用

1.在AS中配置一个模拟器,我安装的是一个8.1的x86_64 的模拟器

2.找到该镜像文件所在目录,我的在 /opt/Android/SDK/system-images/android-27/default/x86_64,可以看到 ramdisk.img、system.img、userdata.img、vendor.img

3.找到编译产生的镜像文件,我的在 ~/Desktop/MyAOSP/nexus_6P_android-8.1.0_r52/out/target/product/generic_x86_64 下,找到ramdisk.img、system-qemu.img、userdata-qemu.img、vendor-qemu.img 将它们复制到 2步骤中的目录下并修改它们的名字为system.img、userdata.img、vendor.img

4.启动这个模拟器,打开设置,找到关于设备,查看 Build number 是否与编译v启动的模拟器一致。

编译 nexus 6p ( aosp_angler-userdebug )版本

我是先编译的 aosp_x86_64-eng 版本,然后把native代码导入到clion,之后保存快照,再编译这个版本。

有两种编译:一种带驱动编译、一种不带驱动编译。

区别就是编译完成后多了一个 vendor.img

带驱动编译,编译后多一个 vendor.img,可直接连接设备,进行刷机。

不带驱动编译,编译后没有 vendor.img,需要下载与编译版本相对应的版本的线刷包,替换掉原包的img,然后刷进系统。

开始编译

1.由于之前编译过其他版本,所以需要先清空out目录

make clobber

2.下载驱动文件,并提取驱动

        上面提到过:直接下载和下载特定版本,不同的下载方式对应的驱动也不一样。

        直接下载的使用的是 master 分支,驱动程序需要在这里下载 :          

        https://developers.google.cn/android/blobs-preview      

        下载特定版本,驱动程序需要在这里下载 : 

        https://developers.google.com/android/drivers

        打开2个网址发现,只是一些特定设备代码下载方式不同时需要考虑从哪个网址下载驱动

我的是 nexus 6p,所以选择的是后者,需要下载与你编译版本号相同的驱动

 

点击Link,两个驱动都要下载

下载完成后,我们发现是个压缩包,解压后是个sh脚本,把2个脚本放到源码根目录下,我的是~/Desktop/MyAOSP/nexus_6P_android-8.1.0_r52

在根目录下执行脚本,使用 D 来向下翻页,直到最后手动输入 I ACCEPT。

之后会在根目录下多出一个vendor目录里面有 huawei 和 qcom 文件夹就代表已经提取成功。

3.开始编译

source build/envsetup.sh
lunch
输入28,回车(28 是 aosp_angler-userdebug )
make -j12

编译很顺利,等待了2个小时编译成功。

查看 out/target/product/angler 目录下已经有镜像了

直接刷机

1.启动一个终端,添加img环境变量

export ANDROID_PRODUCT_OUT=/home/xxxx/Desktop/MyAOSP/nexus_6P_android-8.1.0_r52/out/target/product/angler

使用你自己的输出目录

2.虚拟机连接到nexus 6p设备

使用

adb devices

来查看设备是否连上

3.重启刷机模式,进入fastboot模式

adb reboot bootloader      

使用

  fastboot devices

来查看设备是否连上

如果没有输出设备信息,请自行百度解决

4.执行刷机命令

fastboot flashall -w

正常情况下,等待一段时间,手机重启后,进入界面,查看设置中的版本号来确认,是否成功。

另外也可以使用线刷包刷机

1.从以下网站下载google亲儿子系列线刷包

https://developers.google.com/android/images

编译版本号和下载的版本号一定要对上

2.解压线刷包,里面有个zip,存放的是镜像,我们拿 out/target/product/angler 目录下的镜像替换掉原镜像。

3.win10连接到nexus 6p设备

使用

adb devices

来查看设备是否连上

4.重启刷机模式,进入fastboot模式

adb reboot bootloader      

使用

  fastboot devices

来查看设备是否连上

如果没有输出设备信息,请自行百度解决

4.进入到解压缩目录下,执行刷机命令

flash-all.bat

正常情况下,等待一段时间,手机重启后,进入界面,查看设置中的版本号来确认,是否成功

四:导入native代码到clion

1.在启动过模拟器的终端执行

export SOONG_GEN_CMAKEFILES=1
export SOONG_GEN_CMAKEFILES_DEBUG=1

2.编译,因为之前已经编译成功,并成功启动模拟器,所以这次编译速度很快

make -j12

3.编译完成后,会在 out/development/ide/clion/ 生成一堆目录,每个目录里都包含CMakeLists.txt

4.为了方便导入,我们需要合并模块,在 out/development/ide/clion 下新建一个总的CMakeLists.txt,如上图,然后先添加以下代码:

cmake_minimum_required(VERSION 3.5)
project(AOSP-NATIVE)

add_subdirectory(frameworks/native)

为什么不把所有的都加进去?

是为了更快的导入代码,再导入完毕后我们可以继续添加我们需要用到的部分

5.开始导入

        5.1 打开CLion
        5.2 选择「New CMake Project from Sources」
        5.3 选择目录 out/development/ide/clion
        5.4 选择「Open Existing Project」

        之后等待加载完毕

        加载如遇报错,修改后重新加载点击 Tools -> CMake -> Reload CMake Project

        5.5 加载完成后的目录结构是扁平的,需要修改工程根目录

        (我也不知道有没有用,有的文章这么写的)
             点击Tools -> CMake -> Change Project Root

        5.6 加载完成后,如果需要其他的native代码,在总的CMakeLists.txt中继续添加,

        例: add_subdirectory(system/core/init/libinit-x86_64-android)
        添加完后,点击 Reload changes

比较完整的导入代码如下

cmake_minimum_required(VERSION 3.5)
project(AOSP-NATIVE)

add_subdirectory(frameworks/native)
add_subdirectory(art/dalvikvm/dalvikvm-arm64-android)
add_subdirectory(art/libdexfile/libdexfile-arm64-android)
add_subdirectory(art/runtime/libart-arm64-android)
add_subdirectory(bionic/libc/libc_bionic-arm64-android)
add_subdirectory(bionic/libc/libc_bionic_ndk-arm64-android)
add_subdirectory(bionic/libc/system_properties/libsystemproperties-arm64-android)
add_subdirectory(external/compiler-rt/lib/sanitizer_common/libsan-arm64-android)
add_subdirectory(frameworks/av/media/libaaudio/src/libaaudio-arm64-android)
add_subdirectory(frameworks/av/soundtrigger/libsoundtrigger-arm64-android)
add_subdirectory(frameworks/base/core/jni/libandroid_runtime-arm64-android)
add_subdirectory(frameworks/native/cmds/installd/installd-arm64-android)
add_subdirectory(frameworks/native/cmds/servicemanager/servicemanager-arm64-android)
add_subdirectory(frameworks/native/libs/binder/libbinder-arm64-android)
add_subdirectory(libcore/libjavacore-arm64-android)
add_subdirectory(libcore/libopenjdk-arm64-android)
add_subdirectory(libnativehelper/libnativehelper-arm64-android)
add_subdirectory(libnativehelper/libnativehelper_compat_libc++-arm64-android)
add_subdirectory(system/core/base/libbase-arm64-android)
add_subdirectory(system/core/init/libinit-arm64-android)
add_subdirectory(system/core/libziparchive/libziparchive-arm64-android)
add_subdirectory(system/core/liblog/liblog-arm64-android)
add_subdirectory(system/core/libcutils/libcutils-arm64-android)
add_subdirectory(system/core/libutils/libutils-arm64-android)
add_subdirectory(system/core/libprocessgroup/libprocessgroup-arm64-android)
add_subdirectory(system/core/logcat/logcatd-arm64-android)
add_subdirectory(system/core/logcat/liblogcat-arm64-android)
add_subdirectory(system/core/logd/logd-arm64-android)
add_subdirectory(system/core/logd/liblogd-arm64-android)
add_subdirectory(system/core/lmkd/liblmkd_utils-arm64-android)
add_subdirectory(system/core/lmkd/lmkd-arm64-android)

注意你编译的版本,我编译的是 x86_64 的,编译后的那些目录都是

xxx/xxx/xxx-x86_64-android 这种格式的,所以在添加 add_subdirectory 时注意版本

接下来是配置clion可调试native代码

6.配置clion的 Run/Debug Configurations

那个 Symbol file 选择:out/target/product/generic_x86_64/symbols 下的 init,即out/target/product/generic_x86_64/symbols/init

7.启动一个模拟器,不管是编译后启动的模拟器,还是as安装的模拟器

8.安装一个app,我是用as直接创建的新工程的app

9.启动一个终端,以调试模式挂起app

adb shell am start -D -n com.example.myapplication/.MainActivity

用as查看进程号,下面需要用到

进程:3435

10.转发端口

adb forward tcp:1234 tcp:1234

11.gdbserver附加进程

adb shell gdbserver64 :1234 --attach 3435

adb shell "su 0 gdbserver64 :1234 --attach 3435"

执行之后如图:

12.点击clion右上角的debug

此时左下角出现提示

 并且

 此时表示连接成功

顺便在 dalvik_system_DexFile.cc 中的

dex_files = runtime->GetOatFileManager().OpenDexFilesFromOat(sourceName.c_str()

打个断点

13.回到as,右上角附加 com.example.myapplication ,点击ok

14.回到clion,此时已停在断点处

接下来可愉快的调试了。 

其他

android-6.0.1_r62、android-7.1.2_r28、android-8.1.0_r52    ----------Nexus 6P
android-8.1.0_r38、android-9.0.0_r46、android-10.0.0_r17    ----------Pixel 

Android Studio 不停 scanning files to index,解决:
1、invalidate and restart 不起作用;
2、右击项目 --> Open module setting --> Modules --> 找到 gen 文件夹 --> 右键选择 Resources

安装python2

mkdir ~/python2 && cd ~/python2
wget https://www.python.org/ftp/python/2.7.9/Python-2.7.9.tgz
tar -zxvf Python-2.7.9.tgz
cd Python-2.7.9
./configure --prefix=/usr/local/python-2.7.9
make
make install
sudo ln -sf /usr/local/python-2.7.9/bin/python /usr/bin/python
输入python,看是否进入python2.7.9 环境
rm -rf ~/python2

调试ART虚拟机遇到的问题(optimized out问题)

调试 zygote 进程

调试 zygote 进程

安卓代号、标记和细分版本号

google亲儿子系列线刷包

google亲儿子系列驱动

https://developers.google.com/android/drivers

https://developers.google.cn/android/blobs-preview

https://mirrors.tuna.tsinghua.edu.cn/aosp-monthly/

http://mirrors.ustc.edu.cn/help/aosp.html#id1

android 7.1 调试笔记 解决出现“There’s an internal problem with your device. Contact your manufacturer”

Android编译user版本提示There’s an internal problem with your device. Contact your manufacturer

解决刷机编译版联网无法自动校准时间

adb root
adb shell setprop persist.sys.timezone Asia/Shanghai
adb shell settings put global ntp_server ntp1.aliyun.com
adb reboot

转载请注明出处

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值