Android-JAVA开发基础

1.四大组件

Android 的四大核心组件是应用开发的基础框架,它们各自承担独特职责并通过系统协同工作:

  1. Activity(活动)

    • 作用:用户交互的界面载体,如一个屏幕页面(例如微信聊天窗口、购物APP商品页)。
    • 特点:管理生命周期(onCreate(), onResume()等),通过Intent启动其他组件。
    • 示例:登录界面、主菜单页面。
  2. Service(服务)

    • 作用:后台执行长时间任务(无界面),如下载文件、播放音乐。
    • 类型
      • Started Service(启动后独立运行)
      • Bound Service(被其他组件绑定调用)
    • 注意:主线程运行,需开子线程防卡顿。
  3. Broadcast Receiver(广播接收器)

    • 作用:监听系统/应用广播事件(如电量不足、网络变化),并触发响应。
    • 使用方式
      • 静态注册(AndroidManifest.xml声明,常驻监听)
      • 动态注册(代码中注册,灵活控制生命周期)
    • 示例:监听开机完成事件、自定义应用消息推送。
  4. Content Provider(内容提供者)

    • 作用:跨应用共享数据的标准化接口(如通讯录、相册访问)。
    • 机制:通过URI标识数据源,配合ContentResolver操作(增删改查)。
    • 安全:需在AndroidManifest.xml声明权限控制访问。

🛠 关键共同点

  • 必须声明:所有组件需在AndroidManifest.xml中注册。
  • 启动方式:通过Intent(隐式/显式)激活组件(Content Provider 通过ContentResolver)。
  • 生命周期:每个组件有独立生命周期回调方法需覆写。

组件间关系示例

1
2
3
4
5
graph LR
A[Activity] -->|启动| B(Service)
A -->|发送| C[Broadcast]
C -->|接收| D[Broadcast Receiver]
A -->|查询| E[Content Provider]

1.1.Activity

1.1.1.启停活动页面

  1. Activity的启动与结束
  • 启动
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ActStartActivity extends AppCompatActivity implements View.OnClickListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_act_start);
findViewById(R.id.btn_act_content).setOnClickListener(this);//监听id为btn_act_content的点击事件,如果点击则触发OnClick事件
}

@Override
public void onClick(View view) {
startActivity(new Intent(this, ActFinishActivity.class));//跳转到ActFinishActivity
}
}
  • 关闭,返回上一个页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//finsh();
public class ActFinishActivity extends AppCompatActivity implements View.OnClickListener {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_act_finish);
findViewById(R.id.iv_back).setOnClickListener(this);//监听id为iv_back的点击事件,如果点击则触发OnClick事件
findViewById(R.id.btn_finish).setOnClickListener(this);
}

@Override
public void onClick(View view) {
if (view.getId() == R.id.iv_back || view.getId() == R.id.btn_finish){//判断点击按钮的ID
// 结束当前的活动页面
finish();
}
}
}
  1. Activity的生命周期

image-20250617225205045

  • onCreate:创建活动。把页面布局加载进内存,进入初始状态。
  • onStart:开始活动。把活动页面显示在屏幕上,进入了就绪状态。
  • onResume:恢复活动。活动页面进入活跃状态,能够与用户正常交互,例如允许响应用户的点击动作、允许用户输入文字等。
  • onPause:暂停活动。活动页面进入暂停状态,无法与用户正常交互。
  • onStop:停止活动。页面将不在显示上屏幕。
  • onDestroy:销毁活动。回收活动占用的系统资源,把页面从内存中清除。
  • onNewIntent:重用已有的活动实例。
  1. Activity的启动模式

    • standard,标准的出栈入栈,不复用。
    • singleTop,当启动的Activity,在栈顶有相同的Activity时,直接复用这个Activity,而不会再增加一个入栈的Activity。
    • singleTask,当启动的Activity,在当前栈中已经有相同的Activity时,哪怕是在底部,也会将上面的其他Activity全部弹出,然后再将该Activity入栈复用。
    • singleInstance,当启动的Activity,栈中已经有其他的Activity时会新建一个栈,然后将当前的Activity加入到新栈中。也就是全局唯一模式,一般应用于主页。

    设置启动模式

1
2
3
4
5
//创建一个意图对象,准备跳转到指定的活动目录
Intent intent = new Intent(this, LoginSuccessActivity.class)
//设置启动表示:跳转到新页面是,栈中的原有实例都被清空,同时开辟新任务或者栈。
intent.setFlags(Intnet.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);//跳转到意图指定的活动页面

1.1.2.在活动之前传递消息

  1. 显示和隐式Intent
  • 显示Intent调用,精准调用指定Action
1
startActivity(new Intent(this, ActFinishActivity.class));

image-20250618221840383

  • 隐式Intent调用,即通过Action匹配
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override
public void onClick(View view) {
String phoneNumber = "12345";
Intent intent = new Intent();
if (view.getId() == R.id.btn_dial) {
// 设置意图动作为准备拨号
intent.setAction(Intent.ACTION_DIAL);
// 声明一个uri
Uri uri = Uri.parse("tel:" + phoneNumber);
intent.setData(uri);
startActivity(intent);
} else if (view.getId() == R.id.btn_sms) {
// 设置意图动作为准备拨号
intent.setAction(Intent.ACTION_SENDTO);
// 声明一个uri
Uri uri = Uri.parse("smsto:" + phoneNumber);
intent.setData(uri);
startActivity(intent);
} else if (view.getId() == R.id.btn_my) {
//跳转到自己的应用
intent.setAction("android.intent.action.NING");
intent.addCategory(Intent.CATEGORY_DEFAULT);
startActivity(intent);
}
}
}

image-20250618221926522

  1. 向下一个Activity发送数据
1
2
3
4
5
6
7
8
9
10
11
12
@Override
public void onClick(View view) {
Intent intent = new Intent(this, ActReceiveActivity.class);
// intent.putExtra("str:", "text message!")
//创建新包裹
Bundle bundle = new Bundle();
bundle.putString("request_time", "18 06 2025");
bundle.putString("request_content", tv_send.getText().toString());
intent.putExtras(bundle);
startActivity(intent);

}
  • 接收数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_act_receive);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
tv_receive = findViewById(R.id.tv_receive);
//接受传过来的数据
Bundle bundle = getIntent().getExtras();
assert bundle != null;
String requestTime = bundle.getString("request_time");
String requestContent = bundle.getString("request_content");
String formatData = String.format("receive request message: \nthe time is: %s\nthe request content is: %s\n", requestTime, requestContent);
tv_receive.setText(formatData);

}
  1. 向上一个Activity返回数据

1.1.3.为活动补充附加信息

  1. 利用资源文件配置字符串
  • 配置resources文件
1
2
3
4
5
<resources>
<string name="app_name">chaper04</string>
<string name="title_activity_act_finish">ActFinishActivity</string>
<string name="weather_str">sunny day</string>
</resources>
  • 在java文件中可以通过getString方法获取这个字符串
1
String value = getString(R.String.weather_str);
  1. 利用元数据传递配置信息
  • 在元数据中配置字符串
1
2
3
4
5
6
7
8
9
<activity
android:name=".ActSendActivity"
android:exported="true" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="api_token" android:value="SNA341FS5E12JAN34AS613YKKSJN"/>
</activity>
  • 在代码中获取这个元数据附加字符串的值
1
2
3
4
5
6
7
// 获取包管理器
PackageManager pm = getPackageManager();
// 从应用包管理器中获取当前活动信息
pm.getActivityInfo(getComponentName(), PackageManager.GET_META_DATA);
// 获取Activity附加的元数据信息
Bundle bundle = info.metaData;
bundle.getString("api_token");

1.2.数据存储

1.2.1.共享参数SharedPreferences

  • SharedPreferences是一个轻量级存储工具。
  • 采用key-value存储形式
  • 使用xml文件格式存储
  • 关闭程序不清空数据
  • 一些记住密码功能可能会将密码存储到SharedProferences中。

image-20250619204140201

  1. 共享参数用法
1
2
3
4
5
6
7
8
//获取SharedPreferences Editor
SharedPreferences preference = getSharedPreferences("config", Context.MODE_PRIVATE);
SharedPreferences.Editor edit = preference.edit();
String name = "anny";
int age = 15;
edit.putString("name", name);
edit.putInt("age", age);
edit.commit();//执行后就会在私有目录下生成xml文件用于存储键值对

保存位置

image-20250619205909070

  • 读取SharedPreferences中的值
1
String value = preference.getString("name", null);

1.2.2.数据库SQLite

SQL的基本语法

  • 创建和删除数据库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="@+id/btn_database_create"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="create database"
android:textColor="@color/black"
android:textSize="17sp"/>
<Button
android:id="@+id/btn_database_delete"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="delete database"
android:textColor="@color/black"
android:textSize="17sp"/>

</LinearLayout>
<TextView
android:id="@+id/tv_database"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="5dp"
android:textColor="@color/black"
android:textSize="17sp"/>

</LinearLayout>
  • DatabaseActivity.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package com.gaomu.chapter06;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;

import androidx.activity.EdgeToEdge;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.graphics.Insets;
import androidx.core.view.ViewCompat;
import androidx.core.view.WindowInsetsCompat;

public class DatabaseActivity extends AppCompatActivity implements View.OnClickListener {

private TextView tv_database;
private String mDatabasePath;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EdgeToEdge.enable(this);
setContentView(R.layout.activity_database);
ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main), (v, insets) -> {
Insets systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars());
v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom);
return insets;
});
tv_database = findViewById(R.id.tv_database);
findViewById(R.id.btn_database_create).setOnClickListener(this);
findViewById(R.id.btn_database_delete).setOnClickListener(this);
// 生成一个测试数据库的完整路径
mDatabasePath = getFilesDir() + "/test.db";

}

@Override
public void onClick(View view) {
if (view.getId() == R.id.btn_database_create) {
//创建数据库,不存在就创建,存在数据库就打开
SQLiteDatabase db = openOrCreateDatabase(mDatabasePath, Context.MODE_PRIVATE, null);
String desc = String.format("数据库%s创建%s", db.getPath(), "成功");
tv_database.setText(desc);
} else if (view.getId() == R.id.btn_database_delete) {
//如果存在数据库就删除
boolean result = deleteDatabase(mDatabasePath);
String desc = String.format("数据库%s删除%s", mDatabasePath, result ? "成功" : "失败");
tv_database.setText(desc);
}
}
}

image-20250619214744339

image-20250619214703632

数据库管理器SQLiteDatabase

常用API接口

openDatabase: 打开指定路径的数据库

isOpen: 判断数据库是否打开

close:关闭数据库。

getVersion:获取数据库版本号

setVersion:设置数据库的版本号

数据处理

execSQL:执行拼接号的SQL控制语句。

delete:删除符合条件的记录

update:更新符合条件的记录

query:执行查询操作,返回结果集的游标

rawQuery:执行拼接号的SQL查询语句,返回结果集的游标。

数据库帮助器SQLLiteOpenHelper

对数据库中的数据进行操作需要用到SQLLiteOpenHelper,

使用继承SQLLiteOpenHelper的数据库操作类,提示重写onCreate和onUpgrade两个方法。

优化记住密码功能

1.2.3.存储卡的文件操作

私有存储空间于公共存储空间

在存储卡上读写文本文件

在存储卡上读写图片文件

1.2.4.应用组件Application

  • 在App运行的整个过程中有且仅有一个Application。
  • Application比Activity新启动,且比Activity后关闭。(现有页面采用应用)

Applicaiton可以编写

  • 在清单文件中自定自己的Applicaiton
1
2
3
4
5
6
7
8
9
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

<application
android:name=".MyApplication"
...
</application>

</manifest>
  • 自定义的Application
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package com.gaomu.chapter06;

import android.app.Application;
import android.content.res.Configuration;
import android.util.Log;

import androidx.annotation.NonNull;

public class MyApplication extends Application {
//在App启动时调用
@Override
public void onCreate() {
super.onCreate();
Log.d("ning", "MyApplication start");
}

//App终止时调用
@Override
public void onTerminate() {
super.onTerminate();
Log.d("ning", "App close");

}

//配置改变时调用,例如横屏变竖屏
@Override
public void onConfigurationChanged(@NonNull Configuration newConfig){
super.onConfigurationChanged(newConfig);
Log.d("ning", "onConfigurationChanged");
}
}

Application的生命周期

  • onCreate
  • onTerminate

利用Application操作全局变量

利用Room简化数据库操作

  • 编写是实体类,该类添加@Entity注解
  • 添加持久化类,@Dao注解
  • 编写数据库类,该类从RoomDatabase派生而来,继承该类,并添加@Database注解,声明抽象的Dao。
  • 在自定义的Application类中声明数据库的唯一实例
  • 在操作信息表的地方获取数据库的持久化对象

1.3.内容提供者

  • ContentProvider为App存取内部数据提供了统一的外部接口。

1.3.1.在应用之间共享数据

通过ContentProvider封装数据

  • URi结构,代表数据操作地址

  • content://authority/data_path/id

  • content:// 通用前缀

  • authority 授权名称,标识具体哪一个ContentProvider提供资源

  • data_path 是数据路径,用来确定请求的是那个数据集

  • id 数据编号,标识单条数据

通过ContentResolver访问数据

image-20250621143838008

  • 出于安全考虑,Android11要求应用事先说明需要访问的其他软件包
1
2
3
4
<queries>
<package android:name="com.gaomu.apkserver"/><!-- 选择其中一种进行配置即可 -->
<provider android:authorities="com.gaomu.apkserver.provider.UserInfoProvider"
</queries>

1.3.2.在内容组件获取通讯信息

运行时动态申请权限

  • 动态申请权限的步骤

    • 检查App是否开启了指定权限:调用ContextCompat的checkSelfPermission方法

    • 调用系统弹窗,以便用户选择是否开启权限:调用ActivityCompat的RequestPermission方法,即可命令系统自动弹出权限申请窗口

    • 判断用户权限选择结果:重写活动页面的权限请求方法onRequestPermissionsResult,在该方法内部处理用户的权限选择结果。

  • lazy模式就是在需要用到该权限时才会去申请权限。

  • hungry模式表示,app启动时就申请权限

利用ContentResolver读写联系人

利用ContentObserver监听短信

1.3.3.在应用之间共享文件

使用相册发送彩信

借助FileProvider发送彩信

借助FileProvider安装应用

1.4.安卓打包发布

1.4.1.默认测试时,会在构建目录下生成测试版本的APK文件

image-20250621173301459

1.4.2.打包生产环境的APK

image-20250621173451109

  • 选择自己打包APK

image-20250621173533491

  • 创建key文件

image-20250621173616189

  • 选择创建Key文件保存的路径

image-20250621173802647

image-20250621173949965

  • 选择OK完成创建,后再点击下一步

image-20250621174021520

  • 选择构建发行版本。

image-20250621174054461

  • 点击创建,发布版创建成功。

image-20250621174215710