Android 四大组件之 ContentProvider

ContentProvider 作为四大组件之一,广泛用于进程间的数据共享,包括多用户之间的数据共享。

记录一个简单的实例, 插入、查询、删除、更新联系人,其中可以选择根据id操作,  需结合数据库 SQLiteOpenHelper。

1. 继承自ContentProvider

实现query/insert/delete/update 操作, 并在onCreate的时候,创建一个SQLiteOpenHelper实例。

package com.example.personprovider;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

//更多介绍 https://www.jianshu.com/p/ea8bc4aaf057
public class PersonProvider extends ContentProvider {
    private static final String TAG = "PersonProvider";


    private static final String AUTH = "com.example.personprovider";
    private static final String DATA_PATH = "person";
    private static final int MATCH_CODE_WITHOUT_ID = 1;
    private static final int MATCH_CODE_WITH_ID = 2;

    private static UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH);
    private DataHelper dataHelper;

    //保存一些合法的Uri
    static {
        //不根据id查  -> "/person"
        matcher.addURI(AUTH, "/" + DATA_PATH, 1); // code 代表类型
        //根据id 查  -> "/person/#"
        matcher.addURI(AUTH, "/" + DATA_PATH + "/#", 2); //#表示任意正数? 传入的时候需要是具体的数值,才能匹配到code
    }

    public PersonProvider() {
        logD("constructor call");
    }

    @Override
    public boolean onCreate() {
        logD("- onCreate");
        dataHelper = new DataHelper(getContext());
        return false;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        logD("- query uri=" + uri + ", projection=" + projection + ", selection=" + selection + ", selectionArgs="
                + selectionArgs + ", sortOrder=" + sortOrder);

        //得到连接对象
        SQLiteDatabase database = dataHelper.getReadableDatabase();

        //1. 匹配 uri, 返回code
        int code = matcher.match(uri);
        logD("- query code=" + code);
        if (code == MATCH_CODE_WITHOUT_ID) {//不根据id查
            Cursor cursor = database.query(DATA_PATH, projection, selection, selectionArgs, null, null, null);
            return cursor;
        } else if (code == MATCH_CODE_WITH_ID) {//根据id 查
            long id = ContentUris.parseId(uri);
            Cursor cursor = database.query(DATA_PATH, projection, "_id=?", new String[]{id + ""}, null, null, null);
            return cursor;
        } else {
            throw new RuntimeException("查询的uri 不合法");
        }
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {

        return null;
    }

    /**
     * "content://com.example.personprovider/person/#" --》 根据id 插入 (#代表id)
     * "content://com.example.personprovider/person" --》 不根据id 插入
     */
    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        logD("insert uri=" + uri + ", values=" + values);

        //得到连接对象
        SQLiteDatabase database = dataHelper.getReadableDatabase();
        //1. 匹配 uri, 返回code
        int code = matcher.match(uri);
        logD("insert code=" + code);
        //如果合法,则插入
        if (code == MATCH_CODE_WITHOUT_ID) {
            long id = database.insert(DATA_PATH, null, values);
            uri = Uri.withAppendedPath(uri, id + "");
            database.close();
            logD("insert without id, insert to id=" + id);
            return uri;
        } else if (code == MATCH_CODE_WITH_ID) {
            long id = database.insert(DATA_PATH, null, values);
            database.close();
            //uri = Uri.withAppendedPath(uri, id + ""); // values 中的id
            logD("insert with id=" + id + ", uri=" + uri);
            return uri;
        } else {
            throw new RuntimeException("插入的uri 不合法");
        }
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        int deleteCount = 0;

        //得到连接对象
        SQLiteDatabase database = dataHelper.getReadableDatabase();
        //1. 匹配 uri, 返回code
        int code = matcher.match(uri);
        logD("delete code=" + code);
        if (code == MATCH_CODE_WITHOUT_ID) {
            deleteCount = database.delete(DATA_PATH, selection, selectionArgs);
            database.close();
        } else if (code == MATCH_CODE_WITH_ID) {
            long id = ContentUris.parseId(uri);
            deleteCount = database.delete(DATA_PATH, "_id=?", new String[]{id + ""});
            database.close();
        } else {
            throw new RuntimeException("删除的uri 不合法");
        }
        logD("delete success deleteCount=" + deleteCount);
        return deleteCount;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        int updateCount = 0;

        //得到连接对象
        SQLiteDatabase database = dataHelper.getReadableDatabase();
        //1. 匹配 uri, 返回code
        int code = matcher.match(uri);
        logD("update code=" + code);
        if (code == MATCH_CODE_WITHOUT_ID) {
            updateCount = database.update(DATA_PATH, values, selection, selectionArgs);
            database.close();
        } else if (code == MATCH_CODE_WITH_ID) {
            long id = ContentUris.parseId(uri);
            updateCount = database.update(DATA_PATH, values, "_id=?", new String[]{id + ""});
            database.close();
        } else {
            throw new RuntimeException("删除的uri 不合法");
        }
        logD("delete success updateCount=" + updateCount);
        return updateCount;
    }

    private void logD(String message) {
        Log.d(TAG, message);
    }
}
package com.example.personprovider;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DataHelper extends SQLiteOpenHelper {

    private static final String PERSON_DB_NAME = "simple.db";
    private static final String TAG = "DataHelper";

    public DataHelper(Context context) {
        super(context, PERSON_DB_NAME, null, 1);

        logD("DataHelper constructor ");
       
        getReadableDatabase();//TODO - CHECK
        logD("DataHelper() constructor end - after call getReadableDatabase ");
    }


    @Override
    public void onCreate(SQLiteDatabase db) {
        logD("onCreate() DB has been created!!!");
        db.execSQL("create table person(_id integer primary key autoincrement, name varchar)");
        db.execSQL("insert into person (name) values ('Petter')");
        db.execSQL("insert into person (name) values ('Petter')");
    }

    @Override
    public void onOpen(SQLiteDatabase db) {
        logD("onOpen - DB has been opened ");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        logD("onOpen() oldVersion=" + oldVersion + ", newVersion=" + newVersion);
    }

    private void logD(String message) {
        Log.d(TAG, message);
    }
}

并且在AndroidManifest中声明 provider:

        <provider
            android:authorities="com.example.personprovider"
            android:name="com.example.personprovider.PersonProvider"
            android:exported="true">

需要有默认的Activity才能创建???

2. 客户端调用

package com.example.personclient;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

public class PersonActivity extends AppCompatActivity implements View.OnClickListener {
    private static final String TAG = "MainActivity";
    private static final String PREFIX = "content://";
    private static final String AUTH = "com.example.personprovider";
    private static final String DATA_PATH = "person";

    private static final String URI_WITHOUT_ID = PREFIX + AUTH + "/" + DATA_PATH;
    private static final String URI_WITH_ID = PREFIX + AUTH + "/" + DATA_PATH + "/";

    EditText mIdEditText;
    EditText mNameEditText;

    CustomButton mQueryButton;
    CustomButton mInsertButton;
    CustomButton mDeleteButton;
    CustomButton mUpdateButton;
    CustomButton mCurrentClickedButton;

    CheckBox mIdCheckBox;
    Button mExecuteButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_person);

        initUI();

        showToast("我是 " + this.getClass().getSimpleName());
    }

    private void initUI() {
        mIdEditText = findViewById(R.id.id_et);
        mNameEditText = findViewById(R.id.name_et);
        mIdCheckBox = findViewById(R.id.id_cb);

        initButton();
        reset();
    }

    private void initButton() {
        mQueryButton = findViewById(R.id.query_button);
        mInsertButton = findViewById(R.id.insert_button);
        mDeleteButton = findViewById(R.id.delete_button);
        mUpdateButton = findViewById(R.id.update_button);
        mExecuteButton = findViewById(R.id.execute_btn);
        mCurrentClickedButton = null;

        mQueryButton.setCallBack(new CustomButton.CallBack() {
            @Override
            public void onPerform() {
                query();
            }
        });
        mInsertButton.setCallBack(new CustomButton.CallBack() {
            @Override
            public void onPerform() {
                insert();
            }
        });
        mDeleteButton.setCallBack(new CustomButton.CallBack() {
            @Override
            public void onPerform() {
                delete();
            }
        });
        mUpdateButton.setCallBack(new CustomButton.CallBack() {
            @Override
            public void onPerform() {
                update();
            }
        });
    }

    private String getId() {
        if (mIdEditText != null && (mIdEditText.getVisibility() == View.VISIBLE)
                && !TextUtils.isEmpty(mIdEditText.getText())) {
            return mIdEditText.getText().toString();
        }
        return null;
    }

    private String getName() {
        if (mNameEditText != null && (mNameEditText.getVisibility() == View.VISIBLE)
                && !TextUtils.isEmpty(mNameEditText.getText())) {
            return mNameEditText.getText().toString();
        }
        return null;
    }

    private Uri getUri(String id) {
        Uri uri;
        if (!TextUtils.isEmpty(id)) {
            uri = Uri.parse(URI_WITH_ID + id); //根据id 查
        } else {
            uri = Uri.parse(URI_WITHOUT_ID); //不根据id 查
        }
        logD(" getUri  id = " + id + ", uri=" + uri);
        return uri;
    }

    /**
     * "content://com.example.personprovider/person/#" --》 根据id 查 (#代表id)
     * "content://com.example.personprovider/person" --》 不根据id 查
     */
    public void query() {
        //1 得到ContentResolver 对象
        ContentResolver resolver = getContentResolver();

        //2 创建Uri, 调用其query, 得到cursor
        Uri uri = getUri(getId());
        logD(" query  uri=" + uri);

        //3. 取出cursor中的数据,并显示
        Cursor cursor = resolver.query(uri, null, null, null, null);
        logD(" query cursor=" + cursor);
        if (cursor == null) {
            Toast.makeText(this, "cursor is null, and uri=" + uri, Toast.LENGTH_SHORT).show();
        } else {
            logD(" query cursor count=" + cursor.getCount());
            showToast("共查询到 " + cursor.getCount() + " 条结果");
            while (cursor.moveToNext()) {
                int id = cursor.getInt(0);
                String name = cursor.getString(1);
                Toast.makeText(this, "查询结果: id = " + id + ", name= " + name, Toast.LENGTH_SHORT).show();
            }
            cursor.close();
        }
    }

    /**
     * "content://com.example.personprovider/person/#" --》 根据id 插入 (#代表id), 需要传具体的id
     * "content://com.example.personprovider/person" --》 不根据id 插入
     * 插入已存在的row ,则会保错
     * // SQLiteDatabase:   android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed:
     * //                      person._id (code 1555 SQLITE_CONSTRAINT_PRIMARYKEY[1555])
     */
    public void insert() {
        //1 得到 ContentResolver 对象
        ContentResolver resolver = getContentResolver();

        //2 创建Uri, 设置插入的值, 调用其 ContentResolver.insert
        String id = getId();
        Uri uri = getUri(id);
        //设置值
        ContentValues contentValues = new ContentValues();
        if (!TextUtils.isEmpty(id)) {
            contentValues.put("_id", Integer.parseInt(id));
        }
        String name = getName();
        if (!TextUtils.isEmpty(name)) {
            contentValues.put("name", name);
        }
        logD(" insert before insert uri=" + uri + ", contentValues=" + contentValues);

        // 3. 插入
        Uri insertUri = resolver.insert(uri, contentValues);
        showToast("插入后的uri=" + insertUri + "");
        logD(" insert after insert uri=" + insertUri);
    }

    public void delete() {
        //1 得到 ContentResolver 对象
        ContentResolver resolver = getContentResolver();

        //2 创建Uri (先获取要删除的id)
        String id = getId();
        Uri uri = getUri(id);
        logD(" delete id=" + id + ", uri=" + uri);

        //3 调用其 ContentResolver.delete
        int deleteCount = resolver.delete(uri, null, null);
        showToast("删除了 " + deleteCount + " 条记录");
    }

    public void update() {
        //1 得到 ContentResolver 对象
        ContentResolver resolver = getContentResolver();

        //2 创建Uri (先获取要删除的id)
        String id = getId();
        Uri uri = getUri(id);
        logD(" delete id=" + id + ", uri=" + uri);

        //更新值
        ContentValues contentValues = new ContentValues();
        if (!TextUtils.isEmpty(id)) {
            contentValues.put("_id", Integer.parseInt(id));
        }
        String name = getName();
        if (!TextUtils.isEmpty(name)) {
            contentValues.put("name", name);
        }
        logD(" update uri=" + uri + ", contentValues=" + contentValues);

        //3 调用其 ContentResolver.update
        int updateCount = resolver.update(uri, contentValues, null, null);
        showToast("更新 " + updateCount + " 条记录");
    }

    public boolean useId() {
        return mIdCheckBox != null && mIdCheckBox.isChecked();
    }

    private void logD(String message) {
        Log.d(TAG, message);
    }

    private void showToast(String message) {
        Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onClick(View v) {
        logD("onClick v=" + v);
        if (v == null) {
            logD("onClick v is null , return");
        }

        switch (v.getId()) {
            case R.id.insert_button:
                mCurrentClickedButton = mInsertButton;
                updateCheckState();
                break;
            case R.id.query_button:
                mCurrentClickedButton = mQueryButton;
                updateCheckState();
                break;
            case R.id.delete_button:
                mCurrentClickedButton = mDeleteButton;
                updateCheckState();
                break;
            case R.id.update_button:
                mCurrentClickedButton = mUpdateButton;
                updateCheckState();
                break;

            case R.id.id_cb:
                CheckBox cb = (CheckBox) v;
                setIdEditTextVisible(cb.isChecked());
                break;

            case R.id.execute_btn:
                execute();
                break;
            default:
                break;
        }
    }

    private void execute() {
        if (mCurrentClickedButton != null && mCurrentClickedButton.mCallBack != null) {
            mCurrentClickedButton.mCallBack.onPerform();
        }
        reset();
    }

    private void reset(){
        if (mNameEditText != null) {
            mNameEditText.setText("");
        }
        if (mIdEditText != null) {
            mIdEditText.setText("");
        }
        if(mIdCheckBox!=null){
            mIdCheckBox.setChecked(false);
        }
        setIdEditTextVisible(false);
        setNameEditTextVisible(false);
    }

    //任何时候只有一个能被选中
    private void updateCheckState() {
        mQueryButton.setChecked(mCurrentClickedButton == mQueryButton);
        mInsertButton.setChecked(mCurrentClickedButton == mInsertButton);
        mDeleteButton.setChecked(mCurrentClickedButton == mDeleteButton);
        mUpdateButton.setChecked(mCurrentClickedButton == mUpdateButton);

        setNameEditTextVisible(mCurrentClickedButton == mInsertButton || mCurrentClickedButton == mUpdateButton);
    }

    private void setIdEditTextVisible(boolean visible) {
        if (mIdEditText == null) {
            return;
        }
        mIdEditText.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
    }

    private void setNameEditTextVisible(boolean visible) {
        if (mNameEditText == null) {
            return;
        }
        mNameEditText.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
    }
}

其中,CustonButton 是为了统一处理点击事件

package com.example.personclient;

import android.content.Context;
import android.util.AttributeSet;

import androidx.appcompat.widget.AppCompatCheckBox;

public class CustomButton extends AppCompatCheckBox {

    public CallBack mCallBack;

    public CustomButton(Context context) {
        super(context);
    }

    public CustomButton(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public interface CallBack {
        void onPerform();
    }

    public void setCallBack(CallBack mCallBack) {
        this.mCallBack = mCallBack;
    }
}

布局文件 activity_person.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/first_row_guide_line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.005" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/second_row_guide_line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.1" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/third_row_guide_line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.30" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/fourth_row_guide_line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        app:layout_constraintGuide_percent="0.45" />

    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/first_column_guide_line"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layout_constraintGuide_percent="0.05" />

    <TextView
        android:id="@+id/tip_text"
        android:layout_width="300dp"
        android:layout_height="50dp"
        android:text="根据条件进行数据库操作"
        android:gravity="center_vertical"
        android:textSize="@dimen/title_size"
        app:layout_constraintStart_toEndOf="@id/first_column_guide_line"
        app:layout_constraintTop_toBottomOf="@id/first_row_guide_line"/>

    <com.example.personclient.CustomButton
        android:id="@+id/delete_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="删除联系人"
        android:textSize="@dimen/title_size"
        app:layout_constraintStart_toStartOf="@id/insert_button"
        app:layout_constraintTop_toBottomOf="@id/insert_button" />

    <com.example.personclient.CustomButton
        android:id="@+id/update_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="更新联系人"
        android:textSize="@dimen/title_size"
        app:layout_constraintStart_toStartOf="@id/delete_button"
        app:layout_constraintTop_toBottomOf="@id/delete_button" />

    <com.example.personclient.CustomButton
        android:id="@+id/query_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="查询联系人"
        android:textSize="@dimen/title_size"
        app:layout_constraintStart_toStartOf="@id/first_column_guide_line"
        app:layout_constraintTop_toBottomOf="@id/second_row_guide_line" />


    <com.example.personclient.CustomButton
        android:id="@+id/insert_button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:onClick="onClick"
        android:text="插入联系人"
        android:textSize="@dimen/title_size"
        app:layout_constraintStart_toStartOf="@id/query_button"
        app:layout_constraintTop_toBottomOf="@id/query_button" />

    <LinearLayout
        android:id="@+id/id_layout"
        android:layout_width="wrap_content"
        android:layout_height="55dp"
        android:orientation="horizontal"
        app:layout_constraintStart_toEndOf="@id/first_column_guide_line"
        app:layout_constraintTop_toBottomOf="@id/third_row_guide_line">

        <CheckBox
            android:id="@+id/id_cb"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:onClick="onClick"/>

        <TextView
            android:id="@+id/id_text"
            android:layout_width="wrap_content"
            android:layout_height="40dp"
            android:layout_marginLeft="@dimen/margin_left"
            android:maxLines="1"
            android:text="@string/use_id_to_operate"
            android:textSize="@dimen/title_size"/>

        <EditText
            android:id="@+id/id_et"
            android:layout_width="120dp"
            android:layout_height="45dp"
            android:hint="输入id..."
            android:layout_marginLeft="@dimen/margin_left"
            android:inputType="number" />
    </LinearLayout>

    <EditText
        android:id="@+id/name_et"
        android:layout_width="110dp"
        android:layout_height="45dp"
        android:hint="输入名字..."
        android:inputType="textCapCharacters"
        android:visibility="visible"
        app:layout_constraintTop_toBottomOf="@id/id_layout"
        app:layout_constraintStart_toStartOf="@id/first_column_guide_line" />

    <Button
        android:id="@+id/execute_btn"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="开始执行"
        android:textSize="@dimen/title_size"
        android:onClick="onClick"
        app:layout_constraintTop_toBottomOf="@id/fourth_row_guide_line"
        app:layout_constraintStart_toEndOf="@id/first_column_guide_line"/>

</androidx.constraintlayout.widget.ConstraintLayout>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值