# 应用基础 ***Copyright © Quectel Wireless Solutions Co., Ltd. 2026. All rights reserved.*** --- # 引言 本章节为基于嵌入式硬件平台(以Quectel Pi智能主控板系列为例)的Android应用开发者提供全流程开发指导,涵盖开发环境搭建、调试工具使用、硬件相关API调用以及应用部署启动的核心操作。旨在帮助开发者快速入门嵌入式Android开发,解决从环境配置到应用落地的基础问题,同时适配嵌入式硬件的特殊开发需求,为后续复杂功能开发奠定技术基础。 # 开发环境 ## 开发工具准备 ### Android Studio下载与安装 - **官方下载地址**:[Android Studio官网]() - **安装流程** - 依据操作系统(Windows/macOS/Linux)下载对应安装包; - Windows系统:双击 `.exe` 安装包启动向导; - macOS系统:挂载 `.dmg` 镜像,将 Android Studio 拖拽至应用程序文件夹; - Linux系统:解压 `.tar.gz` 包至 `/opt` 目录,执行 `/opt/android-studio/bin/studio.sh` 启动安装。 - 勾选“**Android Studio**”和“**Android SDK**”核心组件,选择非系统盘(建议预留≥20 GB 空间)作为安装路径,完成基础安装; - 首次启动选择“**Standard**”标准配置,等待SDK及工具链自动下载完成。 ### 嵌入式硬件驱动配置 针对Quectel Pi系列硬件,需额外完成驱动配置: - Windows系统 请下载Quectel官方硬件驱动包,双击安装程序并按向导完成配置。 - Linux或macOS系统 系统通常已内置通用USB驱动,硬件连接后可被自动识别,一般无需手动安装额外驱动。 - 验证驱动:连接硬件至电脑,在Windows设备管理器或Linux终端执行 `lsusb` 命令,若能识别到相应设备,则表示驱动配置成功。 ### NDK配置(用于底层硬件API调用) - 打开Android Studio,进入 `File > Settings > Appearance & Behavior > System Settings > Android SDK`,切换至 `SDK Tools` 标签页。 - 勾选 `Show Package Details`、`NDK (Side by side)` 和 `CMake`,选择对应版本后点击“**Apply**”完成下载安装。 ```{image} images/image_Kqgubeo4PoQeiVxJvrGcc0jTnvf.webp :width: 1270px :height: 515px ``` - 在项目 `local.properties` 文件中配置NDK路径: ```plaintext ndk.dir=SDK安装路径/ndk/对应版本号 ``` ## 项目环境初始化 - 新建Android项目,选择“**Empty Activity**”模板,配置项目名称和包名,最低兼容Android版本建议≥Android 10(以适配嵌入式硬件)。 - 在 `build.gradle`(Module 级)文件中导入Quectel硬件SDK等相关依赖包(此步骤可选)。 # APK开发 - **启动Android Studio**:点击"New Project"新建项目。选择项目模板时,建议选择最简洁的 **Empty Views Activity** 模板。 - ***注意:若选择"Empty Activity"模板,生成的将是Compose UI项目。*** ```{image} images/image_HVnUbyhNHoX6k2xYFr8cDrApnQc.webp :width: 762px :height: 294px ``` ```{image} images/image_WXfCbqQz4oJn7WxpBMScyxyfn5b.webp :width: 888px :height: 636px ``` - **选择语言**:在此步骤选择开发语言。即使选择了Kotlin,项目中仍然可以使用Java进行开发,Android项目完全支持Java和Kotlin混合开发。 ```{image} images/image_Ca9BbZyu5oyxRXxtgKRcEatYnmf.webp :width: 870px :height: 470px ``` - **下载依赖与SDK**:完成以上步骤后,Android Studio将自动打开项目并下载Gradle及所需依赖。首次启动若提示“缺少SDK”,点击“Next”即可自动下载Android SDK。请确认SDK安装路径并等待下载完成,构建时长取决于网络状况。 - **构建与运行**:Gradle构建完成后进入主界面,系统已生成基础演示代码。若无报错且顶部绿色“运行”按钮可用,说明构建正常。点击该按钮即可编译项目并启动模拟器。此外,也可以使用 `adb connect` 命令连接真机运行。 ```{image} images/image_CCS2b7veIoB5xixa52KcxILWnXd.webp :width: 1271px :height: 315px ``` - **运行成功**:此时应用已成功运行在模拟器上,显示简单的欢迎界面,即可开始开发。 ```{image} images/image_RMlYbS3kjoyhsfxUQdrcGlSun74.webp :width: 1280px :height: 626px ``` ## 基础API示例 ### GPIO控制API(嵌入式硬件核心功能) #### GPIO简介 GPIO(General-Purpose Input/Output,通用输入/输出)是嵌入式系统和硬件开发中的一个基础且至关重要的概念。在Android设备上,它充当了系统与外部物理世界交互的“桥梁”,嵌入式Android的GPIO控制需结合硬件驱动,以下为基于系统文件操作的基础示例(以GPIO 12为例)。 #### 权限配置 在 `AndroidManifest.xml` 中添加硬件访问权限: ```plaintext ``` #### java代码实现 通过java代码读写/sys/class/gpio下的文件通常需要root权限。因此,在常规的Android手机上进行这些操作非常困难,该方式通常仅适用于已获得root权限的设备或专为开发者设计的开发板。 ```plaintext import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; public class GpioManager { //GPIO操作基础路径 private static final String GPIO\_BASE\_PATH = "/sys/class/gpio/"; private final int gpioNum; public GpioManager(int gpioNum) { this.gpioNum = gpioNum; exportGpio(); } private void exportGpio() { try { File exportFile = new File(GPIO\_BASE\_PATH + "export"); BufferedWriter writer = new BufferedWriter(new FileWriter(exportFile)); writer.write(String.valueOf(gpioNum)); writer.close(); //默认设置为输出模式 setGpioDirection("out"); } catch (IOException e) { e.printStackTrace(); } } /\*\* \* 设置GPIO方向(in/out) \* @param direction方向参数 \*/ public void setGpioDirection(String direction) { try { File dirFile = new File(GPIO\_BASE\_PATH + "gpio" + gpioNum + "/direction"); BufferedWriter writer = new BufferedWriter(new FileWriter(dirFile)); writer.write(direction); writer.close(); } catch (IOException e) { e.printStackTrace(); } } /\*\* \* 控制GPIO电平(高电平1/低电平0) \* @param value电平值 \*/ public void setGpioValue(int value) { try { File valueFile = new File(GPIO\_BASE\_PATH + "gpio" + gpioNum + "/value"); BufferedWriter writer = new BufferedWriter(new FileWriter(valueFile)); writer.write(String.valueOf(value)); writer.close(); } catch (IOException e) { e.printStackTrace(); } } /\*\* \* 释放GPIO引脚 \*/ public void unexportGpio() { try { File unexportFile = new File(GPIO\_BASE\_PATH + "unexport"); BufferedWriter writer = new BufferedWriter(new FileWriter(unexportFile)); writer.write(String.valueOf(gpioNum)); writer.close(); } catch (IOException e) { e.printStackTrace(); } } } ``` #### 调用示例 ```plaintext //初始化GPIO 12引脚 GpioManager gpioManager = new GpioManager(12); //设置GPIO为高电平 gpioManager.setGpioValue(1); //业务逻辑执行完毕后释放GPIO gpioManager.unexportGpio(); ``` ### NDK代码实现 由于普通应用的Java/Kotlin代码运行在Android应用层,无法直接操作硬件,因此需通过JNI(Java Native Interface)进行桥接。调用C/C++编写的Native层代码实现GPIO控制(读写/sys/class/gpio文件或直接操作/dev/mem寄存器),并通过NDK将C/C++代码编译为.so动态链接库供Java层调用。 #### JNI层代码示例 ```plaintext #include #include #include #include #include #define GPIO_BASE_PATH "/sys/class/gpio/" //导出GPIO引脚 static int gpio_export(int pin) { char buffer[64]; int fd = open(GPIO_BASE_PATH "export", O_WRONLY); if (fd < 0) { perror("Failed to open export file"); return -1; } snprintf(buffer, sizeof(buffer), "%d", pin); write(fd, buffer, strlen(buffer)); close(fd); return 0; } //释放GPIO引脚 static int gpio_unexport(int pin) { char buffer[64]; int fd = open(GPIO_BASE_PATH "unexport", O_WRONLY); if (fd < 0) { perror("Failed to open unexport file"); return -1; } snprintf(buffer, sizeof(buffer), "%d", pin); write(fd, buffer, strlen(buffer)); close(fd); return 0; } //设置GPIO方向(in/out) static int gpio_set_direction(int pin, const char *dir) { char buffer[64]; snprintf(buffer, sizeof(buffer), GPIO_BASE_PATH "gpio%d/direction", pin); int fd = open(buffer, O_WRONLY); if (fd < 0) { perror("Failed to open direction file"); return -1; } write(fd, dir, strlen(dir)); close(fd); return 0; } //设置GPIO电平(1/0) static int gpio_set_value(int pin, int value) { char buffer[64]; snprintf(buffer, sizeof(buffer), GPIO_BASE_PATH "gpio%d/value", pin); int fd = open(buffer, O_WRONLY); if (fd < 0) { perror("Failed to open value file"); return -1; } char val_str[2] = {value + '0', '\0'}; write(fd, val_str, strlen(val_str)); close(fd); return 0; } //JNI方法:初始化GPIO JNIEXPORT jint JNICALL Java_com_example_gpiocontrol_MainActivity_gpioInit(JNIEnv *env, jobject thiz, jint pin) { if (gpio_export(pin) < 0) return -1; if (gpio_set_direction(pin, "out") < 0) return -1; return 0; } //JNI方法:设置GPIO电平 JNIEXPORT jint JNICALL Java_com_example_gpiocontrol_MainActivity_gpioSetValue(JNIEnv *env, jobject thiz, jint pin, jint value) { return gpio_set_value(pin, value); } //JNI方法:释放GPIO JNIEXPORT jint JNICALL Java_com_example_gpiocontrol_MainActivity_gpioRelease(JNIEnv *env, jobject thiz, jint pin) { return gpio_unexport(pin); } ``` #### CMakeLists配置 ```plaintext cmake_minimum_required(VERSION 3.22.1) project("gpiocontrol") # 添加共享库 add_library( gpiocontrol SHARED gpio_control.c) # 链接系统库 target_link_libraries( gpiocontrol log) ``` #### java层代码调用 ```plaintext package com.example.gpiocontrol; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; public class MainActivity extends AppCompatActivity { //加载Native库 static { System.loadLibrary("gpiocontrol"); } //声明native方法 private native int gpioInit(int pin); private native int gpioSetValue(int pin, int value); private native int gpioRelease(int pin); private static final int GPIO_PIN = 12; //控制的GPIO引脚 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //初始化GPIO gpioInit(GPIO_PIN); //高电平按钮 Button btnHigh = findViewById(R.id.btn_high); btnHigh.setOnClickListener(v -> gpioSetValue(GPIO_PIN, 1)); //低电平按钮 Button btnLow = findViewById(R.id.btn_low); btnLow.setOnClickListener(v -> gpioSetValue(GPIO_PIN, 0)); } @Override protected void onDestroy() { super.onDestroy(); //释放GPIO gpioRelease(GPIO_PIN); } } ``` #### Android App直接调用Shell(Runtime.exec) 在Java/Kotlin代码中通过Runtime.getRuntime().exec()执行Shell命令,以su权限读写/sys/class/gpio目录下的GPIO控制文件,实现电平控制。此方式需开发板具备Root权限。 ##### 核心代码示例 ```plaintext //初始化GPIO(导出引脚+设置输出方向) try { execShellCommand("su -c echo " + GPIO_PIN + " > /sys/class/gpio/export"); execShellCommand("su -c echo out > /sys/class/gpio/gpio" + GPIO_PIN + "/direction"); } catch (IOException e) { e.printStackTrace(); } //高电平按钮 Button btnHigh = findViewById(R.id.btn_high); btnHigh.setOnClickListener(v -> { try { execShellCommand("su -c echo 1 > /sys/class/gpio/gpio" + GPIO_PIN + "/value"); } catch (IOException e) { e.printStackTrace(); } }); //低电平按钮 Button btnLow = findViewById(R.id.btn_low); btnLow.setOnClickListener(v -> { try { execShellCommand("su -c echo 0 > /sys/class/gpio/gpio" + GPIO_PIN + "/value"); } catch (IOException e) { e.printStackTrace(); } }); /** * 执行Shell命令 */ private void execShellCommand(String command) throws IOException { Runtime.getRuntime().exec(command); } ``` ### 厂商提供的专用API或Jar包 多数厂商会基于底层驱动封装标准化的Java API(以Jar包或SDK形式提供)。开发者可从厂商处获取相应的SDK,将其集成到Android应用中,即可直接调用封装好的API来控制GPIO,无需关注底层硬件操作的具体实现。 ## 摄像头API(基于Camera2原生API) 以下为基于Android原生Camera2 API的实现示例,适配嵌入式设备单摄像头场景,支持预览和拍照功能。 ### 依赖配置 Camera2为Android原生API,无需导入第三方依赖。仅需在 `build.gradle`(Module 级)中确认最低兼容版本不低于Android 5.0(API 21)。 ### 权限与布局配置 - **权限声明**(AndroidManifest.xml): ```plaintext <-- 摄像头硬件特性 --> ``` - **布局文件**(activity_camera2.xml): 采用 `TextureView` 作为预览载体,适配嵌入式设备屏幕尺寸: ```plaintext