# ContentProvider详解

## 目录
- [ContentProvider作用](#contentprovider作用)
- [URI机制](#uri机制)
- [ContentResolver使用](#contentresolver使用)
- [数据访问权限](#数据访问权限)
- [ContentObserver](#contentobserver)
- [ContentProvider实现](#contentprovider实现)
- [ContentProvider最佳实践](#contentprovider最佳实践)
- [面试常见问题](#面试常见问题)

---

## ContentProvider作用

### 主要作用

1. **数据共享**：应用间共享数据
2. **数据封装**：封装数据访问逻辑
3. **统一接口**：提供统一的数据访问接口
4. **权限控制**：控制数据访问权限

### 使用场景

```java
// 场景1：应用间共享数据
// 应用A提供联系人数据
// 应用B访问联系人数据

// 场景2：封装数据访问
// 统一管理数据库访问
// 隐藏数据存储细节

// 场景3：跨进程数据访问
// 通过 Binder 机制跨进程访问
```

---

## URI机制

### URI 格式

```java
// URI 格式
content://authority/path/id

// 示例
content://com.example.provider/user/1
content://com.example.provider/user
content://com.example.provider/user/1/name
```

### URI 组成

```java
// authority：ContentProvider 的标识
// path：数据路径
// id：数据ID（可选）

// 解析 URI
Uri uri = Uri.parse("content://com.example.provider/user/1");
String authority = uri.getAuthority(); // com.example.provider
List<String> pathSegments = uri.getPathSegments(); // [user, 1]
String id = uri.getLastPathSegment(); // 1
```

### URI 匹配

```java
// 使用 UriMatcher 匹配 URI
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

static {
    uriMatcher.addURI("com.example.provider", "user", USER);
    uriMatcher.addURI("com.example.provider", "user/#", USER_ID);
}

@Override
public Cursor query(Uri uri, String[] projection, String selection,
                   String[] selectionArgs, String sortOrder) {
    switch (uriMatcher.match(uri)) {
        case USER:
            // 查询所有用户
            break;
        case USER_ID:
            // 查询指定用户
            String id = uri.getLastPathSegment();
            break;
    }
}
```

---

## ContentResolver使用

### 查询数据

```java
// 查询数据
Uri uri = Uri.parse("content://com.example.provider/user");
Cursor cursor = getContentResolver().query(
    uri,
    new String[]{"id", "name", "email"}, // projection
    "age > ?", // selection
    new String[]{"18"}, // selectionArgs
    "name ASC" // sortOrder
);

if (cursor != null) {
    while (cursor.moveToNext()) {
        int id = cursor.getInt(cursor.getColumnIndex("id"));
        String name = cursor.getString(cursor.getColumnIndex("name"));
        String email = cursor.getString(cursor.getColumnIndex("email"));
    }
    cursor.close();
}
```

### 插入数据

```java
// 插入数据
Uri uri = Uri.parse("content://com.example.provider/user");
ContentValues values = new ContentValues();
values.put("name", "John");
values.put("email", "john@example.com");
Uri newUri = getContentResolver().insert(uri, values);
```

### 更新数据

```java
// 更新数据
Uri uri = Uri.parse("content://com.example.provider/user/1");
ContentValues values = new ContentValues();
values.put("name", "Jane");
int count = getContentResolver().update(uri, values, null, null);
```

### 删除数据

```java
// 删除数据
Uri uri = Uri.parse("content://com.example.provider/user/1");
int count = getContentResolver().delete(uri, null, null);
```

---

## 数据访问权限

### 权限声明

```xml
<!-- AndroidManifest.xml -->
<provider
    android:name=".MyProvider"
    android:authorities="com.example.provider"
    android:exported="true"
    android:readPermission="com.example.READ_PERMISSION"
    android:writePermission="com.example.WRITE_PERMISSION" />
```

### 权限检查

```java
public class MyProvider extends ContentProvider {
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                       String[] selectionArgs, String sortOrder) {
        // 检查读取权限
        if (getContext().checkCallingOrSelfPermission(
                "com.example.READ_PERMISSION") != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Permission denied");
        }
        // 查询数据
        return null;
    }
}
```

---

## ContentObserver

### ContentObserver 使用

```java
// 监听数据变化
public class MyObserver extends ContentObserver {
    public MyObserver(Handler handler) {
        super(handler);
    }
    
    @Override
    public void onChange(boolean selfChange) {
        super.onChange(selfChange);
        // 数据变化时调用
    }
    
    @Override
    public void onChange(boolean selfChange, Uri uri) {
        super.onChange(selfChange, uri);
        // 指定 URI 的数据变化
    }
}

// 注册观察者
Uri uri = Uri.parse("content://com.example.provider/user");
ContentObserver observer = new MyObserver(new Handler());
getContentResolver().registerContentObserver(uri, true, observer);

// 注销观察者
getContentResolver().unregisterContentObserver(observer);
```

---

## ContentProvider实现

### 完整实现

```java
public class UserProvider extends ContentProvider {
    private static final String AUTHORITY = "com.example.provider";
    private static final String TABLE_USER = "user";
    
    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static final int USER = 1;
    private static final int USER_ID = 2;
    
    static {
        uriMatcher.addURI(AUTHORITY, TABLE_USER, USER);
        uriMatcher.addURI(AUTHORITY, TABLE_USER + "/#", USER_ID);
    }
    
    private SQLiteDatabase database;
    
    @Override
    public boolean onCreate() {
        DatabaseHelper helper = new DatabaseHelper(getContext());
        database = helper.getWritableDatabase();
        return database != null;
    }
    
    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                       String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder queryBuilder = new SQLiteQueryBuilder();
        queryBuilder.setTables(TABLE_USER);
        
        switch (uriMatcher.match(uri)) {
            case USER:
                break;
            case USER_ID:
                queryBuilder.appendWhere("id = " + uri.getLastPathSegment());
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        
        Cursor cursor = queryBuilder.query(database, projection, selection,
            selectionArgs, null, null, sortOrder);
        cursor.setNotificationUri(getContext().getContentResolver(), uri);
        return cursor;
    }
    
    @Override
    public Uri insert(Uri uri, ContentValues values) {
        long id = database.insert(TABLE_USER, null, values);
        if (id > 0) {
            Uri newUri = ContentUris.withAppendedId(uri, id);
            getContext().getContentResolver().notifyChange(newUri, null);
            return newUri;
        }
        return null;
    }
    
    @Override
    public int update(Uri uri, ContentValues values, String selection,
                     String[] selectionArgs) {
        int count;
        switch (uriMatcher.match(uri)) {
            case USER:
                count = database.update(TABLE_USER, values, selection, selectionArgs);
                break;
            case USER_ID:
                String id = uri.getLastPathSegment();
                count = database.update(TABLE_USER, values, "id = ?", new String[]{id});
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }
    
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        int count;
        switch (uriMatcher.match(uri)) {
            case USER:
                count = database.delete(TABLE_USER, selection, selectionArgs);
                break;
            case USER_ID:
                String id = uri.getLastPathSegment();
                count = database.delete(TABLE_USER, "id = ?", new String[]{id});
                break;
            default:
                throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        getContext().getContentResolver().notifyChange(uri, null);
        return count;
    }
    
    @Override
    public String getType(Uri uri) {
        switch (uriMatcher.match(uri)) {
            case USER:
                return "vnd.android.cursor.dir/user";
            case USER_ID:
                return "vnd.android.cursor.item/user";
            default:
                return null;
        }
    }
}
```

---

## ContentProvider最佳实践

### 1. 通知数据变化

```java
// 数据变化时通知观察者
getContext().getContentResolver().notifyChange(uri, null);
```

### 2. 使用事务

```java
database.beginTransaction();
try {
    // 批量操作
    database.setTransactionSuccessful();
} finally {
    database.endTransaction();
}
```

### 3. 权限控制

```java
// 检查权限
if (getContext().checkCallingOrSelfPermission(permission) 
        != PackageManager.PERMISSION_GRANTED) {
    throw new SecurityException("Permission denied");
}
```

---

## 面试常见问题

### Q1: ContentProvider 的作用？

**答案：**
1. 数据共享：应用间共享数据
2. 数据封装：封装数据访问逻辑
3. 统一接口：提供统一的数据访问接口
4. 权限控制：控制数据访问权限

### Q2: URI 的格式？

**答案：**
`content://authority/path/id`
- authority：ContentProvider 标识
- path：数据路径
- id：数据ID（可选）

### Q3: ContentResolver 和 ContentProvider 的关系？

**答案：**
- **ContentResolver**：客户端，用于访问数据
- **ContentProvider**：服务端，提供数据访问接口
- 通过 URI 和 Binder 机制通信

### Q4: ContentObserver 的作用？

**答案：**
- 监听 ContentProvider 数据变化
- 数据变化时自动通知
- 实现数据更新通知机制

---

*最后更新：2024年*
