Android 提供了多种数据存储方案,每种方案都有其特定的使用场景和优缺点。以下是 Android 数据存储的全面解析:

一、SharedPreferences(轻量级键值存储)

适用场景:小量简单数据的持久化(用户设置、应用配置等)

实现原理

  • 基于 XML 文件存储键值对

  • 数据保存在 /data/data/<package-name>/shared_prefs/ 目录下

  • 采用单例模式保证进程内数据一致性

使用示例

// 获取SharedPreferences对象
SharedPreferences sp = getSharedPreferences("user_prefs", Context.MODE_PRIVATE);

// 写入数据
SharedPreferences.Editor editor = sp.edit();
editor.putString("username", "john_doe");
editor.putInt("login_count", 5);
editor.apply(); // 异步提交
// editor.commit(); // 同步提交

// 读取数据
String username = sp.getString("username", "default");
int loginCount = sp.getInt("login_count", 0);

优缺点分析

优点

  • 使用简单,API友好

  • 自动处理文件读写

  • 支持异步提交(apply)和同步提交(commit)

缺点

  • 不适合存储大量数据

  • 没有类型安全保证

  • 多进程访问时可能不一致

  • 性能随数据量增加而下降

高级用法

  • 监听数据变化

    sp.registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> {
        // 处理变化
    });
  • 多进程模式

    // 注意:MODE_MULTI_PROCESS在API 23已废弃
    getSharedPreferences("prefs", Context.MODE_MULTI_PROCESS);

二、文件存储(File Storage)

适用场景:大文件存储(图片、音频、视频等)

存储位置

  1. 内部存储

    • 路径:/data/data/<package-name>/files/

    • 特点:应用私有,卸载时自动删除

  2. 外部存储

    • 公共目录:Environment.getExternalStoragePublicDirectory()

    • 私有目录:Context.getExternalFilesDir()

    • 特点:可能被用户访问,卸载时私有目录删除

使用示例

// 写入内部存储
String filename = "myfile.txt";
String content = "Hello World!";
try (FileOutputStream fos = openFileOutput(filename, Context.MODE_PRIVATE)) {
    fos.write(content.getBytes());
}

// 读取内部存储
try (FileInputStream fis = openFileInput(filename)) {
    InputStreamReader isr = new InputStreamReader(fis);
    BufferedReader br = new BufferedReader(isr);
    StringBuilder sb = new StringBuilder();
    String line;
    while ((line = br.readLine()) != null) {
        sb.append(line);
    }
    String fileContent = sb.toString();
}

// 外部存储检查
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    File externalFile = new File(getExternalFilesDir(null), "external_file.txt");
    // 读写操作...
}

注意事项

  • 需要处理运行时权限(API 23+)

  • 大文件操作应在子线程进行

  • 考虑文件加密(敏感数据)

  • 外部存储可能不可用(SD卡移除等)

三、SQLite 数据库

适用场景:结构化数据存储(用户数据、消息记录等)

核心组件

  1. SQLiteOpenHelper:数据库创建和版本管理

  2. ContentValues:数据包装类

  3. Cursor:查询结果集

使用示例

public class DBHelper extends SQLiteOpenHelper {
    private static final String DB_NAME = "myapp.db";
    private static final int DB_VERSION = 1;
    
    public DBHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
    }
    
    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL("CREATE TABLE users (_id INTEGER PRIMARY KEY, name TEXT, age INTEGER)");
    }
    
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL("DROP TABLE IF EXISTS users");
        onCreate(db);
    }
}

// 插入数据
DBHelper dbHelper = new DBHelper(this);
SQLiteDatabase db = dbHelper.getWritableDatabase();

ContentValues values = new ContentValues();
values.put("name", "Alice");
values.put("age", 25);
long newRowId = db.insert("users", null, values);

// 查询数据
Cursor cursor = db.query("users", 
        new String[]{"_id", "name", "age"},
        "age > ?", new String[]{"20"},
        null, null, "name DESC");

while (cursor.moveToNext()) {
    String name = cursor.getString(cursor.getColumnIndex("name"));
    int age = cursor.getInt(cursor.getColumnIndex("age"));
    // 处理数据...
}
cursor.close();
db.close();

优化建议

  1. 使用事务

    db.beginTransaction();
    try {
        // 批量操作
        db.setTransactionSuccessful();
    } finally {
        db.endTransaction();
    }
  2. 索引优化

    CREATE INDEX idx_users_age ON users(age);
  3. 使用Room替代原生SQLite(见下文)

四、Room 持久化库

适用场景:现代Android应用的数据持久化(官方推荐)

核心组件

  1. @Entity:定义数据表

  2. @Dao:数据库访问对象

  3. @Database:数据库持有类

使用示例

// 定义实体
@Entity
public class User {
    @PrimaryKey(autoGenerate = true)
    public int uid;
    
    @ColumnInfo(name = "first_name")
    public String firstName;
    
    @ColumnInfo(name = "last_name")
    public String lastName;
    
    public int age;
}

// 定义DAO
@Dao
public interface UserDao {
    @Query("SELECT * FROM user")
    List<User> getAll();
    
    @Query("SELECT * FROM user WHERE uid IN (:userIds)")
    List<User> loadAllByIds(int[] userIds);
    
    @Insert
    void insertAll(User... users);
    
    @Delete
    void delete(User user);
}

// 定义数据库
@Database(entities = {User.class}, version = 1)
public abstract class AppDatabase extends RoomDatabase {
    public abstract UserDao userDao();
}

// 使用数据库
AppDatabase db = Room.databaseBuilder(getApplicationContext(),
        AppDatabase.class, "database-name").build();

// 异步查询
AsyncTask.execute(() -> {
    List<User> users = db.userDao().getAll();
    // 更新UI需要切回主线程
});

Room优势

  • 编译时SQL验证

  • 减少样板代码

  • 与LiveData/RxJava无缝集成

  • 内置主线程保护机制

五、ContentProvider(内容提供者)

适用场景:跨应用数据共享(联系人、日历等)

核心概念

  1. URIcontent://<authority>/<path>/<id>

  2. MIME类型vnd.android.cursor.dir/vnd.<company>.<type>

  3. CRUD操作:query, insert, update, delete

实现示例

public class MyProvider extends ContentProvider {
    private static final String AUTHORITY = "com.example.myprovider";
    private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    
    static {
        sUriMatcher.addURI(AUTHORITY, "users", 1);
        sUriMatcher.addURI(AUTHORITY, "users/#", 2);
    }
    
    @Override
    public boolean onCreate() {
        // 初始化数据库等
        return true;
    }
    
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        SQLiteDatabase db = getDatabase();
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
        
        switch (sUriMatcher.match(uri)) {
            case 1:
                qb.setTables("users");
                break;
            case 2:
                qb.setTables("users");
                qb.appendWhere("_id=" + uri.getLastPathSegment());
                break;
            default:
                throw new IllegalArgumentException("Unknown URI");
        }
        
        Cursor c = qb.query(db, projection, selection, selectionArgs, 
                null, null, sortOrder);
        c.setNotificationUri(getContext().getContentResolver(), uri);
        return c;
    }
    
    // 实现其他方法...
}

使用场景

  • 系统数据访问(联系人、媒体库等)

  • 应用插件化架构

  • 统一数据访问接口

六、DataStore(SharedPreferences替代方案)

适用场景:替代SharedPreferences(Jetpack组件)

两种实现

  1. Preferences DataStore:键值对存储

  2. Proto DataStore:类型化对象存储

使用示例

// Preferences DataStore
val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "settings")

val EXAMPLE_COUNTER = preferencesKey<Int>("example_counter")

suspend fun incrementCounter() {
    context.dataStore.edit { settings ->
        val currentCounterValue = settings[EXAMPLE_COUNTER] ?: 0
        settings[EXAMPLE_COUNTER] = currentCounterValue + 1
    }
}

val exampleCounterFlow: Flow<Int> = context.dataStore.data
    .map { preferences ->
        preferences[EXAMPLE_COUNTER] ?: 0
    }

// Proto DataStore
object CounterSerializer : Serializer<Counter> {
    override fun readFrom(input: InputStream): Counter {
        return Counter.parseFrom(input)
    }
    
    override fun writeTo(t: Counter, output: OutputStream) {
        t.writeTo(output)
    }
}

val Context.counterDataStore: DataStore<Counter> by dataStore(
    fileName = "counter.pb",
    serializer = CounterSerializer
)

suspend fun incrementProtoCounter() {
    context.counterDataStore.updateData { currentCounter ->
        currentCounter.toBuilder().setValue(currentCounter.value + 1).build()
    }
}

优势

  • 异步API(基于Kotlin协程/Flow)

  • 类型安全

  • 支持数据一致性事务

  • 无ANR风险

七、网络存储(云端存储)

适用场景:需要跨设备同步或备份的数据

实现方案

  1. Firebase Realtime Database

  2. Firebase Cloud Firestore

  3. 自定义REST API + 本地缓存

架构建议

  • 实现离线优先策略

  • 使用Repository模式封装数据源

  • 考虑数据同步冲突解决方案

八、存储方案选择指南

方案 数据类型 数据量 持久性 查询复杂度 跨应用共享
SharedPreferences 键值对 简单 不支持
文件存储 任意 无/简单 可配置
SQLite 结构化 中-大 复杂 不支持
Room 结构化 中-大 复杂 不支持
ContentProvider 任意 任意 任意 支持
DataStore 键值对/类型化 简单 不支持
网络存储 任意 任意 依赖网络 依赖实现 支持

九、安全最佳实践

  1. 敏感数据加密

    • 使用Android Keystore系统

    • 实现示例:

      public class CryptoHelper {
          private static final String KEY_ALIAS = "MyKeyAlias";
          private static final String AES_MODE = "AES/GCM/NoPadding";
          
          public byte[] encrypt(byte[] input) throws Exception {
              KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
              keyStore.load(null);
              
              if (!keyStore.containsAlias(KEY_ALIAS)) {
                  KeyGenerator keyGenerator = KeyGenerator.getInstance(
                          KeyProperties.KEY_ALGORITHM_AES, "AndroidKeyStore");
                  
                  keyGenerator.init(new KeyGenParameterSpec.Builder(KEY_ALIAS,
                          KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
                          .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
                          .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
                          .setRandomizedEncryptionRequired(false)
                          .build());
                  
                  keyGenerator.generateKey();
              }
              
              Cipher cipher = Cipher.getInstance(AES_MODE);
              cipher.init(Cipher.ENCRYPT_MODE, keyStore.getKey(KEY_ALIAS, null));
              
              return cipher.doFinal(input);
          }
      }
  2. 文件权限控制

    // 仅限本应用访问
    openFileOutput("private.txt", Context.MODE_PRIVATE);
    
    // 允许其他应用读取
    openFileOutput("public.txt", Context.MODE_WORLD_READABLE); // API 17已废弃
  3. 数据库安全

    • 使用SQLCipher加密SQLite数据库

    • Room集成示例:

      SupportFactory factory = new SupportFactory(
              SQLiteDatabase.getBytes("myPassword".toCharArray()));
      
      AppDatabase db = Room.databaseBuilder(context, AppDatabase.class, "encrypted.db")
              .openHelperFactory(factory)
              .build();

十、性能优化技巧

  1. IO操作优化

    • 使用缓冲流(BufferedInputStream/BufferedOutputStream)

    • 大文件使用NIO(FileChannel)

    • 避免主线程IO

  2. 数据库优化

    • 合理使用索引

    • 批量操作使用事务

    • 定期VACUUM(SQLite维护命令)

  3. 缓存策略

    • 内存缓存(LruCache)

    • 磁盘缓存(DiskLruCache)

    • 多级缓存架构

  4. 数据分页

    • Room的Paging库

    • SQLite的LIMIT/OFFSET

      @Query("SELECT * FROM users LIMIT :limit OFFSET :offset")
      List<User> getUsers(int limit, int offset);

十一、测试策略

  1. 单元测试

    • 使用AndroidX Test

    • 内存数据库测试

      @Test
      public void insertAndGetUser() {
          User user = new User("John", "Doe");
          userDao.insert(user);
          List<User> users = userDao.getAll();
          assertEquals(1, users.size());
      }

  2. UI测试

    • Espresso测试数据绑定

      @Test
      public void listScrollTest() {
          onView(withId(R.id.recyclerView))
              .perform(RecyclerViewActions.scrollToPosition(50));
      }

  3. 性能测试

    • Android Profiler监控

    • 严格模式检测

      StrictMode.setVmPolicy(new VmPolicy.Builder()
              .detectLeakedSqlLiteObjects()
              .penaltyLog()
              .build());

十二、未来趋势

  1. DataStore全面替代SharedPreferences

  2. Room功能增强(多表关联、复杂类型支持)

  3. 云端同步无缝集成

  4. NoSQL解决方案增多(如MongoDB Realm)

  5. 区块链存储探索

通过全面理解Android的各种存储方案及其适用场景,开发者可以为应用选择最合适的数据持久化策略,在保证性能和安全的同时提供良好的用户体验。

Logo

魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。

更多推荐