简介:这是大疆 Mobile-SDK-Android 的官方示例 DEMO,面向希望在 Android 端集成无人机控制能力的开发者,主要解决从 SDK 初始化、设备连接、状态获取到飞行控制与媒体采集的完整接入问题。资源共 829 个文件,核心包括 550 个 HTML 文档、80 个 Java 源文件和 61 个 XML 配置,同时附带 Gradle 构建脚本、PNG 图片、字体与样式表等辅助内容,压缩包整体约 10.96 MB,目录结构清晰,便于按文档、源码与资源配置对照学习。已有 228 人浏览学习。该示例覆盖无人机连接、状态回调、飞行控制、照片与视频拍摄等典型场景;读者可结合源码与 API 文档,理解 Android Studio 集成、权限声明、JNI/NDK 原生调用、蓝牙/WiFi 通信、多线程异步处理、实时视频流显示以及错误日志定位等关键技术点;同时可参考其工程结构与配置方式,快速梳理大疆 SDK 的开发流程。无论初学者搭建开发框架,还是中高级开发者查阅接口实现与调试细节,这套资源都颇具参考价值。
1. 从压缩包到可运行 Demo,先搞清这个 Android SDK 项目到底在做什么
拿到Mobile-SDK-Android-master_DEMO_android_这个项目名,第一反应是它同时包含了两层信息:Mobile-SDK-Android是 SDK 主体,DEMO_android是配套的示例工程。这类仓库在 GitHub 上非常常见,通常是把 SDK 源码、Gradle 配置、Demo app 和文档打在一个压缩包里。你需要做的不是把整个工程跑起来,而是搞清楚哪些模块是 SDK、哪些是 Demo,然后让 Demo 成功链接到 SDK 并运行在模拟器或真机上。否则你会陷入一个典型误区:直接 Open 整个根目录,Gradle 同步失败,NDK 版本不匹配,最后连错误日志都看不懂。
这篇文章会带你走一遍我处理这类项目的标准路径:先识别工程结构,再配置 Android SDK、NDK 和 Gradle 环境,接着编译 SDK 模块,最后让 Demo 跑起来并做基础联调。我会把sdk、ndk not configured、android sdk 官网下载、android studio 怎么设置中文这些高频搜索词对应的实际问题都覆盖到。如果你是第一次接触这类带原生代码的 SDK Demo,按下面的顺序操作,可以少踩一半的坑。
2. 工程结构拆解:先分清 SDK 模块和 Demo 模块,再决定怎么编译
2.1 从目录名识别模块边界
解压Mobile-SDK-Android-master_DEMO_android_之后,先不要急着用 Android Studio 打开。我一般会先在终端里看一眼目录树,确认仓库的顶层结构。常见布局有两种:一种是sdk/和demo/平级,另一种是android-sdk/子目录下再套demo/。用tree -L 2或find . -maxdepth 2 -type d都能快速看到全貌。
Mobile-SDK-Android-master/ ├── sdk/ # SDK 源码模块 │ ├── src/ │ ├── build.gradle │ └── proguard-rules.pro ├── demo/ # Demo app 模块 │ ├── src/ │ ├── build.gradle │ └── proguard-rules.pro ├── build.gradle # 根工程构建脚本 ├── settings.gradle └── gradle.properties这个布局说明 Demo 和 SDK 在同一个 Gradle 工程里,Demo 可以直接通过implementation project(':sdk')依赖 SDK 模块,这是最理想的状况。但另一种情况是 SDK 只提供.aar或.jar文件,放在libs/目录下,Demo 通过本地文件依赖。这两种方式在demo/build.gradle里表现完全不同,前者用project依赖,后者用files('libs/xxx.aar')或implementation fileTree(dir: 'libs', include: ['*.aar'])。
2.2 通过 settings.gradle 判断构建入口
settings.gradle文件决定了 Android Studio 会加载哪些模块。如果文件里只有:
include ':demo'说明 SDK 不参与当前构建,你需要单独处理 SDK 模块。如果写的是:
include ':sdk', ':demo'那整个工程就是一个完整的多模块 Gradle 项目,直接 Open 根目录就能识别。还有第三种情况:settings.gradle使用动态 include,比如遍历子目录自动加载模块,这种写法在从 master 分支下载的仓库里偶尔会出现。
project(':sdk')依赖的好处是修改 SDK 源码后 Demo 会即时重编,适合调试 SDK 本身;本地.aar依赖更接近真实发布场景,适合验证 SDK 的稳定性和接口兼容性。我建议你先按原仓库的依赖方式跑通一次,不要改构建配置。
2.3 识别 JNI 和 NDK 相关目录
项目名带Mobile,这类 SDK 往往包含 C/C++ 原生代码,src/main/jni/、src/main/cpp/或src/main/jniLibs/是重点排查对象。jniLibs里放的是预编译的.so文件,按 ABI 分目录(armeabi-v7a、arm64-v8a、x86、x86_64),这决定了你在什么架构的模拟器或真机上能跑。cpp目录则说明需要 NDK 参与编译。
find . -name "*.so" -o -name "CMakeLists.txt" -o -name "Android.mk" | head -20这条命令能快速定位所有原生代码相关文件。如果发现有CMakeLists.txt,工程用的是 CMake 构建系统,需要在app/build.gradle的externalNativeBuild里声明路径;如果只有Android.mk,则是老的 ndk-build 方式。看到.so文件但没有任何构建脚本,说明 SDK 已经预编译好原生库,你只需关注 ABI 匹配即可。
2.4 主工程的 build.gradle 往往藏着版本陷阱
打开根目录的build.gradle,重点看dependencies里的classpath 'com.android.tools.build:gradle:xxx'。这个版本必须和你的 Android Studio 版本兼容。比如 AGP 8.x 要求 Gradle 8.x 和 JDK 17,AGP 7.x 可能需要 JDK 11。如果仓库的 AGP 版本过老,Android Studio 会提示升级,但升级后可能引发其他连锁问题。我的经验是:若工程能识别且同步成功,就保持原版本;若同步失败,优先升级 AGP 和 Gradle wrapper,而非直接改compileSdkVersion。
gradle/wrapper/gradle-wrapper.properties里的distributionUrl是另一个关键点。有些网络环境下载 Gradle 发行包很慢,你可以手动改成国内镜像地址,比如https://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip。改完后再执行同步,速度会明显提升。
3. 环境配置三件套:Android SDK、NDK 和 Gradle JDK 的匹配原则
3.1 当前机器缺什么:用 SDK Manager 做一次基线检查
在 Windows 上常见报错是这样的:
NDK not configured. Download it with SDK manager. Preferred NDK version is 21.4.7075529这说明工程声明了ndkVersion,但本机没装对应版本。打开 Android Studio 的Tools -> SDK Manager,切到SDK Tools标签页,勾选NDK (Side by side)并选择工程要求的版本。下载完成后,SDK 管理器会自动把路径配置到local.properties。如果你是从android sdk 官网下载独立安装的 SDK,需要手动创建local.properties文件指定路径:
sdk.dir=D\:\\Android\\Sdk ndk.dir=D\:\\Android\\Sdk\\ndk\\21.4.7075529Windows 下路径要转义,正斜杠也可以直接用:sdk.dir=D:/Android/Sdk。注意ndk.dir在新版 Android Gradle Plugin 里已弃用,推荐用模块级build.gradle中的ndkVersion属性代替,这样更利于版本锁定。
3.2 三个 build.gradle 关键参数怎么对齐
无论工程多复杂,compileSdk、minSdk、targetSdk三者必须和你装的 SDK Platform 匹配。新版 AGP 推荐用compileSdk = 34这种写法,老版本则用compileSdkVersion 34。如果多个模块之间有依赖关系,所有模块的compileSdk应该一致,否则会出现 AAR 元数据冲突。一个典型的报错是:
Dependency ':sdk' requires core library desugaring or compileSdk 34这表示某个模块的compileSdk低于依赖方的要求。解决方法是统一所有模块的compileSdk和targetSdk,不要只在报错的模块里改。另外 AGP 8.0 开始,targetSdk低于 31 时某些系统权限行为会变化,如果 Demo 的目标版本过低,运行时申请权限的逻辑可能需要重写。
3.3 JDK 版本和 Gradle 版本怎么查怎么配
Gradle 版本决定它能在哪个 JDK 上跑。Gradle 8.5 要求 JDK 8 到 JDK 21 都能运行,但 AGP 8.2 强制要求 JDK 17。检查当前 JDK 版本:
java -version如果本机默认 JDK 是 8 或 11,需要在 Android Studio 的File -> Project Structure -> SDK Location里指定 JDK 17 的路径,或者在gradle.properties中设置:
org.gradle.java.home=C\:\\Program Files\\Java\\jdk-17这个配置只在命令行或 CI 环境下常用。在 Android Studio 里手动指定 JDK 路径更可控。若工程是用命令行./gradlew assembleDebug构建,JDK 版本会直接影响构建是否成功,通常报错会直接写明Unsupported class file major version,一看就知道是 JDK 太新或太旧。Gradle 版本查询用./gradlew --version,AGP 和 Gradle 的对应关系可以参考官方兼容性表,经验法则是 AGP 8.1 配 Gradle 8.0+,AGP 7.4 配 Gradle 7.5+。
3.4 使用命令行快速验证环境完整性
在 Android Studio 打开工程之前,我习惯先在终端里验证环境。写好local.properties后,执行:
./gradlew :demo:assembleDebug --stacktrace如果输出BUILD SUCCESSFUL,说明 SDK、NDK、Gradle、JDK 全部匹配,Android Studio 打开后大概率能直接跑。如果报错,--stacktrace能打出完整调用链,比 IDE 里显示的摘要信息有用得多。这里有个常见情况:Windows 下gradlew.bat和gradlew同时存在,PowerShell 里直接./gradlew可能报权限问题,需要用.\gradlew.bat调用。
4. Demo 从编译到安装:模块依赖、权限声明和首次运行排错
4.1 确认 Demo 对 SDK 的依赖方式
打开demo/build.gradle,看dependencies块。三种典型写法:
// 方式一:直接依赖 SDK 源码模块 implementation project(':sdk') // 方式二:依赖本地 aar 文件 implementation files('libs/mobile-sdk-release.aar') // 方式三:依赖本地 jar 文件 implementation files('libs/mobile-sdk.jar')方式一要确保settings.gradle里 include 了:sdk。方式二和方式三要确认libs目录下文件真实存在且文件名完全一致,大小写都不能错。有时候仓库把.aar放在demo/libs/下,但.gitignore忽略了*.aar,导致克隆后文件不存在,Gradle 同步时报Could not find mobile-sdk-release.aar。解决办法不是改文件名,而是回到 SDK 模块单独编译出.aar文件:
./gradlew :sdk:assembleRelease产物在sdk/build/outputs/aar/目录下,把它复制到demo/libs/再重新同步。
4.2 检查 AndroidManifest 的权限和组件声明
SDK 一般会在自己的AndroidManifest.xml里声明所需权限,但有些权限需要 Demo 主动声明。以常见的网络请求、文件读写和相机权限为例,检查demo/src/main/AndroidManifest.xml是否包含:
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>Android 6.0 以上,危险权限必须在运行时动态申请,仅声明是不够的。如果 Demo 里已经有一个MainActivity处理了权限回调,直接沿用即可;若没有,你需要自己补一段权限申请逻辑。
4.3 常见编译错误清单及对应处理
编译阶段出问题最多的是资源冲突、依赖冲突和 namespace 缺失。AGP 8.0 之后,每个模块必须在build.gradle里显式声明namespace,否则报错:
Namespace not specified. Specify a namespace in the module's build file.解决方式很简单,在sdk/build.gradle和demo/build.gradle的android块里补上:
android { namespace 'com.example.mobilesdk' // 替换成 SDK 实际包名 }如果项目中同时引用了多个版本相同的依赖库,会出现Duplicate class错误。在demo/build.gradle的dependencies块中,通过implementation关键字逐条排查并用./gradlew :demo:dependencies查看依赖树,找到重复项后用exclude或统一版本号解决。
4.4 模拟器与真机的 ABI 匹配问题
SDK 如果只提供了armeabi-v7a的.so,而你的模拟器是x86_64架构,运行时必然报java.lang.UnsatisfiedLinkError: dlopen failed或者library "xxx.so" not found。检查方式:
adb shell getprop ro.product.cpu.abi如果模拟器是x86_64而 SDK 没有x86_64目录,优先换 ARM 架构镜像的模拟器,或者直接插一台 ARM 架构的真机调试。另一种方案是在demo/build.gradle中开启兼容模式:
android { defaultConfig { ndk { abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64' } } }这样 Gradle 会尝试把所有 ABI 打进去,但如果 SDK 模块中的.so只存在于部分 ABI 目录,打包时可能会跳过缺失项,运行到对应方法时仍然会崩溃。最稳妥的办法始终是让真机和库的 ABI 保持一致。
4.5 首次运行的崩溃排查思路
SDK Demo 首次安装后闪退,先看adb logcat的输出。按优先级过滤:
adb logcat -v time *:E重点关注AndroidRuntime标签下的 FATAL EXCEPTION,里面会明确抛出异常类型和行号。最常见的几类:
UnsatisfiedLinkError:动态库未找到或 ABI 不匹配,按上一节处理ClassNotFoundException:SDK 模块未正确打包进 APK,检查build.gradle依赖方式SecurityException:权限未声明或未动态申请NetworkOnMainThreadException:网络请求写在了主线程,SDK 的回调可能没有处理好线程切换
SecurityException在 Android 8.0 以上高频出现,特别是读取设备标识符(IMEI)需要READ_PHONE_STATE权限,且部分系统版本禁止普通应用获取。若 SDK 依赖设备标识符且拿不到,考虑在gradle.properties里加android.useAndroidX=true并引入兼容库,但这不是万能解,遇到具体问题具体分析:有的 SDK 提供设置假设备 ID 的接口,有的只能换真机测试。
5. Gradle 同步慢和 NDK 下载失败,这两类问题的高效解法
5.1 替换仓库镜像,解决依赖下载卡住
工程同步时卡在Downloading https://dl.google.com/...是国内开发者最常见的问题。修改根目录build.gradle中的repositories和pluginManagement的仓库配置:
buildscript { repositories { maven { url 'https://maven.aliyun.com/repository/google' } maven { url 'https://maven.aliyun.com/repository/public' } mavenCentral() google() } }settings.gradle里的pluginManagement.repositories同样要加镜像地址。注意阿里云镜像的google仓库和public仓库覆盖了大部分 Android 依赖,但某些冷门库仍可能只在jcenter()或特定私有仓库中存在,若同步报找不到依赖,把镜像配置放回mavenCentral()或google()再试。顺序很重要:镜像仓库排在前面,官方仓库兜底。
5.2 NDK 版本精准安装,绕过 SDK Manager 的下载困难
SDK Manager 下载 NDK 失败时,可以手动从官网下载对应版本的压缩包。以21.4.7075529为例,在 Android Studio 的 SDK Manager 中显示的版本路径是sdk/ndk/21.4.7075529,目录名包含了完整版本号。下载后解压到该目录,并确认目录内直接包含source.properties文件,Gradle 才能识别。注意目录层级不能多包一层,正确路径应类似:
D:\Android\Sdk\ndk\21.4.7075529\source.properties如果解压后二级结构变成了D:\Android\Sdk\ndk\21.4.7075529\android-ndk-r21e\source.properties,Gradle 会报NDK not configured或Invalid NDK version。此时把内层目录里的全部内容上移一层即可。还有一种做法是直接把压缩包名字改成21.4.7075529.zip放到sdk/ndk/下,Android Studio 会自动识别并解压,但这个方式只在部分版本可用,手动解压更可控。
5.3 Gradle wrapper 版本调整与加速
某些老仓库的 Gradle wrapper 版本过低,与新版 Android Studio 不兼容。修改gradle/wrapper/gradle-wrapper.properties:
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zipbin版本不含源码和文档,体积比all版本小很多,日常构建完全够用。如果想要更快,把services.gradle.org换成腾讯镜像:
distributionUrl=https\://mirrors.cloud.tencent.com/gradle/gradle-8.7-bin.zip修改之后,Android Studio 会提示重新同步,或在终端执行./gradlew wrapper --gradle-version 8.7更新 wrapper。如果仓库里的 AGP 版本不兼容 Gradle 8.7,会出现:
Minimum supported Gradle version is 8.9. Current version is 8.7.这行提示已经说得很直白,按提示调整即可。先升 Gradle 再降 AGP 是常见的弯路:正确的顺序是看 AGP 要求的 Gradle 最低版本,再决定升哪个。
5.4 离线构建方案:依赖缓存与本地仓库
如果你频繁在无网环境构建,可以提前把依赖下载好,复制本机的 Gradle 缓存目录。默认缓存位置在:
- Windows:
C:\Users\<用户名>\.gradle\caches - macOS / Linux:
~/.gradle/caches
把这些目录整体拷贝到目标机器后,在gradle.properties中设置离线模式:
org.gradle.offline=true开启后 Gradle 只从本地缓存找依赖,不会发起网络请求。缺点是缓存缺失时无法下载,报错信息比较隐晦,一般是Could not resolve com.android.tools.build:gradle:8.1.0。这种情况下只能用有网环境补全缓存再切回离线模式。
6. 验证 SDK 功能是否正常:Demo 外的三种联调技巧
6.1 用 adb shell 直接调用 SDK 暴露的服务或接口
如果 SDK 内含独立进程的服务组件,可以先启动 Demo,再用adb shell查看服务和进程状态:
adb shell ps -A | grep com.example.mobilesdk adb shell dumpsys activity services | grep -i mobiledumpsys activity services能列出所有已注册服务,关键看是否包含 SDK 的 service 包名,以及是否处于started或bound状态。如果服务未启动,可能是 Demo 中的绑定逻辑只在特定时机触发。SDK 暴露的自定义权限无法直接通过dumpsys完整验证,但若 SDK 声明了带protectionLevel="signature"的权限,而 Demo 的签名与 SDK 不一致,服务绑定时会返回Permission Denial,logcat里会有一行明显的拒绝记录。
6.2 用 logcat 过滤 SDK 的日志标签
SDK 通常会打日志,但开发者未必都遵守统一的 TAG 命名。先用logcat观察所有日志,再按包名过滤:
adb logcat -v color --pid=$(adb shell pidof com.example.mobilesdk)这条命令在 Windows 的 CMD 下会因$()语法不兼容而失败,建议在 PowerShell 或 Git Bash 里执行。日志级别调整也很实用,SDK 的 debug 日志可能在 release 构建中默认关闭,如果需要在 debug 包下看到详细日志,可以在demo/build.gradle中设置:
buildTypes { debug { debuggable true } }6.3 修改 Demo 的入口代码,验证 SDK 初始化回调
大多数 SDK 都要求在 Application 或 MainActivity 中先初始化,再调用具体功能。如果 Demo 默认跑的是示例功能,你可以改入口逻辑,在MainActivity里加一段初始化检查:
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); MobileSDK.init(getApplicationContext(), "your_api_key", new SDKInitCallback() { @Override public void onSuccess() { Log.d("SDK_CHECK", "init success"); } @Override public void onError(int code, String message) { Log.e("SDK_CHECK", "init error: " + code + ", " + message); } }); } }onSuccess回调触发说明 SDK 的初始化链路、权限和底层库加载都正常;如果卡在onError,message里通常包含了失败原因,最常见的是 API Key 无效或网络不可达。SDK 的初始化结果与底层硬件强相关时,要确认设备支持。接口文档缺失时,SDK 的 Jar 包或源码注释是唯一可靠的依据,用javap反编译 class 文件查看公开方法:
javap -classpath sdk/build/intermediates/javac/release/classes com.example.mobilesdk.MobileSDK如果 SDK 是 Kotlin 编写的,javap输出会混入$Companion之类的额外类,但不影响查看主方法的签名和参数类型。通过源码注释或 AAR 内的api.txt,能反向还原出 SDK 的对外能力边界。
6.4 自定义 Gradle 任务做一键验证
重复的构建和安装操作可以封装成一个 Gradle 任务。在根目录的build.gradle里加一个:
task installAndRun(dependsOn: ':demo:installDebug') { doLast { exec { commandLine 'adb', 'shell', 'am', 'start', '-n', 'com.example.mobilesdk/.MainActivity' } } }执行./gradlew installAndRun,构建、安装、启动一步完成。参数调整时只需修改commandLine里的组件名或增加am start的--es附加参数。如果你的设备每次都要手动解锁,还可以在任务里加adb shell input keyevent 82发送菜单键唤醒屏幕,实测在自动化回归场景下能省掉大量无意义的等待时间。这种自定义任务本质上就是调adb,但它把验证流程固化成了可重复执行的构建步骤,比每次手动敲三条命令更不容易出错。
本文还有配套的精品资源,点击获取