🏆作者简介:|康有为| ,大四在读,目前在小米做安卓开发,一起学习安卓,加油
🏆本文收录于 安卓学习大全持续更新中,欢迎关注
🏆安卓学习资料推荐:
视频:b站搜动脑学院 视频链接 (他们的视频后面一部分没再更新,看看前面也挺好的)
书籍:《第一行代码》(第3版) by 郭霖 (z-lib.org)
思维导图: https://www.processon.com/view/link/62427cb2e0b34d0730e20e00(来自动脑学院)
目录
一、共享参数SharedPreferences
SharedPreferences是Android的一个轻量级存储工具,采用的存储结构是Key-Value的键值对方式。
重量级存储工具就是指的我们常用的数据库
服务器开发中 常用properties来存储一些键值对,在安卓中就是用SharedPreferences
共享参数的使用场景
共享参数主要适用于如下场合:
- 简单且孤立的数据,若是复杂且相互间有关的数据,则要保存在数据库中。
相互关联的数据:指的就是数据A中包含了数据B这种
- 文本形式的数据,若是二进制数据,则要保存在文件中。
- 需要持久化存储的数据,在APP退出后再次启动时,之前保存的数据仍然有效。
实际开发,共享参数经常存储的数据有APP的个性化配置信息、用户使用APP的行为信息、临时需要保存的片段信息等。
比如:自动登录,记住密码,小说app(返回后再次进入还是 原来看的页数),按钮的状态。
特点
SharedPreferences 是 Android 中用于存储简单键值对数据的一种轻量级持久化存储方式。它的主要特点包括:
- 简单易用:SharedPreferences 提供了简单易用的 API,允许应用程序以键值对的形式存储和检索数据。
- 轻量级:SharedPreferences 存储的数据通常很小,并且适合用于存储简单的配置信息、用户偏好设置等。
- 基于文件存储:SharedPreferences 内部以 XML 文件的形式存储数据。每个应用程序都有自己的 SharedPreferences 文件用于存储应用程序私有的数据。
- 应用级别数据:SharedPreferences 存储的数据是应用程序级别的,即每个应用程序都有自己独立的 SharedPreferences 实例用于存储数据,其他应用程序无法直接访问。
- 轻量级加密:SharedPreferences 的数据存储在文件中,默认情况下是以明文形式存储的。但是,可以通过使用 MODE_PRIVATE 加密模式来提高数据的安全性,使得其他应用程序无法轻易读取和修改数据。
- 实时性:SharedPreferences 中的数据是实时更新的,一旦数据发生变化,其他组件可以立即获取到最新的数据。
尽管 SharedPreferences 具有上述优点,但也有一些限制,例如不适合存储大量复杂数据、不支持事务操作等。因此,对于需要存储大量数据或需要复杂查询和操作的情况,可能需要选择其他数据存储方式,如 SQLite 数据库或使用 Room 持久化库。
共享参数的用法
如果想看更多的用法示例:
推荐
获取 SharedPreferences 对象:
在 Activity 或 Fragment 中,可以通过 getPreferences() 方法获取 SharedPreferences 对象。如果需要在多个组件之间共享数据,可以使用 getSharedPreferences() 方法。
// 获取默认的 SharedPreferences 对象(仅在当前 Activity 或 Fragment 内有效)
SharedPreferences preferences = getPreferences(Context.MODE_PRIVATE);
// 获取自定义名称的 SharedPreferences 对象(可在多个组件之间共享)
SharedPreferences preferences = getSharedPreferences("custom_prefs", Context.MODE_PRIVATE);
编辑 SharedPreferences:
要修改 SharedPreferences 中的数据,需要获取 SharedPreferences.Editor 对象,并使用其方法进行修改。修改完成后,必须调用 commit() 或 apply() 方法来提交更改。
SharedPreferences.Editor editor = preferences.edit();
editor.putString("key", "value");
editor.putInt("intKey", 42);
editor.apply(); // 或者使用 editor.commit();
读取 SharedPreferences 中的数据:
使用 SharedPreferences 对象的方法来获取存储的数据。
第一个参数是键名,第二个参数是默认值,如果找不到对应的键时将返回默认值。
String value = preferences.getString("key", "default_value");
int intValue = preferences.getInt("intKey", 0);
二、数据库SQLite
在Android中,SQLite是默认的本地数据库引擎,它提供了一种轻量级、嵌入式的数据库解决方案。在Android应用程序中,你可以使用SQLite数据库来存储和检索数据。
推荐 学习博客
【Android入门到项目实战--4.5】—— SQLite数据库存储实现增删改查
Android 数据库(SQLite)【简介、创建、使用(增删改查、事务、实战演练)、数据显示控件(ListView、Adapter、实战演练-绿豆通讯录)】
SQLite基本语法
因为 这里学习过MySQL,所以上手SQLite会比较容易。
SQLite 和 MySQL 都属于关系型数据库管理系统(RDBMS),它们使用 SQL语言进行数据库操作,因此在很大程度上,它们的基本 SQL 语法是相似的。许多基本的 SQL 查询、插入、更新、删除等语句在这两种数据库系统中都是通用的。
以下是一些在 SQLite 和 MySQL 中通用的 SQL 语句:
-- 创建表格
CREATE TABLE employees (
id INTEGER PRIMARY KEY,
name TEXT,
age INTEGER,
salary REAL
);
-- 插入数据
INSERT INTO employees (name, age, salary)
VALUES ('John Doe', 30, 50000.00);
-- 查询数据
SELECT name, age
FROM employees
WHERE age > 25;
-- 更新数据
UPDATE employees
SET salary = 55000.00
WHERE name = 'John Doe';
-- 删除数据
DELETE FROM employees
WHERE age < 25;
-- 查询所有数据
SELECT * FROM employees;
-- 查询去重数据
SELECT DISTINCT name, age
FROM employees;
-- 排序数据
SELECT * FROM employees
ORDER BY salary DESC;
-- 连接表格
SELECT employees.name, departments.department_name
FROM employees
JOIN departments ON employees.department_id = departments.department_id;
虽然基本语法很相似,但是需要注意一些差异。一些高级功能、数据类型、约束等方面可能会有一些不同。例如,MySQL 支持 AUTO_INCREMENT 属性来定义自增主键,而 SQLite 使用 INTEGER PRIMARY KEY AUTOINCREMENT 来实现相似的功能。此外,一些数据库管理系统特定的函数、语法和性能优化等也可能有所不同。
数据库帮助器SQLiteOpenHelper
数据库帮助器(SQLiteOpenHelper)是 Android 中用于管理 SQLite 数据库的帮助类。它提供了创建、升级数据库的方法,并帮助你获取数据库的实例。以下是关于数据库帮助器的一些重要概念和用法:
创建数据库帮助器类
通常,你需要创建一个继承自 SQLiteOpenHelper 的类,用于管理数据库的创建和版本升级。
这个类可以专门放到一个包中,例如 创建一个包database
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "mydatabase.db";
private static final int DATABASE_VERSION = 1;
// 构造方法
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
// 创建表格
@Override
public void onCreate(SQLiteDatabase db) {
String createTableQuery = "CREATE TABLE mytable (_id INTEGER PRIMARY KEY, name TEXT, age INTEGER);";
db.execSQL(createTableQuery);
}
// 升级数据库
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 升级数据库的逻辑,例如删除旧表并重新创建新表
db.execSQL("DROP TABLE IF EXISTS mytable;");
onCreate(db);
}
}
使用数据库帮助器获取数据库实例
在你的应用程序的任何地方,你可以通过创建数据库帮助器类的实例来获取数据库的实例。这通常在 Activity 或 Application 的生命周期中完成。
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
private DatabaseHelper dbHelper;
private SQLiteDatabase db;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 初始化数据库帮助器
dbHelper = new DatabaseHelper(this);
// 获取可写的数据库实例
db = dbHelper.getWritableDatabase();
// 在这里可以执行数据库操作
}
@Override
protected void onDestroy() {
super.onDestroy();
// 关闭数据库连接,避免资源泄漏
if (db != null && db.isOpen()) {
db.close();
}
}
}
在上述示例中,onCreate 方法中创建了数据库帮助器的实例,并通过 getWritableDatabase() 方法获取可写的数据库实例。在 onDestroy 方法中关闭了数据库连接,以确保在 Activity 销毁时释放资源。
数据库版本管理
在 SQLiteOpenHelper 中,有一个数据库版本(DATABASE_VERSION)的概念。当你的应用需要升级数据库结构时,你可以递增这个版本号,并在 onUpgrade 方法中执行相应的升级逻辑。
private static final int DATABASE_VERSION = 2;
public DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
if (oldVersion < 2) {
// 执行升级逻辑,例如增加新的表格或修改表格结构
db.execSQL("ALTER TABLE mytable ADD COLUMN new_column INTEGER;");
}
// 其他升级逻辑...
}
数据库管理器SQLiteDatabase
SQLiteDatabase 是 Android 中用于执行 SQL 操作的类,它是对 SQLite 数据库的封装,提供了一组方法用于执行常见的数据库操作。以下是一些关于 SQLiteDatabase 类的基本用法:
获取 SQLiteDatabase 实例
在使用 SQLiteDatabase 之前,通常需要获取一个可读写的数据库实例。这可以通过 SQLiteOpenHelper 或直接使用 SQLiteDatabase 的工厂方法来实现。
1.通过 SQLiteOpenHelper 获取:
这里的DatabaseHelper 是自己写的一个继承了SQLiteOpenHelper 的类,具体方法可以看上面一节内容
DatabaseHelper dbHelper = new DatabaseHelper(context);
SQLiteDatabase db = dbHelper.getWritableDatabase(); // 获取可读写的数据库实例
2.直接使用 SQLiteDatabase 工厂方法:
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(databasePath, null);
执行 SQL 语句
SQLiteDatabase 提供了几个方法来执行 SQL 语句,其中最常用的是 execSQL() 和 rawQuery()。
1.使用 execSQL() 执行非查询操作(如创建表、插入、更新、删除):
String createTableQuery = "CREATE TABLE mytable (_id INTEGER PRIMARY KEY, name TEXT, age INTEGER);";
db.execSQL(createTableQuery);
2.使用 rawQuery() 执行查询操作:
String selectQuery = "SELECT * FROM mytable WHERE age > ?";
String[] selectionArgs = {"20"};
Cursor cursor = db.rawQuery(selectQuery, selectionArgs);
// 处理查询结果...
插入数据
ContentValues 是一个键值对的容器,用于插入数据。
ContentValues values = new ContentValues();
values.put("name", "Alice");
values.put("age", 30);
long newRowId = db.insert("mytable", null, values);
if (newRowId != -1) {
// 插入成功
} else {
// 插入失败
}
这里的第二个参数“null” 是干什么的?
第二个参数是空列(nullColumnHack)参数。它在 insert 方法中表示一个可以为 null 的列名,这个参数的作用主要是在插入数据时,如果插入的数据集合 ContentValues 中没有包含所有表的列(或者包含了所有列但是值为 null),系统会将这个列的值设置为默认值,通常是 null。
实际上,nullColumnHack 是一个历史原因遗留下来的参数,它的存在是为了在插入一行数据的时候,至少有一列有值,以满足数据库表中至少有一列不能为 null 的要求。
然而,大多数情况下,这个参数可以传递为 null,因为在实际应用中,通常会确保插入的数据集合 ContentValues 中包含了所有需要插入的列,而不是依赖于 nullColumnHack。
查询数据
使用 query() 方法查询数据:
String[] projection = {"_id", "name", "age"};
String selection = "age > ?";
String[] selectionArgs = {"25"};
Cursor cursor = db.query("mytable", projection, selection, selectionArgs, null, null, null);
while (cursor.moveToNext()) {
long itemId = cursor.getLong(cursor.getColumnIndexOrThrow("_id"));
String itemName = cursor.getString(cursor.getColumnIndexOrThrow("name"));
int itemAge = cursor.getInt(cursor.getColumnIndexOrThrow("age"));
// 处理数据...
}
cursor.close();
- 选择条件 (selection): 这是一个字符串,表示筛选条件。只有符合这个条件的行才会被返回。
- 选择条件参数 (selectionArgs): 这是一个字符串数组,用于替代选择条件中的占位符 ?。
更新数据
ContentValues values = new ContentValues();
values.put("age", 26);
String selection = "name = ?";
String[] selectionArgs = {"Alice"};
int count = db.update("mytable", values, selection, selectionArgs);
if (count > 0) {
// 更新成功
} else {
// 更新失败,可能因为没有满足选择条件的行
}
删除数据
String selection = "age < ?";
String[] selectionArgs = {"25"};
int deletedRows = db.delete("mytable", selection, selectionArgs);
if (deletedRows > 0) {
// 删除成功
} else {
// 删除失败,可能因为没有满足选择条件的行
}
事务处理
事务:要么一起成功,要么一起失败
db.beginTransaction();//开启事务
try {
// 执行一系列数据库操作
db.execSQL("INSERT INTO mytable (name, age) VALUES ('Bob', 28);");
db.execSQL("UPDATE mytable SET age = 29 WHERE name = 'Charlie';");
// 如果一切顺利,设置事务标志为成功
db.setTransactionSuccessful();
} finally {
db.endTransaction();//结束事务
}
根据操作的结果,决定是提交事务还是回滚事务。如果调用了setTransactionSuccessful(),则调用endTransaction()将提交事务,否则将回滚事务。
关闭数据库连接
在不再需要使用数据库实例时,应该关闭数据库连接,以释放资源。
db.close();
操作数据库方式-总结
SQLiteOpenHelper 是一个抽象类,你需要继承它并实现它的两个主要方法:onCreate 和 onUpgrade。这样,你可以在 onCreate 方法中定义数据库的创建逻辑,包括表的结构等;在 onUpgrade 方法中定义数据库升级的逻辑,比如修改表结构等。通常,你可以在自定义的 SQLiteOpenHelper 类中添加一些辅助的方法,以便更方便地执行数据库操作。
SQLiteDatabase 是一个用于执行 SQL 语句的类,它提供了一系列的方法来执行增、删、改、查等数据库操作。你不需要自己创建 SQLiteDatabase 类,而是通过调用 getWritableDatabase() 或 getReadableDatabase() 方法,由系统帮你管理数据库的创建和版本控制,并返回一个可读写或只读的 SQLiteDatabase 实例。
在使用时,通常的做法是:
- 创建一个自定义的 SQLiteOpenHelper 类,继承并实现其中的方法。
- 在需要执行数据库操作的地方,创建该自定义类的实例,获取 SQLiteDatabase实例,然后调用相应的数据库操作方法。
实例,然后调用相应的数据库操作方法。
下面是一个简单的示例:
public class MyDatabaseHelper extends SQLiteOpenHelper {
private static final String DATABASE_NAME = "mydatabase";
private static final int DATABASE_VERSION = 1;
public MyDatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
@Override
public void onCreate(SQLiteDatabase db) {
// 创建表的 SQL 语句
String createTableSQL = "CREATE TABLE users " +
"(_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"user_name TEXT);";
// 执行创建表的 SQL 语句
db.execSQL(createTableSQL);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 在数据库版本升级时执行的操作,可以进行表结构的修改等
}
}
在活动或其他组件中,你可以这样使用:
// 初始化数据库辅助类
MyDatabaseHelper dbHelper = new MyDatabaseHelper(this);
// 获取可写的数据库
SQLiteDatabase db = dbHelper.getWritableDatabase();
// 执行数据库操作,比如插入、查询等
// ...
// 关闭数据库
db.close();
这样,你可以方便地通过 SQLiteDatabase 执行数据库操作,而无需关心具体的数据库的创建和版本管理的细节。
三、存储卡的文件操作
外部存储控件
对Andriod来说,存储空间分为内部存储空间和外部存储空间。
外部存储空间也就是常说的SD卡,可以分为私有存储空间和公共存储空间。
内部存储空间和外部存储空间的私有存储空间,都是每个APP独有的,不允许其他APP访问。
公共存储空间则是所有APP都可以访问,空间也更大,可以用于存储一些大的音频文件。
那么很自然地可以得出,当APP卸载后,内部存储空间和外部存储空间的私有存储空间的文件都被清空了,但公共存储空间的文件不会被删除。
在这三个空间存储,需要获得不同的路径:
String dir = null;
//外部存储私有空间
dir = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).toString();
//外部存储公共空间
dir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).toString();
//内部存储空间
dir = getFilesDir().toString();
完整代码,请看博客http://t.csdnimg.cn/K5cTe
在存储卡上读写图片文件
完整代码,请看博客http://t.csdnimg.cn/6xrlK
在Android开发中,从存储卡上读写图片文件涉及到一些基本的文件和图像处理操作。以下是一个简单的示例,演示如何从存储卡上读取图片文件并将图片写入存储卡:
1. 添加存储权限:
确保在AndroidManifest.xml文件中添加了存储权限:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
2. 从存储卡读取图片文件:
// 获取存储卡根目录
File storageDirectory = Environment.getExternalStorageDirectory();
// 定义图片文件路径
File imageFile = new File(storageDirectory, "example.jpg");
// 检查文件是否存在
if (imageFile.exists()) {
// 从文件读取图片
Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getAbsolutePath());
// 在ImageView中显示图片
imageView.setImageBitmap(bitmap);
} else {
// 文件不存在的处理
}
四、应用组件Application
在安卓应用程序中,Application 类是一个重要的组件,它代表整个应用的生命周期。Application 类是一个全局的单例对象,用于维护应用的全局状态以及执行一些应用范围的初始化工作。
特点
- 全局性:Application 是整个应用程序的上下文环境,它的生命周期与应用程序一致,因此可以在应用程序的任何地方被访问。
- 单例模式:在整个应用程序中,Application 类只有一个实例,即全局唯一,因此可以用来保存全局状态和共享数据。
- 生命周期管理:Application 类可以监听应用程序的生命周期事件,如创建、销毁、进入后台等,方便开发者进行相应的处理。
- 资源访问:通过 Application 类可以方便地访问应用程序的资源,如字符串、颜色、尺寸等。
- 应用程序初始化:可以在 Application 类中进行应用程序的初始化工作,例如初始化全局变量、配置日志、初始化第三方库等。
- 全局配置:Application 类可以保存应用程序的全局配置信息,如主题样式、语言设置等。
- 自定义扩展:开发者可以继承 Application 类,并重写其中的方法,实现自定义的初始化逻辑、全局异常捕获等功能。
综上所述,Application 类在 Android 应用程序中具有重要的地位,是整个应用程序的入口和核心,通过它可以实现全局的状态管理、资源访问、生命周期管理等功能。
application的生命周期
生命周期: Application 对象的生命周期与应用的整个生命周期相同。它在应用启动时创建,在应用退出时销毁。因此,它是一个在整个应用中保持持久状态的理想位置。
在一个App运行过程中有且仅有一个Application对象贯穿整个生命周期。
系统默认的 Application 类会在应用启动时被创建。如果你想自定义应用的全局初始化工作、管理全局状态,或者执行一些应用范围内的操作,那么你可以创建自己的 Application 类,例如命名为 MyApplication
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
// 在这里进行应用的全局初始化工作
// 例如初始化数据库、设置全局变量等
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
// 处理配置变化时的逻辑
// 例如重新加载资源、调整布局等(例如屏幕旋转时)
}
@Override
public void onTerminate() {
// 在应用终止时调用,可以执行一些清理工作
//注意:此方法在普通开发中永远不会调用,可能用于系统开发
super.onTerminate();
}
}
需要注意的是,尽管提供了 onTerminate() 方法,但在正常情况下,Android 并不保证会调用这个方法。当应用的进程终止时,系统可能会立即终止进程,而不等待 onTerminate() 方法执行完毕。因此,开发者应该谨慎使用这个方法,尽量在其他地方进行资源释放和清理工作,例如在 Activity 的 onDestroy() 方法中。
要使用自定义的 Application 类,需要在 AndroidManifest.xml 文件中注册:
<application
android:name=".MyApplication"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
...>
<!-- 其他应用配置 -->
</application>
Application 操作 全局变量
在 Android 应用开发中,Application 类通常被用于保存全局变量,以便在整个应用的生命周期内共享数据。全局变量可以在应用的任何地方访问,包括 Activity、Service、BroadcastReceiver 等组件。
多个组件之间数据共享。举例:两个Activity之间数据共享
Application 对同一个应用程序是唯一的,所以可以使用Application进行数据共享。
全局的意思是其他代码都可以引用该变量,因此全局变量是共享数据和消息传递的好帮手。
适合在Application中保存的全局变量主要有下面3类数据:
- 会频繁读取的信息,如用户名、手机号等。
- 不方便由意图传递的数据,例如位图对象、非字符串类型的集合对象等。
- 容易因频繁分配内存而导致内存泄漏的对象,如Handler对象等。
注意,系统分配给每个应用的内存是有限的,所以不要将太多数据存到内存中。
以下是关于在 Application 类中使用全局变量的一些重要信息:
创建全局变量: 在自定义的 Application 类中,你可以声明全局变量并在 onCreate() 方法中进行初始化。这样的变量通常被设计为单例,以确保在整个应用中只有一个实例。
public class MyApplication extends Application {
private String globalVariable;
@Override
public void onCreate() {
super.onCreate();
// 在这里进行应用的全局初始化工作
// 例如初始化数据库、设置全局变量等
globalVariable = "Initial Value";
}
public String getGlobalVariable() {
return globalVariable;
}
public void setGlobalVariable(String newValue) {
globalVariable = newValue;
}
}
示例:用Application保存map信息
import android.app.Application;
import java.util.HashMap;
import java.util.Map;
public class MyApplication extends Application {
private static MyApplication instance;
private Map<String, Object> myMap;
@Override
public void onCreate() {
super.onCreate();
// 在这里进行应用的全局初始化工作
// 例如初始化数据库、设置全局变量等
instance = this;
myMap = new HashMap<>();
}
public static MyApplication getInstance() {
return instance;
}
public void saveToMap(String key, Object value) {
myMap.put(key, value);
}
public Object getFromMap(String key) {
return myMap.get(key);
}
// 其他全局变量和方法
}
然后,你可以在应用的任何地方通过 MyApplication.getInstance() 来获取 MyApplication 的实例,并使用 saveToMap 和 getFromMap 方法来存储和获取 Map 类型的信息:
public class MyActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 获取 MyApplication 实例
MyApplication myApp = MyApplication.getInstance();
// 存储信息到 Map 中
myApp.saveToMap("key1", "value1");
myApp.saveToMap("key2", 42);
// 从 Map 中获取信息
String value1 = (String) myApp.getFromMap("key1");
int value2 = (int) myApp.getFromMap("key2");
}
}
通过这种方式,你可以在整个应用程序中方便地存储和获取 Map 类型的信息,而不需要传递大量参数或使用其他复杂的方法。