A
第 2 章JAVA30 分钟

第一个 Hello World 应用

新建 Empty Views Activity 项目、运行到模拟器、目录结构初探、修改 TextView 文案并构建可安装 APK

学习目标

  • 使用 Android Studio 模板新建 Empty Views Activity Java 项目并配置 SDK 版本
  • 把应用运行到模拟器并通过 Logcat 查看日志
  • 初步认识 app 模块的目录结构(manifest / java / res)
  • 修改 activity_main.xml 中 TextView 的文案并重新运行
  • 用 Gradle 命令行构建一个 release/debug APK

学习目标

第 1 章我们搞定了环境,本章把环境用起来:从零创建一个 Java 版的 Hello World,让它跑在模拟器上,并理解“一个 Android App 是怎么从源码变成屏幕上的文字”。具体目标:

  • 用 Empty Views Activity 模板新建一个 Java 项目并配置 compileSdk 34、minSdk 24;
  • 通过 Run 把应用安装到模拟器,并在 Logcat 验证进程已启动;
  • 初步认识 app/src/main 下的三大目录:java/、res/、AndroidManifest.xml;
  • 修改 activity_main.xml 中 TextView 的文案,观察 UI 变化;
  • 用 Gradle 命令行产出 debug APK 并查看其产物。

新建 Empty Views Activity 项目

1. 模板选择

Android Studio Hedgehog 之后模板被重新组织。我们选用 Empty Views Activity,它生成的是经典 View 体系项目,而非 Jetpack Compose。理由:本教程前半部分用 View + Java 讲解基础,Compose 留到 Part 9。

操作步骤:

  1. Android Studio 主界面 New Project;
  2. 在 Phone and Tablet 分类下选 Empty Views Activity;
  3. 弹出配置窗口,按下表填写:
字段 值 说明
Name HelloApp 应用名
Package name com.example.helloapp 全小写,至少两段
Save location D:\projects\HelloApp 路径不含中文/空格
Language Java 本教程主语言
Minimum SDK API 24 (Android 7.0) 覆盖 95%+ 设备
Build configuration language Groovy DSL (build.gradle) 用 .gradle 而非 .gradle.kts
Use legacy android.support libraries 不勾选 用 AndroidX
  1. 点击 Finish,等待 Gradle 同步完成(首次 5-15 分钟)。

2. 模板生成的 build.gradle(app 级)

新建完成后,app/build.gradle 长这样(节选):

plugins {
    id 'com.android.application'
}

android {
    namespace 'com.example.helloapp'
    compileSdk 34

    defaultConfig {
        applicationId "com.example.helloapp"
        minSdk 24
        targetSdk 34
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
}

dependencies {
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'com.google.android.material:material:1.11.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
}

注意:

  • namespace 决定生成的 R 类包名,必须与 applicationId 同源,但两者不必完全相同;
  • compileSdk 与 targetSdk 都设为 34,minSdk 24,对应本教程约定;
  • sourceCompatibility 与 targetCompatibility 都升到 17,与第 1 章的 JDK 17 对齐。

运行到模拟器

1. 选择运行设备

顶部工具栏右侧的下拉框应能自动识别第 1 章创建的 Pixel_7_API_34 AVD。如果没显示,重启 Android Studio 或手动点击 Device Manager → Refresh。

2. 点击 Run

点绿色 ▶ 按钮,Android Studio 会执行:

  1. Build:调用 Gradle 执行 assembleDebug,编译 Java、处理资源、生成 R.java、合并 manifest;
  2. Install:通过 adb install 把 app-debug.apk 安装到模拟器;
  3. Start:发出 am start -n com.example.helloapp/.MainActivity 启动主 Activity。

构建过程输出在底部 Build 标签:

> Task :app:preBuild UP-TO-DATE
> Task :app:preDebugBuildConfig UP-TO-DATE
> Task :app:compileDebugJavaWithJavac
> Task :app:processDebugResources
> Task :app:packageDebug
> Task :app:installDebug
Installed on 1 device.

模拟器屏幕出现空白页面,正中央显示 Hello, World!。

3. 用 Logcat 验证进程

底部 Logcat 标签,左侧选设备 Pixel_7_API_34,过滤栏输入 package:com.example.helloapp。能看到类似输出:

2024-01-01 10:00:01.123  1234-1234  ActivityTaskManager I ... START com.example.helloapp/.MainActivity ...
2024-01-01 10:00:01.200  1234-1234  helloapp               D ... Loaded apk ...

如果要自己加日志,可以在 MainActivity.java 里写:

package com.example.helloapp;

import android.os.Bundle;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "HelloApp";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d(TAG, "onCreate: 应用启动");
    }
}

按 Ctrl + F10(Windows)重新 Run,Logcat 过滤 tag:HelloApp 能看到 onCreate: 应用启动。

4. 真机运行(可选)

如果你有真机:

  1. 手机 → 设置 → 关于手机 → 连续点击 7 次“版本号”启用开发者选项;
  2. 设置 → 系统 → 开发者选项 → 开启 USB 调试;
  3. 用 USB 连接电脑,弹窗选 传输文件,授权指纹;
  4. Android Studio 顶部下拉框选你的手机型号,再点 Run。

部分国产 ROM(如 MIUI、ColorOS)需要在开发者选项里额外开启 USB 安装 与 无线调试 权限,否则首次安装 APK 会被拦截。

目录结构初探

新建项目后,左侧 Project 视图默认用 Android 模式展示。切到 Project 模式(右上角下拉)能看到真实目录树:

HelloApp/
├── .gradle/                       # Gradle 缓存
├── .idea/                         # IDE 配置
├── app/
│   ├── build.gradle               # 模块级构建脚本
│   ├── proguard-rules.pro         # 代码混淆规则
│   └── src/
│       └── main/
│           ├── AndroidManifest.xml
│           ├── java/com/example/helloapp/
│           │   └── MainActivity.java
│           └── res/
│               ├── drawable/      # 矢量图
│               ├── layout/
│               │   └── activity_main.xml
│               ├── mipmap-anydpi-v26/
│               │   └── ic_launcher.xml      # 自适应图标
│               ├── mipmap-hdpi/
│               │   └── ic_launcher.png      # 普通图标
│               ├── values/
│               │   ├── colors.xml
│               │   ├── strings.xml
│               │   └── themes.xml
│               └── xml/
│                   ├── backup_rules.xml
│                   └── data_extraction_rules.xml
├── build.gradle                   # 项目级脚本
├── settings.gradle                # 模块聚合与仓库
├── gradle.properties              # Gradle 与 AndroidX 配置
└── local.properties               # 本机 SDK 路径,不入库

1. AndroidManifest.xml

清单文件,向系统声明应用基本信息(包名、版本、组件、权限等)。模板生成的关键节点:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        android:allowBackup="true"
        android:dataExtractionRules="@xml/data_extraction_rules"
        android:fullBackupContent="@xml/backup_rules"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/Theme.HelloApp">

        <activity
            android:name=".MainActivity"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

android:exported="true" 自 Android 12 起对带 intent-filter 的入口 Activity 强制要求显式声明,否则安装失败。

2. MainActivity.java

package com.example.helloapp;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

setContentView(R.layout.activity_main) 把 res/layout/activity_main.xml 设为当前界面,框架会自动解析 XML 并实例化 View 树。

3. activity_main.xml

模板生成的布局:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/main_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello, World!"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

注意 tools:context 仅在预览中起作用,编译时被忽略;xmlns:tools 命名空间需要在文件头部声明(IDE 自动补全)。

修改 TextView 文案

1. 直接改 XML

把 android:text="Hello, World!" 改成 "你好,Android!"。按 Ctrl + R10(或点 Run)重新部署,模拟器屏幕文字变化。

2. 推荐做法:用 strings.xml

把硬编码字符串迁到 res/values/strings.xml:

<resources>
    <string name="app_name">HelloApp</string>
    <string name="main_greeting">你好,Android!</string>
</resources>

布局里改用引用:

<TextView
    android:id="@+id/main_text"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="@string/main_greeting" />

好处:

  • 多语言只需新增 values-zh-rTW/、values-en/ 等目录即可切换;
  • 字符串集中管理,复用率高;
  • lint 能识别未使用的字符串资源。

3. 在代码里改文案

package com.example.helloapp;

import android.os.Bundle;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        TextView greeting = findViewById(R.id.main_text);
        greeting.setText(getString(R.string.main_greeting));
    }
}

findViewById 是经典 View 体系的查控件方式,返回 View,需要强转为具体类型。后续会用 ViewBinding 替代它,避免类型转换风险。

构建 APK

1. Debug APK

最简单:菜单 Build → Build Bundle(s) / APK(s) → Build APK(s)。完成后弹窗点 locate 找到 app/build/outputs/apk/debug/app-debug.apk。

命令行等价做法:

# 在项目根目录执行
.\gradlew.bat :app:assembleDebug

构建产物路径:

app/build/outputs/apk/debug/app-debug.apk

直接拖到运行中的模拟器即可安装。也可以用 adb 安装:

adb install app/build/outputs/apk/debug/app-debug.apk

2. Release APK

release 构建需要签名。最简流程:菜单 Build → Generate Signed Bundle / APK → 选 APK:

  1. Module 选 app;
  2. Key store path 点 Create new:
    • Key store path:D:\keys\helloapp.jks
    • Password:至少 6 位,例如 123456
    • Alias:helloapp
    • Validity (years):25
    • 填写姓名、组织、城市、国家等;
  3. 选 release Build Variant,勾选 V1 (Jar Signature) 与 V2 (Full APK Signature);
  4. 点 Finish,等待生成 app-release.apk。

不要把 .jks 与密码提交到 Git,建议放 D:\keys\ 这种项目外目录,并用 proguard-rules.pro、CI 加密变量管理。

3. 命令行 release 构建(签名配置)

在 app/build.gradle 中加签名配置:

android {
    signingConfigs {
        release {
            storeFile file('D:/keys/helloapp.jks')
            storePassword '123456'
            keyAlias 'helloapp'
            keyPassword '123456'
        }
    }
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
            signingConfig signingConfigs.release
        }
    }
}

实际工程中密码应放 ~/.gradle/gradle.properties,不要写死在脚本里:

signingConfigs {
    release {
        storeFile file(RELEASE_STORE_FILE)
        storePassword RELEASE_STORE_PASSWORD
        keyAlias RELEASE_KEY_ALIAS
        keyPassword RELEASE_KEY_PASSWORD
    }
}

执行:

.\gradlew.bat :app:assembleRelease

产物:app/build/outputs/apk/release/app-release.apk。

4. 检查 APK

用 aapt 查看 APK 的应用名、版本、入口:

aapt dump badging app/build/outputs/apk/release/app-release.apk

输出包含:

package: name='com.example.helloapp' versionCode='1' versionName='1.0'
launchable-activity: name='com.example.helloapp.MainActivity'

aapt.exe 位于 $sdkRoot\build-tools\34.0.0\,把该路径加入 PATH 才能直接调用。

常见坑与最佳实践

  1. 模板选成 Empty Activity(Compose):从 Hedgehog 起 Empty Activity 默认生成 Compose 项目,Java 项目要选 Empty Views Activity。混淆后用 ViewBinding 时会找不到布局。
  2. R.layout.activity_main 报红:通常是 Gradle 同步失败或刚改了 manifest 没同步。点 File → Sync Project with Gradle Files,必要时 Build → Clean Project 再 Rebuild。
  3. findViewById 返回 null:必须在 setContentView 之后调用,且 R.id.xxx 必须真实存在于当前布局 XML 中;用 ViewBinding 可彻底避免此类 NPE。
  4. minSdk 设置过低导致 lint 报错:本教程统一 minSdk 24,如果调用了 API 26+ 的方法(如 String.join),需要 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) 保护。
  5. release 包崩溃但 debug 不崩:开了 minifyEnabled true 后未在 proguard-rules.pro 保留反射使用的类,导致 ClassNotFoundException。对外暴露的 Java Bean、@Entity、@Parcelize 等需要 -keep。
  6. 硬编码字符串难做国际化:所有用户可见文字都应放 strings.xml,按 values-zh/、values-en/ 拆分;lint 中 HardcodedText 警告要全部消除。
  7. 修改图标没生效:AndroidManifest.xml 的 android:icon 指向 @mipmap/ic_launcher,而 ic_launcher.xml(自适应图标)会根据 mipmap-anydpi-v26 覆盖旧版 PNG,老版本可能显示默认。要确认 drawable/ic_launcher_foreground.xml 等前置资源存在。
  8. 构建超时卡 gradle-8.x-all.zip:第一次同步时把对应版本离线包放到 ~/.gradle/wrapper/dists/gradle-8.x-all/<hash>/ 下,再次同步会跳过下载。

章节小结

  • 用 Empty Views Activity 模板新建 Java 项目,compileSdk 34 / targetSdk 34 / minSdk 24 是本教程统一约定;
  • Run 流程 = assembleDebug → adb install → am start,可在 Build / Logcat 标签验证;
  • 项目三大核心:AndroidManifest.xml 声明组件、java/ 放逻辑、res/ 放资源,R 类把它们桥接起来;
  • 用户可见文字应放 strings.xml 引用,不要硬编码;
  • Debug APK 用 Build APK(s) 一键产出,Release APK 需要签名(.jks + signingConfigs)。

下一章预告

第 3 章我们会深入项目结构:逐行剖析 AndroidManifest.xml、res 目录下的 layout/values/drawable/mipmap、build.gradle、settings.gradle、gradle.properties、签名配置与 proguard-rules.pro,让你看懂“模板都帮我们配了什么”。