为了账号安全,请及时绑定邮箱和手机立即绑定

轻松搞定炫彩填充ProgressBar【Android】21.0 权限处理(三)——访问其他程序数据:提供数据给别的程序使

标签:
Android
  • 另一个是新开的项目,ProviderTest,主要是用于操作DatabaseTest应用程序中的数据,可以进行增删查改。
    两个项目的目录如下:


    webp

    2019-02-20_210145.png

    webp

    2019-02-20_210200.png

4.0 先看下DatabaseTest项目运行后的画面:

webp

2019-02-20_210309.png

5.0本篇内容只需要这个应用程序在模拟器上装载即可,不要去点击“创建数据库”按钮,测试运行的时候,可以把Android studio中DatabaseTest项目关闭(稍后还需要向DatabaseTest项目中添加代码)。
6.0 ContentProvider,内容提供器,Android 四大组件之一

(Activity、Service、Broadcast Receiver、Content Provider)

关于什么是Android四大组件:
链接:android四大组件(组成)是什么,功能分别是

想要实现跨程序共享数据功能,官方推荐的方式就是这个——使用内容提供器。

一个程序可以通过实现一个ContentProvider的抽象接口将自己的数据完全暴露出去,而且ContentProvider是以类似数据库中表的方式将数据暴露的。那么外界获取其提供的数据,也就应该与从数据库中获取数据的操作基本一样,只不过是采用URL来表示外界需要访问的“数据库”。

ContentProvider类有6个抽象方法,在使用子类继承它的时候,需要将这6个方法全部重写。比如新建一个MyProvider 继承自ContentProvider ,代码示范如下(本代码和上面所述的两个项目无关,但可以便于我们实际理解):

package com.example.contactstest;import android.content.ContentProvider;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;public class MyProvider extends ContentProvider {    @Override
    public boolean onCreate() {        // TODO: Implement this to initialize your content provider on startup.
        //TODO: 实现此方法以在启动时初始化内容提供程序。
        return false;//返回true表示内容提供器创建成功
    }    @Override
    public String getType(Uri uri) {        // TODO: Implement this to handle requests for the MIME type of the data
        // TODO:实现此方法以处理对数据的mime类型的请求
        //根据传入的URI返回相应的MIME类型
        return null;
    }    @Override
    public Uri insert(Uri uri, ContentValues values) {        // TODO: Implement this to handle requests to insert a new row.
        // TODO: 实现此方法以处理插入新行的请求。
        //uri:用于确定更新哪张表
        // values:数据都保存在这个参数内
        return null;//添加完成后,返回一个用于表示这条新纪录的URI
    }    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
                        String sortOrder) {        // TODO: Implement this to handle query requests from clients.
        //TODO:实现此方法以处理来自客户端的查询请求。
        //uri:用于确定查询哪张表
        //projection:用于确定查询哪列
        //selection和selectionArgs:用于约束确定查询哪些行
        //sortOrder:用于确定采用何种方式进行排序
        return null;

    }    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {         //TODO: Implement this to handle requests to delete one or more rows.
        //TODO:实现此方法以处理删除一行或多行的请求。
        // uri:用于确定删除哪张表中的数据
        //selection和selectionArgs:用于约束确定删除哪些行

        return 0;//被删除的行数将作为返回值返回
    }    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {        // TODO: Implement this to handle requests to update one or more rows.
        // TODO: 实现此方法以处理更新一行或多行的请求。
        // uri:用于确定更新哪张表中的数据
        // values:数据都保存在这个参数内
        // selection和selectionArgs:用于约束确定更新哪些行
        return 0;//受影响的行数将作为返回值返回
    }
}



这里需要特别说明下的是getType( )方法。

一个标准的内容URI的写法是这样的:
content://com.example.app.provider/table1
知道什么是内容URI的程序员看一眼就知道,我们期望访问的是com.example.app这个应用的table1表中的数据,或者格式如下:
content://com.example.app.provider/table1/1 表示我们期望访问的是com.example.app这个应用的table1表中id为1的数据

划线的重点来了:
1.内容URI主要有以上两种格式:

  • 以路径结尾,表示期望访问该表中所有数据。

  • 以id结尾,表示期望访问该表中拥有相应id的数据。

2.可以转成使用通配符来表示,通配符规则如下:
* :表示匹配任意长度的任意字符。
# :表示匹配任意长度的数字。

因此,内容URI分别可以改写成:
content://com.example.app.provider/*
content://com.example.app.provider/table1/#

3.一个内容URI所对应的MIME字符串主要由3部分组成:

  • 必须以vnd开头

  • 如果内容URI以路径结尾,则后接android.cursor.dir/,如果内容URI以id结尾,则后接Android.cursor.item/

  • 最后接上vnd.<authority>.<path>

4.所以对于内容URIcontent://com.example.app.provider/table1所对应的MIME类型格式为:
vnd.android.cursor.dir/vnd.com.example.app.provider/table1
对于内容URIcontent://com.example.app.provider/table/1所对应的MIME类型格式为:
vnd.android.cursor.item/vnd.com.example.app.provider/table1/1

5.MIME数据类型
作用:指定某个扩展名的文件用某种应用程序来打开
如指定.html文件采用text应用程序打开、指定.pdf文件采用flash应用程序打开
(当然,这里其实用不到这个知识点,因为只有上面所述的两种写法)

OK,理论到位,接着往下走。

7.0 这时候我们可以摆弄DatabaseTest项目中的代码了。它需要做到公开自己的数据。

首先,activity_main.xml这个布局文件,不需要起作用(虽然里面有一个Button控件,所以这里就不贴代码了)
然后,MainActivity.java也不需要增加什么代码(虽然里面有个数据库创建的程序响应):

package com.example.databasetest;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;public class MainActivity extends AppCompatActivity {    private MyDatabaseHelper dbHelper;    @Override
    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        dbHelper = new MyDatabaseHelper(this,"BookStore.db",null,1);
        Button createDatabase = (Button) findViewById(R.id.create_database);
        createDatabase.setOnClickListener(new View.OnClickListener() {            @Override
            public void onClick(View v) {
                dbHelper.getWritableDatabase();
            }
        });
    }
}
8.0 MyDatabaseHelper类主要是数据库的创建:
package com.example.databasetest;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import android.widget.Toast;public class MyDatabaseHelper extends SQLiteOpenHelper {    public static final String CREATE_BOOK = "create table Book ("
            + "id integer primary key autoincrement,"
            + "author text,"
            + "price real,"
            + "pages integer,"
            + "name text)";    private Context mContext;    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(CREATE_BOOK);
    }    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory,                            int version) {        super(context, name, factory, version);
        mContext=context;
    }
}

MyDatabaseHelper类继承了SQLiteOpenHelper类,这是一个抽象类,这个抽象类需要重写onCreate()onUpgrade()方法,在这两个方法里面,我们可以实现创建、升级SQLite数据库。

9.0 在目录com.example.databasetest包名上右击,NewOtherContent Provider:

webp

2019-02-20_184028.png

会弹出如下窗口:

webp

2019-02-20_184157.png


我们将内容提供器命名为:DatabaseProvider,authority指定为com.example.databasetest.provider,Exported属性表示是否允许外部程序访问我们的内容提供器,Enable表示是否启用这个内容提供器,都勾上,点击finish创建:


接着修改DatabaseProvider中的代码:

package com.example.databasetest;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;public class DatabaseProvider extends ContentProvider {    public static final int BOOk_DIR = 0;    public static final int BOOK_ITEM = 1;    public static final int CATEGORY_DIR = 2;    public static final int CATEGORY_ITEM = 3;    public static final String AUTHORITY = "com.example.databasetest.provider";    public static UriMatcher uriMatcher;    private MyDatabaseHelper dbHelper;    static {        //uriMatcher中有个addURI()方法,这个方法接收三个参数
        // 分别把authority、path和一个自定义代码传进去。
        //这样可以调用uriMatcher的match()方法时可以将一个Uri对象传入
        //match()方法的返回值是某个能匹配这个Uri对象所对应的自定义代码
        //利用这个代码,我们就可以知道访问的是哪张表中的数据
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);        //BOOk_DIR:表示访问book表中的所有数据
        uriMatcher.addURI(AUTHORITY, "book", BOOk_DIR);        //BOOK_ITEM:表示访问book表中的单条数据
        uriMatcher.addURI(AUTHORITY, "book/#", BOOK_ITEM);        //CATEGORY_DIR:表示访问category表中的所有数据
        uriMatcher.addURI(AUTHORITY, "category", CATEGORY_DIR);        //CATEGORY_ITEM:表示访问category表中的单条数据
        uriMatcher.addURI(AUTHORITY, "category/#", CATEGORY_ITEM);
    }    public DatabaseProvider() {
    }    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {        //TODO: Implement this to handle requests to delete one or more rows.
        //TODO:实现此方法以处理删除一行或多行的请求。
        SQLiteDatabase db = dbHelper.getReadableDatabase();        int deletedRows = 0;        switch (uriMatcher.match(uri)) {            case BOOk_DIR:
                deletedRows = db.delete("Book", selection, selectionArgs);                break;            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                deletedRows = db.delete("Book", "id = ?", new String[]{bookId});                break;            case CATEGORY_DIR:
                deletedRows = db.delete("Category", selection, selectionArgs);                break;            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                deletedRows = db.delete("Category", "id = ?", new String[]{categoryId});                break;            default:                break;
        }        return deletedRows;
    }    @Override
    public String getType(Uri uri) {        // TODO: Implement this to handle requests for the MIME type of the data
        // TODO:实现此方法以处理对数据的mime类型的请求
        // at the given URI.
        switch (uriMatcher.match(uri)) {            //这里可以看到对内容URI转为MIME类型的实际处理
            case BOOk_DIR:                return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.book";            case BOOK_ITEM:                return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.book";            case CATEGORY_DIR:                return "vnd.android.cursor.dir/vnd.com.example.databasetest.provider.category";            case CATEGORY_ITEM:                return "vnd.android.cursor.item/vnd.com.example.databasetest.provider.category";
        }        return null;
    }    @Override
    public Uri insert(Uri uri, ContentValues values) {        // TODO: Implement this to handle requests to insert a new row.
        // TODO: 实现此方法以处理插入新行的请求。
        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Uri uriReturn = null;        switch (uriMatcher.match(uri)) {            case BOOk_DIR:            case BOOK_ITEM:                long newBookId = db.insert("Book", null, values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/book/" + newBookId);                break;            case CATEGORY_DIR:            case CATEGORY_ITEM:                long newCategoryId = db.insert("Category", null, values);
                uriReturn = Uri.parse("content://" + AUTHORITY + "/category/" + newCategoryId);                break;            default:                break;
        }        //因为insert()方法需要返回一个Uri对象,所以还需要把内容URI转成Uri对象

        return uriReturn;
    }    @Override
    public boolean onCreate() {        // TODO: Implement this to initialize your content provider on startup.
        //TODO: 实现此方法以在启动时初始化内容提供程序。

        dbHelper = new MyDatabaseHelper(getContext(), "BookStore.db", null, 2);        return true;
    }    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {        // TODO: Implement this to handle query requests from clients.
        //TODO:实现此方法以处理来自客户端的查询请求。

        SQLiteDatabase db = dbHelper.getReadableDatabase();
        Cursor cursor = null;        switch (uriMatcher.match(uri)) {            case BOOk_DIR:
                cursor = db.query("Book", projection, selection, selectionArgs, null, null,
                        sortOrder);                break;            case BOOK_ITEM:                //访问单条数据时用到这个getPathSegments()方法
                // 它会将内容URI之后的部分以“/”符号进行分割
                // 返回的结果放到一个字符串列表中
                //列表的第0个位置放的是路径,第1个位置放的就是id了
                String bookId = uri.getPathSegments().get(1);
                cursor = db.query("Book", projection, "id = ?", new String[]{bookId}, null, null,
                        sortOrder);                break;            case CATEGORY_DIR:
                cursor = db.query("Category", projection, selection, selectionArgs, null, null,
                        sortOrder);                break;            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                cursor = db.query("Category", projection, "id = ?", new String[]{categoryId},                        null, null, sortOrder);                break;            default:                break;
        }        return cursor;
    }    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {        // TODO: Implement this to handle requests to update one or more rows.
        // TODO: 实现此方法以处理更新一行或多行的请求。
        SQLiteDatabase db = dbHelper.getReadableDatabase();        int updateRows = 0;        switch (uriMatcher.match(uri)) {            case BOOk_DIR:
                updateRows = db.update("Book", values, selection, selectionArgs);                break;            case BOOK_ITEM:
                String bookId = uri.getPathSegments().get(1);
                updateRows = db.update("Book", values, "id = ?", new String[]{bookId});                break;            case CATEGORY_DIR:
                updateRows = db.update("Category", values, selection, selectionArgs);                break;            case CATEGORY_ITEM:
                String categoryId = uri.getPathSegments().get(1);
                updateRows = db.update("Category", values, "id = ?", new String[]{categoryId});                break;            default:                break;
        }        return updateRows;
    }
}

代码说明都在代码块中。

10.0 最后,内容提供器一定要在AndroidManifest.xml中注册,但是由于我们是使用Android studio软件的快捷方式创建的,注册这一步已经自动完成了:
<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.databasetest">

    <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">
        <provider
            android:name=".DatabaseProvider"
            android:authorities="com.example.databasetest.provider"
            android:enabled="true"
            android:exported="true"></provider>

        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application></manifest>

<provider>标签内就是,其中:

  • android:name属性指定了DatabaseProvider的类名

  • android:authorities属性指定了DatabaseProvider的authority

  • android:enabled属性和android:exported属性上面已经说了,根据我们创建时打钩的状态自动生成的,表示DatabaseProvider允许被其他应用程序进行访问。

11.0 DatabaseTest项目代码全部编写完毕。

一开始也说了,本篇内容只需要DatabaseTest这个应用程序在模拟器上装载即可,不要去点击“创建数据库”按钮,运行DatabaseTest项目后,可以把Android studio中DatabaseTest项目关闭了。

12.0 新建项目,ProviderTest。

先看布局文件activity_main.xml

<?xml version="1.0" encoding="utf-8"?><android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/add_data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="添加数据到book"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.20"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.06" />

    <Button
        android:id="@+id/query_data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="从book中查询数据"
        app:layout_constraintBottom_toBottomOf="@+id/add_data"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/add_data"
        app:layout_constraintTop_toTopOf="@+id/add_data" />

    <Button
        android:id="@+id/update_data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        android:text="更新数据到book"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="@+id/add_data"
        app:layout_constraintStart_toStartOf="@+id/add_data"
        app:layout_constraintTop_toBottomOf="@+id/add_data"
        app:layout_constraintVertical_bias="0.06" />

    <Button
        android:id="@+id/delete_data"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="从book中删除数据"
        app:layout_constraintBottom_toBottomOf="@+id/update_data"
        app:layout_constraintEnd_toEndOf="@+id/query_data"
        app:layout_constraintStart_toStartOf="@+id/query_data"
        app:layout_constraintTop_toTopOf="@+id/update_data" /></android.support.constraint.ConstraintLayout>

效果很简单:


webp



作者:我睡醒刚刚
链接:https://www.jianshu.com/p/6d8619252e59


点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消