Android四大组件之ContentProvider

前言

Hi,你们好,咱们又双叒叕见面啦,为了让你们快速的学习Android知识,咱们天天都在更新文章,相信小伙伴们已经开始眼熟咱们了!这一期咱们讲解ContentProvider(内容提供者)相关知识,他也是咱们近期更新的Android四大组件中最后一个。话很少说,让咱们赶忙开始学习吧~java

简介

ContentProvider是Android系统中为开发者专门提供的不一样应用间进行数据共享的组件,其提供了一套标准的接口用来获取以及操做数据,准许开发者把本身的应用数据根据需求开放给其余应用进行增删改查,而无须担忧直接开放数据库权限而带来的安全问题。系统预置了许多ContentProvider用于获取用户数据,例如消息、联系人、日程表等。android

具体形式以下图所示:数据库

使用方式

1 ContentResolver

在ContentProvider的使用过程当中,须要借用ContentResolver来控制ContentProvider所暴露处理的接口,做为代理来间接操做ContentProvider以获取数据。安全

在 Context.java 的源码中以下抽象方法app

/** Return a ContentResolver instance for your application's package. */
    public abstract ContentResolver getContentResolver();
复制代码

因此能够在全部继承Context的类中经过 getContentResovler() 方法获取ContentResolveride

ContentResolver contentResolver = getContentResovler();
复制代码
2 ContentProvider

ContentProvider做为Android四大组件之一,并无Activity那样复杂的生命周期,只有简单地onCreate过程。学习

建立一个自定义ContentProvider的方式是继承ContentProvider类并实现其六个抽象方法:ui

/** * @author: 下码看花 * date: 2019/8/8 * description: ContentProvider例子 */
public class MyContentProvider extends ContentProvider {

	/** * 执行初始化工做 * @return */    
	@Override    
	public boolean onCreate() {
      	return false;    
	}    

	/** * 查询数据 * @param uri * @param projection * @param selection * @param selectionArgs * @param sortOrder * @return */    
	@Override    
	public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
      	return null;    
	}    

	/** * 经过Uri返回对应的MIME类型 * @param uri * @return */    
	@Override    
	public String getType(@NonNull Uri uri) {
      	return null;    
	}    

	/** * 插入新数据 * @param uri * @param values * @return */    
	@Override    
	public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
		return null;   
	} 
	
	/** * 删除已有数据 * @param uri * @param selection * @param selectionArgs * @return */   
	@Override    
	public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
		return 0;    
	}

	/** * 更新数据 * @param uri * @param values * @param selection * @param selectionArgs * @return */    
	@Override    
	public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
		return 0;    
	}
}
复制代码

以后,须要在AdnroidManifest.xml中对ContentProvider进行注册spa

<!-- xxx为上一层包名 -->
<provider android:name=".MyContentProvider" android:authorities="com.xmkh.MyContentProvider" android:exported="true"/>
复制代码

name:自定义的ContentProvider的全称类名。代理

authorities:自定义ContentProvider的惟一标识,外部应用经过该属性值来访问咱们的ContentProvider。所以该属性值必须是惟一的,建议在命名时以包名为前缀。

exported:代表是否容许其余应用调用ContentProvider,true表示支持,false表示不支持。默认值根据开发者的属性设置而会有所不一样,若是包含 Intent-Filter 则默认值为true,不然为false。

3 Uri

观察MyContentProvider中的几个方法,能够发现除了 onCreate() 方法外,其它五个抽象方法都包含了一个Uri(统一资源标识符)参数,经过这个对象能够来匹配对应的请求。那么从ContentProvider的数据操做方法能够看出都依赖于Uri,而对于Uri有其固定的数据格式,例如:

content://com.xmkh.contentproviderdemo.MyContentProvider/article

好比,ContentProvider中操做的数据能够都是从SQLite数据库中获取的,而数据库中可能存在许多张表,这时候就须要用到Uri来代表是要操做哪一个数据库、操做数据库的哪张表了

/** * @author: 下码看花 * date: 2019/8/8 * description: URI组装代码示例 */
public class TestContract {

    public static final String CONTENT_AUTHORITY = "com.xmkh.contentproviderdemo.MyContentProvider";

    public static final Uri BASE_CONTENT_URI = Uri.parse("content://" + CONTENT_AUTHORITY);

    public static final String PATH_ARTICLE = "article";

    public static final class TestEntry implements BaseColumns {

        public static final Uri CONTENT_URI = BASE_CONTENT_URI.buildUpon().appendPath(PATH_ARTICLE).build();

        protected static Uri buildUri(long id) {
            return ContentUris.withAppendedId(CONTENT_URI, id);
        }

        protected static final String TABLE_NAME = "article";

        public static final String COLUMN_NAME = "name";
    }
}
复制代码

从上面代码咱们能够看到,咱们建立了一个 content://com.xmkh.contentproviderdemo.MyContentProvider/article的uri,而且开了一个静态方法,用以在有新数据产生时根据id生成新的Uri。

那么如何肯定一个Uri是要执行哪项操做呢?这里须要用到UriMatcher来帮助ContentProvider匹配Uri,它仅包含了两个方法:

addURI(String authority, String path, int code):用于向UriMatcher对象注册Uri。authtity是在AndroidManifest.xml中注册的ContentProvider的authority属性值;path表示一个路径,能够设置为通配符,#表示任意数字,*表示任意字符;二者组合成一个Uri,而code则表明该Uri对应的标识码

match(Uri uri):匹配传入的Uri。返回addURI()方法中传递的code参数,若是找不到匹配的标识码则返回-1

实战

讲了这么多,相信你们已经有一个初步的了解,为了让咱们加深记忆,跟我一块儿写一个demo吧!

首先,自定义一个ContentProvider,而后向其写入和读取数据,使用SQLite做为ContentProvider的数据存储地址和数据来源,所以须要先创建一个SQLiteOpenHelper,建立一个名为"article.db"的数据库,包含“article”和“author”两张表:

/** * @author: 下码看花 * date: 2019/8/8 * description: 演示demo数据库 */
public class DbOpenHelper extends SQLiteOpenHelper {
    
	/** * 数据库名 */
    private static final String DATA_BASE_NAME = "article.db";

    /** * 数据库版本号 */
    private static final int DATE_BASE_VERSION = 1;

    /** * 表名-文章 */
    public static final String ARTICLE_TABLE_NAME = "article";

    /** * 表名-做者 */
    public static final String AUTHOR_TABLE_NAME = "author";

    /** * 建立表-文章(两列:主键自增加、文章名称) */
    private final String CREATE_ARTICLE_TABLE = "create table " + ARTICLE_TABLE_NAME
            + "(_id integer primary key autoincrement, articleName text)";

    /** * 建立表-做者(三列:主键自增加、做者名、性别) */
    private final String CREATE_AUTHOR_TABLE = "create table " + AUTHOR_TABLE_NAME
            + "(_id integer primary key autoincrement, authorName text, sex text)";

    public DbOpenHelper(Context context) {
        super(context, DATA_BASE_NAME, null, DATE_BASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_ARTICLE_TABLE);
        db.execSQL(CREATE_AUTHOR_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }


}
复制代码

自定义ContentProvider 当中,getTableName(Uri uri)方法用于断定Uri指向的数据库表名 而后在initProviderData()方法中向数据库插入一些原始数据

为了方便你们理解,咱们将上述出现的代码进行修改,展现给你们:

/** * @author: 下码看花 * date: 2019/8/8 * description: ContentProvider例子 */
public class MyContentProvider extends ContentProvider {

    
    private Context mContext;

    private SQLiteDatabase sqLiteDatabase;

    public static final String AUTHORITY = "com.xmkh.MyContentProvider";

    public static final int ARTICLE_URI_CODE = 0;

    public static final int AUTHOR_URI_CODE = 1;

    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        uriMatcher.addURI(AUTHORITY, DbOpenHelper.ARTICLE_TABLE_NAME, ARTICLE_URI_CODE);
        uriMatcher.addURI(AUTHORITY, DbOpenHelper.AUTHOR_TABLE_NAME, AUTHOR_URI_CODE);
    }


    private String getTableName(Uri uri) {
        String tableName = null;
        switch (uriMatcher.match(uri)) {
            case ARTICLE_URI_CODE:
                tableName = DbOpenHelper.ARTICLE_TABLE_NAME;
                break;
            case AUTHOR_URI_CODE:
                tableName = DbOpenHelper.AUTHOR_TABLE_NAME;
                break;
        }
        return tableName;
    }

    public MyContentProvider() {

    }


    /** * 执行初始化工做 * * @return */
    @Override
    public boolean onCreate() {
        mContext = getContext();
        initArticleProviderData();
        return false;
    }


    //初始化原始数据
    private void initArticleProviderData() {
        sqLiteDatabase = new DbOpenHelper(mContext).getWritableDatabase();
        sqLiteDatabase.beginTransaction();
        ContentValues contentValues = new ContentValues();
        contentValues.put("articleName", "Android四大组件之Activity");
        sqLiteDatabase.insert(DbOpenHelper.ARTICLE_TABLE_NAME, null, contentValues);
        contentValues.put("articleName", "Android四大组件之BroadcastReceiver");
        sqLiteDatabase.insert(DbOpenHelper.ARTICLE_TABLE_NAME, null, contentValues);
        contentValues.put("articleName", "Android四大组件之Service");
        sqLiteDatabase.insert(DbOpenHelper.ARTICLE_TABLE_NAME, null, contentValues);
        contentValues.clear();

        contentValues.put("authorName", "ptt");
        contentValues.put("sex", "女");
        sqLiteDatabase.insert(DbOpenHelper.AUTHOR_TABLE_NAME, null, contentValues);
        contentValues.put("authorName", "HiYoung");
        contentValues.put("sex", "男");
        sqLiteDatabase.insert(DbOpenHelper.AUTHOR_TABLE_NAME, null, contentValues);
        contentValues.put("authorName", "gy");
        contentValues.put("sex", "男");
        sqLiteDatabase.insert(DbOpenHelper.AUTHOR_TABLE_NAME, null, contentValues);
        sqLiteDatabase.setTransactionSuccessful();
        sqLiteDatabase.endTransaction();
    }


    /** * 查询数据 * * @param uri * @param projection * @param selection * @param selectionArgs * @param sortOrder * @return */
    @Override
    public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        String tableName = getTableName(uri);
        if (tableName == null) {
            throw new IllegalArgumentException("Unsupported URI:" + uri);
        }
        return sqLiteDatabase.query(tableName, projection, selection, selectionArgs, null, null, sortOrder, null);
    }

    /** * 经过Uri返回对应的MIME类型 * * @param uri * @return */
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }

    /** * 插入新数据 * * @param uri * @param values * @return */
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        String tableName = getTableName(uri);
        if (tableName == null) {
            throw new IllegalArgumentException("Unsupported URI:" + uri);
        }
        sqLiteDatabase.insert(tableName, null, values);
        mContext.getContentResolver().notifyChange(uri, null);
        return uri;
    }

    /** * 删除已有数据 * * @param uri * @param selection * @param selectionArgs * @return */
    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        String tableName = getTableName(uri);
        if (tableName == null) {
            throw new IllegalArgumentException("Unsupported URI:" + uri);
        }
        int count = sqLiteDatabase.delete(tableName, selection, selectionArgs);
        if (count > 0) {
            mContext.getContentResolver().notifyChange(uri, null);
        }
        return count;
    }

    /** * 更新数据 * * @param uri * @param values * @param selection * @param selectionArgs * @return */
    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
        String tableName = getTableName(uri);
        if (tableName == null) {
            throw new IllegalArgumentException("Unsupported URI:" + uri);
        }
        int row = sqLiteDatabase.update(tableName, values, selection, selectionArgs);
        if (row > 0) {
            mContext.getContentResolver().notifyChange(uri, null);
        }
        return row;
    }

}
复制代码

ContentProvider建立好切记必定要在AndroidManifest.xml中注册!(前文已经提到了如何注册,我就再也不复述啦~)

而后分别操做article和author两张表,向其插入一条数据后Log输出全部的数据

/** * @author: 下码看花 * date: 2019/8/8 * description: ContentProvider实战 */
public class MainActivity extends AppCompatActivity {
    
 	 private final String TAG = "MainActivity";

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

        Uri articleUri = Uri.parse("content://com.xmkh.MyContentProvider/article");
        ContentValues contentValues = new ContentValues();
        contentValues.put("articleName", "Android四大组件之ContentProvider");
        getContentResolver().insert(articleUri, contentValues);
        Cursor articleCursor = getContentResolver().query(articleUri, new String[]{"_id", "articleName"}, null, null, null);
        if (articleCursor != null) {
            while (articleCursor.moveToNext()) {
                Log.e(TAG, "ID:" + articleCursor.getInt(articleCursor.getColumnIndex("_id"))
                        + " ArticleName:" + articleCursor.getString(articleCursor.getColumnIndex("articleName")));
            }
            articleCursor.close();
        }

        Uri authorUri = Uri.parse("content://com.xmkh.MyContentProvider/author");
        contentValues.clear();
        contentValues.put("authorName", "Austen");
        contentValues.put("sex", "男");
        getContentResolver().insert(authorUri, contentValues);
        Cursor authorCursor = getContentResolver().query(authorUri, new String[]{"_id", "authorName", "sex"}, null, null, null);
        if (authorCursor != null) {
            while (authorCursor.moveToNext()) {
                Log.e(TAG, "ID:" + authorCursor.getInt(authorCursor.getColumnIndex("_id"))
                        + " AuthorName:" + authorCursor.getString(authorCursor.getColumnIndex("authorName"))
                        + " Sex:" + authorCursor.getString(authorCursor.getColumnIndex("sex")));
            }
            authorCursor.close();
        }
    }

}
复制代码

获得的输出结果是:

结语

ContentProvider做为Android的四大组件之一,虽然说咱们平时用的并很少,可是做为安卓四大组件之一,其地位不容忽视。 小伙伴们赶忙上手实操,把它灵活的运用到项目中,让咱们天天一块儿快乐的进步吧~

PS:若是还有未看懂的小伙伴,欢迎加入咱们的QQ技术交流群:892271582,里面有各类大神回答小伙伴们遇到的问题哦~

相关文章
相关标签/搜索