• Android_四大组件之ContentProvider


    一、概述

    ContentProvider(内容提供者)管理对结构化数据集的访问,它们封装数据,并提供用于定义数据安全性的机制。其他应用,通过Context的ContentResolver对象 作为客户端 与ContentProvider进行通信,访问操作数据。

    Android本身的ContentProvider,我们比较常见的有,视频、音频、图像、个人信息等数据。

    下面通过简单例子进行说明(数据存储形式为SQLite数据库).

    二、基础知识

    1.Uri

    通用资源标识符(Universal Resource Identifer),标明了一个数据操作的地址。

    content://<authority>/<path>:指向表的路径

    content://<authority>/<path>/<id>指向单行的路径

    包括整个提供程序的符号名称(其授权authority)和一个指向表或文件的名称(路径path),可选 ID 部分指向表中的单个行。

    授权:ContentProvider都有单一的授权,避免与其他的ContentProvider冲突,所以要唯一。建议如果Android 软件包名称为 com.example.<appname>,则应为提供程序提供 com.example.<appname>.provider 授权。

    路径:指向数据库的表或文件,如果你有数据库table1,则Uri:  授权/table1

    Uri ID:ContentPorvider会将该 ID 值与表的 _ID 列进行匹配,并对匹配的行执行请求的访问。

    UriMatcher:它会通过addURI将授权和路径 映射到整型上,可以在后续通过switch对特定一个和多个Uri进行操作。match()方法会将Uri返回整型。

    注:这里讲到的Uri只是关于ContentProvider使用到的相关部分,Uri远不止这一点。简单扩展一点:

    关于文件存储部分,会涉及到Uri的scheme是file的,如:

    file:///storage/emulated/0/beam/image_to_share.jpg

    Uri(android.net.Uri),URI(java.net.URI):Uri可以看作andorid对java的URI的扩展,对于URI的组成结构可以查看API。

    Uri的语法及组成,下面几个是一步步的划分:

    [scheme:]scheme-specific-part[#fragment]  //基本组成
    [scheme:][//authority][path][?query][#fragment]  //进一步划分
    

    依据:Uri构建和解析要符合RFC 2396

    2.SQLiteDatabase

     SQLiteDatabase是Android提供的管理SQLite database的对象,提供了数据库的基本的操作(增删改查)方法以及执行SQL语句。数据库名 在某应用里需要唯一,不需要在整个系统中唯一。

    创建的数据库在/data/data/<package_name>/databases/xxx.db。 我们可以通过adb shell进入手机,通过sqlite3调试查看调试数据库里的数据。

    sqlite3的基本操作:进入到对应应用的数据库文件夹后

    (1)sqlite3  xxx.db:  键入这个命令后 你就能执行SQLite的各种命令了

    (2). tables: 显示数据库中的表名,注意前面的.(点)啊

    (3)知道表名后就能 通过SQL命令 操作对应的表了,如select * from TABLE1;(不要忘了分号)

    (4) . quit  和 . exit:退出sqlite,  注意前面的.(点)啊

    三、基本用法

    下面通过具体例子 简单说明下 ContentProvider的简单应用。

    1.通过provider在清单文件AndroidManifest.xml中 配置:

            <provider
                android:authorities="com.flx.testapp.provider"
                android:name=".MyContentProvider"
                android:exported="true"/>

    这里的授权名称 在后续都是很重要的。

    2.通过继承SQLiteOpenHelper 创建数据库。MyDatabaseHelper.java

    package com.flx.testcontentprovider;
    
    import android.content.Context;
    import android.database.sqlite.SQLiteDatabase;
    import android.database.sqlite.SQLiteOpenHelper;
    
    public class MyDatabaseHelper extends SQLiteOpenHelper {
        //_id, name, age
    
        final private static String DBNAME = "test.db";
        final public static String TABLE1 = "table1";
        final private static String SQL_CREATE_TAB = "CREATE TABLE TABLE1 " +
                "(_ID INTEGER PRIMARY KEY, NAME VARCHAR(20), AGE INT(10))";
    
        public MyDatabaseHelper(Context context) {
            super(context, DBNAME, null, 1);
        }
    
        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(SQL_CREATE_TAB);
        }
    
        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //TODO
        }
    }

    从代码中可以看到,创建的数据库名叫test.db, 里面只有一个表table1. 表里面3个字段(id,name,age)。很简单的数据库。

    3.清单里配置的ContentProvider的名字是MyContentProvider。代码如下:

    import android.content.ContentProvider;
    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;
    
    public class MyContentProvider extends ContentProvider {
        private MyDatabaseHelper mMyDatabaseHelper;
        final private static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        final private static int TABLE1_CODE = 1;
    
        static {
            mUriMatcher.addURI("com.flx.testapp.provider", "table1", TABLE1_CODE);
        }
    
        @Override
        public boolean onCreate() {
            mMyDatabaseHelper = new MyDatabaseHelper(getContext());
            return true;
        }
    
        @Override
        public Cursor query(Uri uri,String[] projection,  String selection,  String[] selectionArgs,  String sortOrder) {
            SQLiteDatabase sqLiteDatabase = mMyDatabaseHelper.getWritableDatabase();
            Cursor cursor = null;
            if (mUriMatcher.match(uri) == TABLE1_CODE) {
                cursor = sqLiteDatabase.query(MyDatabaseHelper.TABLE1, projection, selection,selectionArgs,null,null,sortOrder);
                Log.d("flx_provider", "MyContentProvider->query()");
            } else {
                Log.d("flx_provider", "MyContentProvider->query() the patch is not match");
            }
            return cursor;
        }
    
        
        @Override
        public String getType(Uri uri) {
            return null;
        }
    
        
        @Override
        public Uri insert( Uri uri,  ContentValues values) {
            SQLiteDatabase sqLiteDatabase = mMyDatabaseHelper.getWritableDatabase();
            if(mUriMatcher.match(uri) == TABLE1_CODE) {
                long rowNum = sqLiteDatabase.insert(MyDatabaseHelper.TABLE1, null, values);
                Log.d("flx_provider", "MyContentProvider->insert() rowNum="+rowNum);
            } else {
                Log.d("flx_provider", "MyContentProvider->insert() the patch is not match");
            }
            return null;
        }
    
        @Override
        public int delete( Uri uri,  String selection,  String[] selectionArgs) {
            return 0;
        }
    
        @Override
        public int update( Uri uri,  ContentValues values,  String selection,  String[] selectionArgs) {
            return 0;
        }
    }

    onCreate()中,通过创建MyDatabaseHelper对象 连接数据库,执行了创建数据的命令,数据库被创建了。

    自定义的ContentPorvider要重写增(insert)删(delete)改(update)查(query)等方法,上述代码只实现了insert()和query(),依次两个为说明。

    上述代码创建了UriMatcher, 定义了Uri规则(前面有简单介绍):addURI中是授权(清单文件中配置时设置的一致),路径(这里是表名),映射的整型。这里映射的整型 在重写的操作数据库方法中 操作哪个数据 是一个重要判断。

    final private static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        final private static int TABLE1_CODE = 1;
    
        static {
            mUriMatcher.addURI("com.flx.testapp.provider", "table1", TABLE1_CODE);
        }
    

      

    4.上述部分,基本ContentProvider的内容都已经完成了。下面是具体的activity中如何去调用的(这里在同一个应用中调用的)

    布局文件activity_main.xml,只有两个按钮,增 查

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <Button android:id="@+id/add_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="AddData"/>
    
        <Button android:id="@+id/query_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="QueryData" />
    
    </LinearLayout>

    MainActivity.java,具体操作待用代码

    import android.content.ContentValues;
    import android.database.Cursor;
    import android.net.Uri;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.util.Log;
    import android.view.View;
    import android.widget.Button;
    
    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            Button addButton = findViewById(R.id.add_btn);
            addButton.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d("flx_provider", "ManiActivity->oncreate() addButton click");
                    Uri uri = Uri.parse("content://com.flx.testapp.provider/table1");
                    String names[] = {"AA", "BB", "CC"};
                    int ages[] = {18, 19, 20};
                    for (int i = 0;i < names.length;i++) {
                        ContentValues values = new ContentValues();
                        values.put("name", names[i]);
                        values.put("age", ages[i]);
                        getContentResolver().insert(uri, values);
                    }
                }
            });
    
            Button queryBtn = findViewById(R.id.query_btn);
            queryBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    Log.d("flx_provider", "ManiActivity->oncreate() queryBtn click");
                    Uri uri = Uri.parse("content://com.flx.testapp.provider/table1");
                    Cursor cursor = getContentResolver().query(uri, null, "name=?",
                            new String[]{"AA"},null);
                    if (cursor != null && cursor.getCount() > 0) {
                        cursor.moveToFirst();
                        do {
                            Log.d("flx_provider", "ManiActivity->oncreate() query results:" + cursor.getString(0)
                                    + "   " + cursor.getString(1) + "   " + cursor.getString(2));
                        } while (cursor.moveToNext());
                        cursor.close();
                    }
                }
            });
        }
    }

    两个按钮的功能很简单,增加按钮:插入3组数据了。 查询按钮:查询name为AA的数据并返回打印log了。

    下面来看看真个内部数据库情况和log流程。

    (1)当应用编译安装后,通过adb shell进入手机中看到,对应应用下数据库文件还不存在。

    (2)首先我们点击查询(QueryData)按钮,这是数据库还不存在。点击后,log如下

    2019-08-03 08:44:44.336 28711-28711/com.flx.testcontentprovider D/flx_provider: ManiActivity->oncreate() queryBtn click
    2019-08-03 08:44:44.428 28711-28711/com.flx.testcontentprovider D/flx_provider: MyContentProvider->query()
    

      

    从log看,执行到了MyContentProvider->query(),数据库创建被执行。我们进入数据库,看下数据库中是什么情况。

    从下面看到此时的数据库里没有数据。

    (3)点击增加(AddData)按钮。

    2019-08-03 08:50:50.295 28711-28711/com.flx.testcontentprovider D/flx_provider: ManiActivity->oncreate() addButton click
    2019-08-03 08:50:50.305 28711-28711/com.flx.testcontentprovider D/flx_provider: MyContentProvider->insert() rowNum=1
    2019-08-03 08:50:50.312 28711-28711/com.flx.testcontentprovider D/flx_provider: MyContentProvider->insert() rowNum=2
    2019-08-03 08:50:50.317 28711-28711/com.flx.testcontentprovider D/flx_provider: MyContentProvider->insert() rowNum=3
    

      通过log看,增加了3条数据,通过sqlite3查看如下,3条数据是插入成功的。

    (4)点击查询(QueryData)按钮,查询OK的。

    2019-08-03 09:01:57.995 28711-28711/com.flx.testcontentprovider D/flx_provider: ManiActivity->oncreate() queryBtn click
    2019-08-03 09:01:58.001 28711-28711/com.flx.testcontentprovider D/flx_provider: MyContentProvider->query()
    2019-08-03 09:01:58.005 28711-28711/com.flx.testcontentprovider D/flx_provider: ManiActivity->oncreate() query results:1   AA   18
    

      

    (5)在点击依次增加按钮

    2019-08-03 09:02:55.125 28711-28711/com.flx.testcontentprovider D/flx_provider: ManiActivity->oncreate() addButton click
    2019-08-03 09:02:55.132 28711-28711/com.flx.testcontentprovider D/flx_provider: MyContentProvider->insert() rowNum=4
    2019-08-03 09:02:55.138 28711-28711/com.flx.testcontentprovider D/flx_provider: MyContentProvider->insert() rowNum=5
    2019-08-03 09:02:55.147 28711-28711/com.flx.testcontentprovider D/flx_provider: MyContentProvider->insert() rowNum=6
    

      

    (6)点击查询,查询的数据是OK的。

    2019-08-03 09:03:37.950 28711-28711/com.flx.testcontentprovider D/flx_provider: ManiActivity->oncreate() queryBtn click
    2019-08-03 09:03:37.960 28711-28711/com.flx.testcontentprovider D/flx_provider: MyContentProvider->query()
    2019-08-03 09:03:37.965 28711-28711/com.flx.testcontentprovider D/flx_provider: ManiActivity->oncreate() query results:1   AA   18
    2019-08-03 09:03:37.965 28711-28711/com.flx.testcontentprovider D/flx_provider: ManiActivity->oncreate() query results:4   AA   18
    

      

  • 相关阅读:
    igraph——图挖掘助力社会网络分析
    python杀死线程
    深入浅出Node.js(四):Node.js的事件机制
    VS.NET调试问题
    Web服务器Tomcat JDK环境变量设置DOS BAT批处理脚本 陈光剑
    sp_help
    JadePool应用范例:查询与分页
    事关就业,我是否应该诚实?
    Linux SWAP 交换分区配置说明
    对学习数据结构的建议
  • 原文地址:https://www.cnblogs.com/fanglongxiang/p/11304243.html
Copyright © 2020-2023  润新知