前言

adb全称为Android Debug Bridge,译为安卓调试桥。是一个命令行工具,主要用于调试设备。adb.exe放置在/sdk/platform-tools目录下,可对设备进行各种操作,包括安装卸载应用、放置与拉取文件、shell交互等等。熟知adb的命令,对于应用调试与测试非常重要。

1.adb原理

adb是一种c(客户端)-s(服务端)架构的程序,包括三个组件客户端,守护进程(adbd),服务器。详细介绍可参考官方说明

下面是我对于adb的个人理解,首先要分清楚两个物理设备,输入adb命令的个人电脑,接收adb命令的Android设备,他们的角色分工如下:

  • 个人电脑:包含客户端与服务器,客户端就是adb.exe,用于开发者交互,服务器是在电脑中的一个后台进程,管理电脑与Android设备的通信。
  • Android设备:包含一个做为后台进程运行的守护进程adbd,作用是在设备上运行命令。

接下来再捋清楚两者通信过程:

  • 个人电脑中运行adb客户端并启动adb服务器进程,进程端口号为本地TCP端口号5037。
  • 电脑中的adb服务器会扫描所有连到电脑上安卓设备的端口号,从5555查找到5585之间的所有奇数号端口,一旦发现adb守护进程adbd便会与设备建立连接。
  • Android设备的奇数号端口用于adb连接,偶数号端口用于控制台连接,换句话说就是奇数号端口用于数据传输,偶数号端口用于命令传输。

最后,成功建立连接,开始用命令控制Android设备。

2.adb命令大全

类型命令说明
通用命令devices所有已连接设备连接
-help查看帮助
-version查看adb客户端版本号
网络connect HOST[:PORT]通过TCP/IP方式连接设备
-disconnect [[HOST]:PORT]断开某个或所有设备的连接
-forward --list列出所有转发的端口
-forward [–no-rebind] LOCAL REMOTE设备端口转发
-ppp TTY [PARAMETER…]通过USB进行点对点连接
-reverse – list列出设备的所有反向端口转发
-reverse [–no-rebind] REMOTE LOCAL操作反向端口转发
文件传输push [–sync] LOCAL… REMOTE本地文件复制到设备
-pull [-a] REMOTE… LOCAL设备文件复制到本地
-sync [all-data-odm-oem-product-system]同步文件
shellshell [-e ESCAPE][-n][-Tt][-x]远程运行脚本命令
-emu COMMAND运行模拟器命令
app安装install [-lrtsdg] [–instant] PACKAGE安装一个程序包
-install-multiple [-lrtsdpg] [–instant] PACKAGE…多个apk合成一个包安装
-install-multi-package [-lrtsdpg] [–instant] PACKAGE…安装多个程序包
-uninstall [-k] PACKAGE卸载应用包
调试bugreport [PATH]将错误报告写入指定的路径
-jdwp列出托管JDWP传输的进程的pid
-logcat查看设备的日志
安全disable-verity在userdebug版本上禁用dm-verity检查
-enable-verity在userdebug版本上打开dm-verity检查
-keygen FILE生成秘钥
脚本wait-for[-TRANSPORT]-STATE等待设备进入给定状态
-get-state获取状态
-get-serialno获取序列号
-get-devpath获取设备路径
-remount [-R]重新挂载文件系统
-reboot [bootloader-recovery]重启设备
-sideload OTAPACKAGE半加载给定的完整OTA软件包
-root以root权限重启adbd守护进程
-unroot用无root权限重启adbd守护进程
-usb通过USB重启adbd监听
-tcpip PORT通过TCP重启adbd监听
内部调试start-server开启adb客户端的服务器
-kill-server停止服务器
-reconnect主机端强制重连
-reconnect device设备端强制重连
-reconnect offline重置离线/未经授权的设备以强制重新连接
环境变量$ADB_TRACE以逗号分隔的调试信息列表,用于记录日志
-$ADB_VENDOR_KEYS以冒号分隔的键列表(文件或目录)
-$ANDROID_SERIAL要连接的设备序列号
-$ANDROID_LOG_TAGSlogcat打印的tag
-$ADB_LOCAL_TRANSPORT_MAX_PORT模拟器最大扫描端口

3.general commands:通用命令

3.1 devices

查看当前adb连接的所有设备,可选参数-l,输出如下信息。

  • 序列号
  • 连接状态
    • offline,设备未连接到 adb 或没有响应
    • device,设备现已连接到 adb 服务器
    • no device 未连接任何设备
  • 设备说明,-l选项输出的信息。
>adb devices -l
List of devices attached
1e0aa136   device product:natrium model:MI_5s_Plus device:natrium transport_id:1

  
  
  • 1
  • 2
  • 3

3.2 version

查看当前adb的版本号与安装路径。

>adb version
Android Debug Bridge version 1.0.41
Version 29.0.6-6198805
Installed as C:\android\sdk\platform-tools\adb.exe

  
  
  • 1
  • 2
  • 3
  • 4

3.3 help

帮助文档,可查看adb的所有命令用法。

4.networking:网络

4.1 connect HOST[:PORT] 与disconnect [[HOST]:PORT]

通过TCP/IP的方式连接设备。可通过此命令连接处于同一局域网下的android设备,只要电脑与手机连在同一上Wifi上就能使用此方式连接设备,需要知道手机设备的IP地址。

>adb connect 192.168.0.2
connected to 192.168.0.2:5555

>adb devices
List of devices attached
192.168.0.2:5555 device

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

当想断开某个设备的Adb连接时,可通过disconnect命令实现,若不带任何参数,则表示断开所有连接。

>adb disconnect
disconnected everything

>adb devices
List of devices attached

  • 1
  • 2
  • 3
  • 4
  • 5

小提示:手机可以打开热点让电脑连接,手机热点的默认IP一般是192.168.43.1。

>adb connect 192.168.43.1
* daemon not running; starting now at tcp:5037
* daemon started successfully
connected to 192.168.43.1:5555
>adb devices
List of devices attached
192.168.43.1:5555       device

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

4.2 forward --list

列出所有端口转发的socket连接,暂未明白如何使用。

5.文件传输:file transfer

5.1 push [–sync] LOCAL… REMOTE

将本地文件复制至设备中。

  • 可选参数sync,当与设备中有同名文件时只复制有新修改的文件
  • LOCAL… 本地文件目录,可同时复制多个文件
  • REMOTE 设备中放置复制文件的目录

示例如下:

C:\android>adb push D:\test\local.txt D:\test\local2.txt /sdcard/Download
D:\test\local.txt: 1 file pushed, 0 skipped. 0.0 MB/s (3 bytes in 0.028s)
D:\test\local2.txt: 1 file pushed, 0 skipped. 0.0 MB/s (3 bytes in 0.002s)
2 files pushed, 0 skipped. 0.0 MB/s (6 bytes in 0.043s)

C:\android>adb shell
//进入设备sdcard目录
generic_x86_arm:/ $ cd /sdcard/Download/
generic_x86_arm:/sdcard/Download $ ls -l
total 8
-rw-rw---- 1 root sdcard_rw 3 2020-04-11 07:15 local.txt
-rw-rw---- 1 root sdcard_rw 3 2020-04-11 07:15 local2.txt
//查看文件内容
generic_x86_arm:/sdcard/Download $ cat local.txt
abc

//使用–sync参数,只复制有修改的文件
C:\android>adb push --sync D:\test\local.txt D:\test\local2.txt /sdcard/Download
D:\test\local.txt: 0 files pushed, 1 skipped.
D:\test\local2.txt: 1 file pushed, 0 skipped. 0.0 MB/s (5 bytes in 0.007s)
//local.txt被跳过,local2.txt被复制,因为local2有经过修改
1 file pushed, 1 skipped. 0.0 MB/s (5 bytes in 0.032s)

generic_x86_arm:/sdcard/Download $ ls -l
total 8
-rw-rw---- 1 root sdcard_rw 3 2020-04-11 07:15 local.txt
-rw-rw---- 1 root sdcard_rw 5 2020-04-11 07:20 local2.txt //与上面对比修改时间有更新

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

复制整个目录到设备,直接传入文件的路径,文件下的所有内容都会被复制到设备中,包括自身目录。

C:\android>adb push D:\test /sdcard/Movies
D:\test\: 3 files pushed, 0 skipped. 0.0 MB/s (8 bytes in 0.020s)
//查看设备中复制的目录,可以看到test目录也被一起复制进了设备中
generic_x86_arm:/sdcard/Movies/test $ ls -l
total 12
-rw-rw---- 1 root sdcard_rw    3 2020-04-11 15:15 local.txt
-rw-rw---- 1 root sdcard_rw    5 2020-04-11 15:20 local2.txt
drwxrwx--x 2 root sdcard_rw 4096 2020-04-11 15:29 test2

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

复制目录下的所有文件到设备指定目录,不复制目录本身,只须在目录后加上一个.即可

C:\android>adb push D:\test\. /sdcard/Music
D:\test\.\: 3 files pushed, 0 skipped. 0.0 MB/s (8 bytes in 0.031s)
//test目录本身并没有被一起复制进来,只复制test目录下的所有文件
generic_x86_arm:/sdcard/Music $ ls -l
total 12
-rw-rw---- 1 root sdcard_rw    3 2020-04-11 15:15 local.txt
-rw-rw---- 1 root sdcard_rw    5 2020-04-11 15:20 local2.txt
drwxrwx--x 2 root sdcard_rw 4096 2020-04-11 15:41 test2

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

5.2 pull [-a] REMOTE… LOCAL

从设备中拉取文件到本地。

  • 可选参数-a,保留文件的时间戳和模式
  • REMOTE… 要拉取的设备文件,可同时摘取多个
  • LOCAL 放置设备文件的本地目录

示例:

//同时拉取两个不同目录的文件到本地目录中
C:\android>adb pull /sdcard/Download/local.txt /sdcard/Music/local2.txt D:/test/pull
/sdcard/Download/local.txt: 1 file pulled, 0 skipped. 0.0 MB/s (3 bytes in 0.006s)
/sdcard/Music/local2.txt: 1 file pulled, 0 skipped. 0.0 MB/s (5 bytes in 0.002s)
2 files pulled, 0 skipped. 0.0 MB/s (8 bytes in 0.022s)
//查看本地目录pull中的文件
C:\android>dir D:\test\pull
 D:\test\pull 的目录
//文件属性时间就是拉取时的时间点
2020/04/11  16:08                 3 local.txt
2020/04/11  16:08                 5 local2.txt
               2 个文件              8 字节
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

带参数-a拉取文件,并指定新的文件名

//带参数拉取文件,并指定文件名为local_a
C:\android>adb pull -a /sdcard/Download/local.txt D:\test\pull\local_a.txt
/sdcard/Download/local.txt: 1 file pulled, 0 skipped. 0.0 MB/s (3 bytes in 0.010s)

//查看文件
D:\test\pull>dir
D:\test\pull 的目录

2020/04/11 16:08 3 local.txt
2020/04/11 16:08 5 local2.txt
//local_a的时间还是设备中文件属性时间,即是保留了文件的时间戳
2020/04/11 15:15 3 local_a.txt
3 个文件 11 字节
2 个目录 205,634,908,160 可用字节

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

5.3 sync [all|data|odm|oem|product|system|system_ext|vendor]

同步一个设定好的本地目录到设备中,暂未研究明白如何使用。

6.shell

在设备中远程运行shell命令,会单独用一篇文章来介绍可在shell中运行的android命令。

7.app安装

7.1 install [-lrtsdg] [–instant] PACKAGE

向设备中安装单个应用

  • 参数l,前向锁定应用
  • 参数r,覆盖安装应用
  • 参数t,允许测试应用安装,一般是在as上直接点运行生成的apk
  • 参数s,在sdcard上安装应用程序
  • 参数d,允许应用降级安装,versioncode低于已安装的应用时可使用
  • 参数g,授予应用所有运行时权限
  • 参数–instant,作为临时应用安装

示例:

//安装apidemos.apk到设备中
>adb install  D:\test\ApiDemos.apk
Performing Streamed Install
Success
//再次安装会报错
Performing Streamed Install
adb: failed to install D:\test\ApiDemos.apk: Failure [INSTALL_FAILED_ALREADY_EXISTS: Attempt to re-install com.example.android.apis without first uninstalling.]
//加上参数-r可实现覆盖安装
>adb install -r  D:\test\ApiDemos.apk
Performing Streamed Install
Success

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
7.1.1 关于参数g是否能跳过应用运行时授权的探究

如果安装时加上参数-g是否代表着所有运行时权限都被自动获取了,下面用一个打开相机的案例进行测试。

正常安装,没有进行运行时授权,打开相机时会报错,提示程序没有CAMERA权限。

java.lang.SecurityException: Permission Denial: starting Intent { act=android.media.action.IMAGE_CAPTURE cmp=com.android.camera/.Camera } from ProcessRecord{4a11c78 6853:com.andrognito.pinlockviewapp/u0a356} (pid=6853, uid=10356) with revoked permission android.permission.CAMERA

 
 
  • 1

按下来用adb带-g参数进行安装。

>adb install -r -g D:\test\app-debug.apk
Performing Streamed Install
adb: failed to install D:\test\app-debug.apk: Security exception: You need the android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag

java.lang.SecurityException: You need the android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag
at com.android.server.pm.PackageInstallerService.createSessionInternal(PackageInstallerService.java:624)
at com.android.server.pm.PackageInstallerService.createSession(Package

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

安装时会报一个错,提示安全异常,需要另一个权限,android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS,下面把这个权限加到清单文件中再次安装。
在这里插入图片描述

>adb install -g D:\test\app-debug.apk
Performing Streamed Install
adb: failed to install D:\test\app-debug.apk: Security exception: You need the android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag

java.lang.SecurityException: You need the android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag
at com.android.server.pm.PackageInstallerService.createSessionInternal(PackageInstallerService.java:624)
at com.android.server.pm.PackageInstallerService.createSession(Package

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

依然还是报同样的错误,android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS是一个系统应用才能使用的权限,普通应用是没法获得此权限的,所以除非是系统应用,否则无法用adb install -g绕过应用运行时权限申请的限制。

7.1.2 直接运行as生成的APK为什么别人无法安装

有一种情况是在开发过程中直接在build里边输出的apk文件发给别人是无法安装的,提示的错误如下:

>adb install -r D:\test\test2\app-debug.apk
Performing Streamed Install
adb: failed to install D:\test\test2\app-debug.apk: Failure [INSTALL_FAILED_TEST_ONLY: installPackageLI]

 
 
  • 1
  • 2
  • 3

无法安装的原因在于build中生成的apk是测试专用的,在清单文件的application中多添加了一个属性android:testOnly="true",如下图所示:
在这里插入图片描述
所以这个应用在别的设备上是无法正常安装的,但可以通过adb添加-t参数进行安装。

>adb install -r -t  D:\test\test2\app-debug.apk
Performing Streamed Install
Success

 
 
  • 1
  • 2
  • 3

7.2 install-multiple [-lrtsdpg] [–instant] PACKAGE…

安装一个应用分包成的多个apk,适用于将一个完整的apk分成多个子apk一起安装的场景。

7.3 install-multi-package [-lrtsdpg] [–instant] PACKAGE…

一次性同时安装多个不同的apk。

>adb install-multi-package ApiDemos.apk app-debug.apk
adb: failed to create multi-package session
Exception occurred while executing:
java.lang.IllegalArgumentException: Unknown option --multi-package
        at com.android.server.pm.PackageManagerShellCommand.makeInstallParams(PackageManagerShellCommand.java:1229)
        at com.android.server.pm.PackageManagerShellCommand.runInstallCreate(PackageManagerShellCommand.java:273)
        at com.android.server.pm.PackageManagerShellCommand.onCommand(PackageManagerShellCommand.java:116)
        at android.os.ShellCommand.exec(ShellCommand.java:96)
        at com.android.server.pm.PackageMan
D:\test>

 
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

多应用安装一直报错,暂时未找到原因。

7.4 uninstall [-k] PACKAGE

卸载应用,参数-k表示保留应用的数据(/data/data/)与缓存(/sdcard/Android/包名).

>adb uninstall com.andrognito.pinlockviewapp
Success

 
 
  • 1
  • 2

8.调试debugging

8.1 bugreport [PATH]

抓取设备的运行日志到本地特定的路径中,默认名是bugreport.zip。
关于此工具的详细说明可参考此篇博客:https://blog.csdn.net/createchance/article/details/51954142

//生成日志数据抓取到本地D盘中的test目录
adb bugreport D:/test/ 
//默认生成的运行日志在设备上的存放目录如下所示
/data/user_de/0/com.android.shell/files/bugreports/bugreport-natrium-OPR1.170623.032-2020-05-17-15-44-12.zip 

 
 
  • 1
  • 2
  • 3
  • 4

在这里插入图片描述

8.2 jdwp

列出托管JDWP传输的进程的pid。

JDWP: Java Debug Wire Protocol 的缩写,它定义了调试器(debugger)和被调试的 Java 虚拟机(target vm)之间的通信协议,主要用以远程调试JVM。

8.3 logcat

查看设备的日志,与android studio上的logcat工具一样的作用。
可使用logcat --help查看更多的参数。

adb logcat *:E //打印等级为E以上的所有日志

 
 
  • 1

9.安全 security

9.1 disable-verity

在userdebug版本上禁用dm-verity检查
dm-verity是dm(device mapper)的一个target,是一个虚拟块设备,专门用于文件系统的校验

>adb disable-verity
disable-verity only works for userdebug builds
verity cannot be disabled/enabled - USER build

 
 
  • 1
  • 2
  • 3

只能在userdebug版本上使用此命令,android系统编译时可选三种模式

  • user(发行版本),adb默认关闭,无法调试,手机不能root
  • userdebug(调试版本),认打开adb功能,也能调试
  • eng(工程版本),最高权限root

官方详细解释

eng 
This is the default flavor. A plain make is the same as make eng.
*       Installs modules tagged with: eng, debug, user, and/or development.
*       Installs non-APK modules that have no tags specified.
*       Installs APKs according to the product definition files, in addition to tagged APKs.
*       ro.secure=0
*       ro.debuggable=1
*       ro.kernel.android.checkjni=1
*       adb is enabled by default.
*       Setupwizard is optional

user
This is the flavor intended to be the final release bits.

  •   Installs modules tagged with user.
    
  •   Installs non-APK modules that have no tags specified.
    
  •   Installs APKs according to the product definition files; tags are ignored for APK modules.
    
  •   ro.secure=1
    
  •   ro.debuggable=0
    
  •   adb is disabled by default.
    
  •   Enable dex pre-optimization for all TARGET projects in default to speed up device first boot-up
    

userdebug
The same as user, except:

  •   Also installs modules tagged with debug.
    
  •   ro.debuggable=1
    
  •   adb is enabled by default.
    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

9.2 enable-verity

在userdebug版本上重新启用dm-verity检查

9.3 keygen FILE

生成adb公钥/私钥; 存储在FILE中的私钥。

>adb keygen D:/test/a.key
adb I 05-17 16:19:17  8124  2280 auth.cpp:64] generate_key(D:/test/a.key)...

 
 
  • 1
  • 2

会在test目录下生成两个文件,一个是a.key,另一个是a.key.pub

10.脚本 scripting

10.1 wait-for[-TRANSPORT]-STATE…

等待设备进入指定的状态中。

STATE 设备状态
  • device, 正常连接设备
  • recovery, 设备复写状态
  • rescue, 系统救援状态
  • sideload, 数据线刷机模式
  • bootloader, 系统启动加载器
  • disconnect,断开连接状态
TRANSPORT 传输协议
  • usb,USB连接
  • local, 连接的端口
  • any [default=any],任意连接

10.2 get-state

获取连接设备的状态,

  • offline离线
  • bootloader加载系统
  • device在线
>adb get-state
device

 
 
  • 1
  • 2

10.3 get-serialno

获取连接设备的序列号

>adb get-serialno
1d0a2131

 
 
  • 1
  • 2

10.4 get-devpath

获取连接设备的路径

>adb get-devpath
unknown

 
 
  • 1
  • 2

10.5 remount [-R]

重新挂载设备,必须要有root权限才能使用

>adb remount
Not running as root. Try "adb root" first

 
 
  • 1
  • 2

10.6 root/unroot

获取root权限/取消root权限

10.7 reboot [bootloader|recovery|sideload|sideload-auto-reboot]

重启设备,可以选定重启的模式

  • bootloader,系统加载器
  • recovery,恢复出厂设备
  • sideload,线刷模式
  • sideload-auto-reboot

10.8 usb/ tcpip PORT

重启设备adbd的监听端口

11.内部调试 internal debugging

11.1 start-server/kill-server

启动adb服务/杀掉adb服务

11.2 reconnect

重新连接设备,可选参数

  • device,从设备侧断开连接以强制重新连接
  • offline ,重置离线/未经授权的设备以强制重新连接
>adb kill-server
//服务被干掉后,调用adb命令会重新启动服务
>adb devices
* daemon not running; starting now at tcp:5037
* daemon started successfully
List of devices attached
1d0a2131        offline

//设备是离线状态,进行重连
>adb reconnect
reconnecting 1d0a2131 [offline]

>adb devices
List of devices attached
1d0a2131 device

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

12.环境变量 environment variables

变量名说明
$ADB_TRACE以逗号分隔的调试信息列表,用于记录日志:
$ADB_VENDOR_KEYS以冒号分隔的键列表(文件或目录)
$ANDROID_SERIAL要连接的序列号(请参阅-s)
$ANDROID_LOG_TAGSlogcat使用的标签(请参阅logcat --help)
$ADB_LOCAL_TRANSPORT_MAX_PORT最大模拟器扫描端口(默认5585,16个emus)
$ADB_MDNS_AUTO_CONNECT逗号分隔的mdns服务列表,以允许自动连接(默认为adb-tls-connect)

13.通用选项

参数名说明
-a监听所有网络接口,而不仅是本地主机
-d使用USB设备
-e使用TCP/IP设备
-s SERIAL使用特定序列号对应的设备 (覆盖 $ANDROID_SERIAL)
-t ID使用特定tcp id对应的设备
-H服务端口名 [default=localhost]
-P服务端口号 [default=5037]
-L SOCKET在给定套接字上侦听adb服务器[default = tcp:localhost:5037]
Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐