Android中ContentProvider的理解使用

来源:互联网 发布:马云 英文演讲 知乎 编辑:程序博客网 时间:2024/06/12 20:42

前言



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。
  
  如果要把一个字符串转换成Uri,可以使用Uri类中的parse()方法,如下:
  
  Uri uri = Uri.parse("content://com.contentproviderdb.TestContentProvider/User").

 然后再介绍一下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);


 后续会有代码展示.


 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(){@Overridepublic 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","1120184385@qq.com");contentValues.put("userPwd","qazwsx");getContentResolver().insert(uri,contentValues);}super.handleMessage(msg);}};

登录后打开内容提供者查看数据:




可以看到最后一天咱们是通过其他应用程序插入的.

最后,我们可以想下,如果内容提供者不再了,第三方程序再执行操作会怎么样
答案是肯定的,肯定会出错,下面是出错log

Failed to find provider info for com.contentproviderdb.TestContentProvider09-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)

你想,内容提供者都不在了,你怎么去操作它本身的数据,因此,大家在使用时,多注意下.

这样,对于一些恶心的全家桶软件就很容易理解了,大多数是要数据共享的,比如淘宝作为内容提供者,咸鱼可以使用淘宝数据,天猫也可以使用淘宝数据等,其实也是最基本的一些操作吧,根据自身业务连身定做吧!




原创粉丝点击