当前位置: 首页 > news >正文

Android基础教程 - 学习完成记录

视频学习教程

视频链接:2022 最新 Android 基础教程,从开发入门到项目实战,看它就够了,更新中_哔哩哔哩_bilibili

学习下来,有遇到很多问题,在 chatgpt、claude 和 Android Studio 插件通义千问的帮助下,一一解决。

目前还是有很多不懂的,虽然学了一遍,敲了一遍,但是 Android 体系还是太多,需要不断地学习。

Gitee 地址

代码均可以跑通,视频中项目实战均实现。
Magnolia/AndroidLearning
如果可以,帮点个小星星,谢谢~


下面是一些记录。

资源IDs 变成 non-final

Resource IDs will be non-final by default in Android Gradle Plugin version 8.0, avoid using them in switch case statements.

在 Android Gradle Plugin 8.0 中,资源 ID 默认变为 non-final,意思是它们不再是编译时常量,而是动态生成的。这是因为 Android 的资源 ID 现在默认使用了新的构建优化方式,使得每次构建时资源 ID 可能发生变化,从而减少了重编译时间,但也带来了以下变化:

解释 non-final

  • 原本的 final 行为:在 Android Gradle Plugin 8.0 之前,资源 ID(如 R.id.example_button)被编译成 final 常量(不可变的静态值),因此可以直接用于 switch 语句,因为编译器在编译时可以确定它们的值。
  • non-final 意味着动态生成:从 8.0 开始,资源 ID 不再是 final 的。它们的值在每次构建时可能会动态生成,无法在编译时确定。因此,在 switch 语句中使用这些非固定值会导致编译错误,因为 switch 语句要求常量。

影响和替代方案

由于资源 ID 变为 non-final,在 Android Gradle Plugin 8.0+ 中,不能在 switch 语句中使用它们。可以使用 if-else 结构来替代 switch

变量声明为 final

将变量声明为 final 有以下好处:

  • 不可变性:确保该变量在初始化后不能被修改,提高代码的安全性和可预测性。
  • 线程安全:在多线程环境中,final 变量可以避免多个线程同时修改同一个变量带来的问题。
  • 优化:编译器可以对 final 变量进行一些优化,提高性能。
private String mRequest = "你好,在吗?";
// 可以写成:
private final String mRequest = "你好,在吗?";

override 注解

@Override 注解在 Java 中用于表示一个方法是在重写父类或实现接口中的方法。使用 @Override 有以下几个好处:

  • 提高可读性:明确告知其他开发者这个方法是一个重写的方法。
  • 编译器检查:如果方法签名与父类或接口中的方法不匹配,编译器会报错,帮助开发者发现潜在的错误。

如果不加 @Override 注解,代码仍然可以正常编译和运行,但会有以下问题:

  • 缺乏明确性:其他开发者阅读代码时可能不清楚这个方法是否是重写的。
  • 潜在错误:如果方法签名写错了(例如拼写错误或参数类型不匹配),编译器不会报错,但方法不会被正确调用,导致难以调试的问题。

.9.png

在 Android 开发中,.9.png 是一种特殊的图片格式,称为 Nine-Patch (九宫格) 图片。这种格式的图片在 .png 图片基础上做了特殊处理,主要用于实现拉伸和缩放效果,适应不同尺寸的屏幕和控件。

特点与用途

  1. 灵活拉伸:Nine-Patch 图片可以指定哪些区域可被拉伸,哪些区域保持不变。这对于按钮、对话框背景等需要根据内容大小动态调整的控件非常有用。
  2. 边界标记:Nine-Patch 图片的边界上有一像素宽的黑色线条,Android 会根据这些标记确定哪些部分可以拉伸,哪些部分保持原样。具体来说:
    • 左侧和上侧边界:定义图片可以被拉伸的区域。
    • 右侧和下侧边界:定义内容显示区域。
  3. 高效适配:Nine-Patch 图片在适应不同分辨率和屏幕尺寸时,不会失真或模糊,提供较好的视觉效果。适合用于各种弹窗、输入框背景、气泡等需要拉伸的图形背景。

示例应用

  • 按钮背景、输入框背景、对话框背景等需要根据内容大小动态变化的 UI 元素。

创建方法

Nine-Patch 图片可以通过 Android Studio 的 Draw 9-Patch 工具制作,或直接在图片上手动添加一像素的黑色边界。Android 会根据这些边界像素的标记进行渲染处理。

Nine-Patch 图片通过简单的标记,能以更小的资源量适配不同屏幕,是一种高效的图片处理方式。

复合按钮

由复合按钮 CompoundButton 派生而来的控件包括:CheckBoxRadioButtonSwitch

导入 Nullable

import androidx.annotation.Nullable;

作用是导入 Nullable 注解。这个注解来自 AndroidX 库,用于标记方法参数、返回值或字段,表示它们可以为 null

  • 代码可读性:提高代码的可读性和维护性,明确指出某个变量或参数可以为 null
  • 静态分析:帮助 IDE 和静态代码分析工具进行更准确的检查,减少潜在的空指针异常。

示例

@Nullable
public String getName() {
    return name;
}

在这个示例中,@Nullable 注解表明 getName 方法的返回值可以为 null

Boolean 与 boolean

Boolean married = ck_married.isChecked(); 
boolean married = ck_married.isChecked(); 这两种写法有什么区别?
  1. Boolean married = ck_married.isChecked();

    • 类型Booleanboolean 的包装类。
    • :可以为 truefalsenull
    • 使用场景:适用于需要表示 null 值的情况,例如在集合中存储布尔值或处理可能为 null 的返回值。
  2. boolean married = ck_married.isChecked();

    • 类型boolean 是基本数据类型。
    • :只能为 truefalse
    • 使用场景:适用于不需要表示 null 值的情况,性能更好,占用内存更少。

在代码中:

  • Boolean married = ck_married.isChecked();:这里 married 变量可以为 truefalsenull。但由于 isChecked() 方法总是返回 truefalse,所以 married 不会为 null
  • boolean married = ck_married.isChecked();:这里 married 变量只能为 truefalse,更符合实际需求,因为 isChecked() 方法不会返回 null

推荐做法

在大多数情况下,特别是当你确定变量不会为 null 时,推荐使用基本数据类型 boolean,因为它更高效且占用内存更少。

boolean married = ck_married.isChecked();

总结

  • Boolean:包装类,可以为 null
  • boolean:基本数据类型,不能为 null
  • 推荐:在不需要表示 null 值的情况下,使用 boolean

参数化查询和静态查询

参数化查询

String sql = "select * from " + TABLE_NAME + " where phone=? and remember=1";
Cursor cursor = mRDB.rawQuery(sql, new String[]{phone});
  1. SQL 查询:

    • select * from <TABLE_NAME> where phone=? and remember=1:这条 SQL 语句从表中选择所有列,条件是 phone 等于某个值且 remember 等于 1。
    • ? 是一个占位符,用于防止 SQL 注入攻击。
  2. 参数化查询:

    • new String[]{phone}:这是一个字符串数组,包含一个元素 phone。这个数组中的值会替换 SQL 语句中的 ? 占位符。
    • 例如,如果 phone 的值是 "1234567890",那么最终的 SQL 语句会变成 select * from <TABLE_NAME> where phone='1234567890' and remember=1

静态查询

String sql = "select * from " + TABLE_NAME + " where remember=1 order by _id desc limit 1";
Cursor cursor = mRDB.rawQuery(sql, null);
  1. SQL 查询:

    • select * from <TABLE_NAME> where remember=1 order by _id desc limit 1:这条 SQL 语句从表中选择所有列,条件是 remember 等于 1,并按 _id 列降序排列,只取第一行。
    • 没有使用占位符 ?,因为查询条件中没有动态参数。
  2. 参数化查询:

    • null:表示没有参数需要传递给 SQL 语句。

为什么前者多了 new String[]{phone}

  • 参数化查询:new String[]{phone} 用于传递动态参数 phone 给 SQL 语句中的占位符 ?。这样做可以防止 SQL 注入攻击,提高安全性。
  • 静态查询:第二段代码没有动态参数,因此不需要传递参数数组,直接使用 null 即可。

总结

  • 第一段代码:使用参数化查询,动态传递 phone 参数。
  • 第二段代码:使用静态查询,没有动态参数。

android.intent.action.VIEW

"android.intent.action.VIEW" 或者 "android.intent.action.ACTION_VIEW" 都可以

Intent intent = new Intent(Intent.ACTION_VIEW);
<activity
    android:name=".ThirdActivity"
    android:exported="false"
    tools:ignore="AppLinkUrlError">
    <intent-filter>
        <action android:name="android.intent.action.ACTION_VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="http" />
    </intent-filter>
</activity>

简要解释原因

在 Android 中,Intentaction 属性用于指定操作类型。"android.intent.action.VIEW""android.intent.action.ACTION_VIEW" 实际上是同一个常量的不同表示形式。

  • "android.intent.action.VIEW":这是直接使用字符串形式的 action 名称。
  • "android.intent.action.ACTION_VIEW":这是通过 Intent 类中的静态常量 Intent.ACTION_VIEW 来引用的。

这两种方式在功能上是等价的,因为它们都指向同一个标准的 Android 操作名称 "android.intent.action.VIEW"

分点描述

  • 字符串形式 vs 常量形式

    • 使用字符串形式(如 "android.intent.action.VIEW")时,代码更直观,但容易出现拼写错误。
    • 使用常量形式(如 Intent.ACTION_VIEW)时,代码更安全,编译器可以进行类型检查和自动补全,减少错误。
  • 推荐使用常量形式

    • 使用常量形式可以提高代码的可读性和维护性。
    • 编译器可以在编译时检查常量的有效性,避免运行时错误。

示例代码

AndroidManifest.xml 中使用常量形式

<activity
    android:name=".ThirdActivity"
    android:exported="false"
    tools:ignore="AppLinkUrlError">
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <!-- 或者使用 -->
        <action android:name="android.intent.action.ACTION_VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="http" />
    </intent-filter>
</activity>

在 Java 代码中使用常量形式

else if (v.getId() == R.id.btn_jump2web) {
    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.setData(Uri.parse("http://www.baidu.com"));
    startActivity(intent);
}

虽然 "android.intent.action.VIEW""android.intent.action.ACTION_VIEW" 都可以正常工作,但建议使用常量形式 Intent.ACTION_VIEW,以提高代码的安全性和可维护性。


Bug记录

购物车数目只更新一次
    private void addToCart(int goodsId, String goodsName) {
        int goodsCount = MyApplication.getInstance().goodsCount;
        ++goodsCount;
        tv_count.setText(String.valueOf(goodsCount));
        mDBHelper.insertCardInfo(goodsId);
        ToastUtil.show(this, "已添加一件" + goodsName + "到购物车");
    }

原因:仅对局部变量 goodsCount 进行自增操作,此时MyApplication.getInstance().goodsCount的值并没有被修改,首次操作时, goodsCount 加一了,所以购物车数目会加一。但是,后面再添加就不会更新,因为每次获取的 goodsCount 都是一样的值,tv_count 更新也都是第一次更新的数字。

先对MyApplication.getInstance().goodsCount进行自增操作,然后将自增后的结果赋值给goodsCount,确保MyApplication.getInstance().goodsCount 和UI显示同步更新。

    private void addToCart(int goodsId, String goodsName) {
        int goodsCount = ++MyApplication.getInstance().goodsCount;
        tv_count.setText(String.valueOf(goodsCount));
        mDBHelper.insertCardInfo(goodsId);
        ToastUtil.show(this, "已添加一件" + goodsName + "到购物车");
    }

问题处理

Run后模拟器未启动app

可能的原因或者解决方法

  • 模拟器是否多开,在另一个中启动

  • 重启模拟器,清除模拟器数据(wipe data)后重启

  • adb devices 查看状态

    如果是 unauthorized,尝试 adb kill-server, adb start-server,设置 cold boot 启动

  • 查看 AndroidManifest.xml 是否配置正确

模拟器进程被占用

AVD Pixel_2_API_31 is already running. If that is not the case, delete the files at D:\software\Android.android\avd/Pixel_2_API_31.avd/*.lock and try again.

taskkill /F /IM qemu-system-* /IM emulator-* /IM adb.exe
del /F /Q D:\software\Android\.android\avd\Pixel_2_API_31.avd\*.lock

使用 procexp.exe 找到并结束进程。


记录

AndroidStudio 中, debug 运行代码,生成包 build/intermediates/apk/debug/chapter06-debug.apk

相关文章:

  • UI基础(1)
  • 【双指针】专题:LeetCode 1089题解——复写零
  • 202527 | RabbitMQ-基础 | 队列 | Direct + Fanout + Topic 交换机 | 消息转换器
  • linux系统安装docker及Qdrant
  • LeetCode 解题思路 38(前 K 个高频元素、数据流的中位数)
  • centos7 yum安装mysql8.0详细步骤(图文教程)
  • java和c#的相似及区别基础对比
  • 如何实现语音智能客服(一)
  • Linux上位机开发实践(OpenCV算法硬件加速)
  • 探索R语言:在线学习资源汇总
  • UE5蓝图之间的通信------接口
  • 安装VS Code 服务器运行版本
  • 第二十四:查看当前 端口号是否被占用
  • 开源 2D 横版跳跃游戏 SuperTux
  • 【PyQt】俄罗斯方块的小程序开发
  • Django 使用 Celery 完成异步任务或定时任务
  • 【Linux】进程创建、进程终止、进程等待
  • Lua 中的 table 类型详解
  • 深入解析 HTML 中 `<script>` 标签的 async 和 defer 属性
  • 高并发短信系统设计:基于SharingJDBC的分库分表、大数据同步与实时计算方案
  • 首家股份行旗下AIC来了,兴银金融资产投资有限公司获批筹建
  • 江苏淮安优化村级资源配置:淮安区多个空心村拟并入邻村
  • “两高”出台司法解释,严打破坏黑土地资源犯罪
  • IPO周报|节后首批3只新股本周申购,色谱设备龙头来了
  • “模”范生上海,如何再进阶?
  • 繁荣活跃!“五一”假期全国重点零售和餐饮企业销售额同比增长6.3%