
简介这是大疆 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 17AGP 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.dirD\:\\Android\\Sdk ndk.dirD\:\\Android\\Sdk\\ndk\\21.4.7075529Windows 下路径要转义正斜杠也可以直接用sdk.dirD:/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.homeC\:\\Program Files\\Java\\jdk-17这个配置只在命令行或 CI 环境下常用。在 Android Studio 里手动指定 JDK 路径更可控。若工程是用命令行./gradlew assembleDebug构建JDK 版本会直接影响构建是否成功通常报错会直接写明Unsupported class file major version一看就知道是 JDK 太新或太旧。Gradle 版本查询用./gradlew --versionAGP 和 Gradle 的对应关系可以参考官方兼容性表经验法则是 AGP 8.1 配 Gradle 8.0AGP 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:nameandroid.permission.INTERNET/ uses-permission android:nameandroid.permission.ACCESS_NETWORK_STATE/ uses-permission android:nameandroid.permission.CAMERA/ uses-permission android:nameandroid.permission.WRITE_EXTERNAL_STORAGE/ uses-permission android:nameandroid.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 modules 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 不匹配按上一节处理ClassNotFoundExceptionSDK 模块未正确打包进 APK检查build.gradle依赖方式SecurityException权限未声明或未动态申请NetworkOnMainThreadException网络请求写在了主线程SDK 的回调可能没有处理好线程切换SecurityException在 Android 8.0 以上高频出现特别是读取设备标识符IMEI需要READ_PHONE_STATE权限且部分系统版本禁止普通应用获取。若 SDK 依赖设备标识符且拿不到考虑在gradle.properties里加android.useAndroidXtrue并引入兼容库但这不是万能解遇到具体问题具体分析有的 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.propertiesGradle 会报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.propertiesdistributionUrlhttps\://services.gradle.org/distributions/gradle-8.7-bin.zipbin版本不含源码和文档体积比all版本小很多日常构建完全够用。如果想要更快把services.gradle.org换成腾讯镜像distributionUrlhttps\://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 缓存目录。默认缓存位置在WindowsC:\Users\用户名\.gradle\cachesmacOS / Linux~/.gradle/caches把这些目录整体拷贝到目标机器后在gradle.properties中设置离线模式org.gradle.offlinetrue开启后 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 声明了带protectionLevelsignature的权限而 Demo 的签名与 SDK 不一致服务绑定时会返回Permission Deniallogcat里会有一行明显的拒绝记录。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 的初始化链路、权限和底层库加载都正常如果卡在onErrormessage里通常包含了失败原因最常见的是 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但它把验证流程固化成了可重复执行的构建步骤比每次手动敲三条命令更不容易出错。本文还有配套的精品资源点击获取