前言
Android四大组件对大家来说都不陌生,但是相对于Activity,Service,广播来说,ContentProvider好像很容易被忽略,因为他确实在普通项目中很少使用,不使用它也能很好的完成项目需求,但是ContentProvider我觉得是Android中最牛逼的设计之一了,很多的数据共享都是用它来实现的,最常用的是android获取手机联系人就是用的ContentProvider来实现的.
ContentProvider介绍
以下内容摘自谷歌:
Content providers are one of the primary building blocks of Android applications, providing content to applications. They encapsulate data and provide it to applications through the single ContentResolver interface. A content provider is only required if you need to share data between multiple applications. For example, the contacts data is used by multiple applications and must be stored in a content provider. If you don't need to share data amongst multiple applications you can use a database directly via SQLiteDatabase.
谷歌翻译:
内容提供者是Android应用程序的主要组成部分之一,为应用程序提供内容。 它们封装数据,并通过单个ContentResolver接口将其提供给应用程序。 只有在需要在多个应用程序之间共享数据时,才需要内容提供者。 例如,联系人数据由多个应用程序使用,并且必须存储在内容提供者中。 如果您不需要在多个应用程序之间共享数据,则可以直接通过SQLiteDatabase使用数据库。
白话翻译:
ContentProvider用来实现不同App间的数据共享,就好比手机联系人,它本身的实现方式就使用了ContentProvider,它本身属于“内容的提供者”,它实现机制就是为了数据共享.
因此,手机联系人是内容提供者,是数据的来源,其他第三方的数据需要使用它的数据,可以通过
ContentResolver接口(数据通道)共享给其他应用程序.谷歌还说了,如果不需要共享数据,那就直接使用SQLite数据库吧.
ContentProvider"内容提供者"实现步骤
ContentProvider组成部分
ContentProvider主要通过Uri(统一资源定位符)来向外共享数据的.
Uri由三部分组成(Uri可以类比于http://www.baidu.com,相对于绝对地址,就是数据存储的位置)
1.scheme
ContentProvider(内容提供者)的scheme已经由Android所规定.
scheme为:content://
2.主机名
主机名(或叫Authority)用于唯一标识这个ContentProvider,比如这里 com.contentproviderdb.TestContentProvider,外部调用者可以 根据这个标识来找到它。
3.path
路径(path)可以用来表示我们要操作的数据,路径的构建应根据业务而定,今天demo我新建的User表
/User
上面三个部分是主要的,还可以有更详细的,比如还包含数据id /User/id。
上面三个部分是主要的,还可以有更详细的,比如还包含数据id /User/id。
如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
Uri uri = Uri.parse("content://com.contentproviderdb.TestContentProvider/User").
然后再介绍一下UriMatcher类使用:
因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri 的工具类,分别为UriMatcher和ContentUris 。掌握它们的使用,会便于我们的开发工作。
UriMatcher类用于匹配Uri,它的用法如下:
然后再介绍一下UriMatcher类使用:
因为Uri代表了要操作的数据,所以我们经常需要解析Uri,并从Uri中获取数据。Android系统提供了两个用于操作Uri 的工具类,分别为UriMatcher和ContentUris 。掌握它们的使用,会便于我们的开发工作。
UriMatcher类用于匹配Uri,它的用法如下:
if(uriMatcher.match(uri) == USER)
首先第一步把你需要匹配Uri路径全部给注册上,如下:
uriMatcher.addURI("com.contentproviderdb.TestContentProviderr","User",USER);
注册完需要匹配的Uri后,就可以使用sMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配 码是调用addURI()方法传入的第三个参数。
还有ContentUris
ContentUris类用于操作Uri路径后面的ID部分,它有个比较实用的方法:
withAppendedId(uri, id)用于为路径加上ID部分:
u = ContentUris.withAppendedId(uri,d);
withAppendedId(uri, id)用于为路径加上ID部分:
u = ContentUris.withAppendedId(uri,d);
后续会有代码展示.
ContentProvider使用步骤
跟Activity,Service一样,需要在Android配置文件中配置.
在Application节点内注册TestContentProvider:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".LoginActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".UserListActivity"
android:configChanges="orientation|keyboardHidden"
android:screenOrientation="portrait"
/>
<provider
android:name=".TestContentProvider"
android:authorities="com.contentproviderdb.TestContentProvider"
android:process="com.contentproviderdb.TestContentProvider"
android:exported="true"
/>
</application>
小彩蛋:
android:authorities:最好带上包名写上全部路径名称.
android:process:意思是该provider单独运行于一个进程中,防止获取不到内容提供者数据时,可以尝试加上该属性.
android:exported 为true:该属性默认不加的时候,就是该Provider可以共享数据,可以让其他应用程序使用它的数据,反之,则其他应用程序不能使用它的数据。
ContentProvider Java代码实现
继承ContentProvider,建立TestContentProvider类,里面为CRUD操作(增删改查),和数据库操作一样.其他应用程序操作数据时,就是调用的这个类,大家可以断点调试下.
package com.contentproviderdb;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
/**
* Created by sdj on 2017/9/7.
*/
public class TestContentProvider extends ContentProvider {
DBHelper dbHelper;
@Override
public boolean onCreate() {
dbHelper = new DBHelper(getContext());
return false;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteDatabase db = dbHelper.getReadableDatabase();
Cursor c = db.query("user", null, selection, selectionArgs, null, null, null);
return c;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues contentValues) {
SQLiteDatabase db = dbHelper.getWritableDatabase();
long id = db.insert("user", null, contentValues);
getContext().getContentResolver().notifyChange(uri, null);
return ContentUris.withAppendedId(uri, id);
}
@Override
public int delete(@NonNull Uri uri, @Nullable String s, @Nullable String[] strings) {
return 0;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues contentValues, @Nullable String s, @Nullable String[] strings) {
return 0;
}
}
ContentProvider在自身应用程序中实现数据的插入,展示
向User表插入数据
private void insertUser() {
Uri uri = Uri.parse("content://com.contentproviderdb.TestContentProvider");
ContentValues contentValues = new ContentValues();
contentValues.put("userName", mEmailView.getText().toString());
contentValues.put("userPwd", mPasswordView.getText().toString());
getContentResolver().insert(uri, contentValues);
}
查询插入的数据
URIS还是上面插入的那个地址,我使用TextView简单展示
Uri uri = Uri.parse(URIS);
Cursor cursor = getContentResolver().query(uri, null, null, null, null);
if (null != cursor && cursor.getCount() > 0) {
StringBuilder stringBuilder = new StringBuilder("用户列表如下:\n");
while (cursor.moveToNext()) {
stringBuilder.append(cursor.getString(cursor.getColumnIndex("userName")) + " 密码= " + cursor.getString(cursor.getColumnIndex("userPwd")) + "\n");
}
user.setUserName(stringBuilder.toString());
}
插入效果图:
展示效果图:
使用其他应用程序操作内容提供者数据
这里我使用我其他的一个项目,在其登录成功后,向TestContentProvider插入一条数据。
private Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
if(MyApplication.session==null){
dialog.dismiss();
Toast.makeText(LoginActivity.this, "账号或密码错误", Toast.LENGTH_SHORT).show();
}else{
dialog.dismiss();
Intent intent=new Intent(LoginActivity.this, HomeActivity.class);
startActivity(intent);
finish();
//Toast.makeText(LoginActivity.this, "登入成功", Toast.LENGTH_SHORT).show();
Uri uri = Uri.parse("content://com.contentproviderdb.TestContentProvider");
ContentValues contentValues = new ContentValues();
contentValues.put("userName","[email protected]");
contentValues.put("userPwd","qazwsx");
getContentResolver().insert(uri,contentValues);
}
super.handleMessage(msg);
}
};
登录后打开内容提供者查看数据:
可以看到最后一天咱们是通过其他应用程序插入的.
最后,我们可以想下,如果内容提供者不再了,第三方程序再执行操作会怎么样
答案是肯定的,肯定会出错,下面是出错log
Failed to find provider info for com.contentproviderdb.TestContentProvider
09-07 06:25:57.527 13948-13948/? E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.IllegalArgumentException: Unknown URL content://com.contentproviderdb.TestContentProvider
at android.content.ContentResolver.insert(ContentResolver.java:860)
at com.email.LoginActivity$1.handleMessage(LoginActivity.java:53)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4745)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
at dalvik.system.NativeStart.main(Native Method)
你想,内容提供者都不在了,你怎么去操作它本身的数据,因此,大家在使用时,多注意下.
这样,对于一些恶心的全家桶软件就很容易理解了,大多数是要数据共享的,比如淘宝作为内容提供者,咸鱼可以使用淘宝数据,天猫也可以使用淘宝数据等,其实也是最基本的一些操作吧,根据自身业务连身定做吧!