Android中ContentProvider的理解使用

前言



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(){
		@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","1120184385@qq.com");
				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)

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

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




评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值