Content Provider(3)カスタムContentProvider


カスタムContentProviderはContentProviderを継承する
1、契約クラスの定義(Contract Class)
この名前は私が自分で取ったので、翻訳ができないかもしれません.これは私たちが以前使っていたUserDictionaryのような外部インタフェースを提供するためのものです.WORD.Wordsは契約クラスです
public class BookContract {
    public static final String AUTHORITY = "com.ckt.myprovider.bookprovider";

    public static final class ITBook implements BaseColumns {
        public static final String ID = BaseColumns._ID;
        public static final String TITLE = "_title";
        public static final String AUTHOR = "_author";

        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/itbook");

        public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.ckt.itbook";
        public static final String CONTENT_ITME_TYPE = "vnd.android.cursor.item/vnd.ckt.itbook";

        public static final String DEFAULT_SORT = TITLE + " asc";
    }


}

AUTHEORITY:標準的な構成はアプリケーションのパッケージ名+カスタムProvider名で、上のようにパッケージ名はcom.ckt.myprovider、カスタムprovider名サブbookprovider
ITBook:このクラスはitbookテーブルに対応しており、BaseColumnsを実現するのは、このテーブルにBaseColumns_を追加するためです.IDの列、これはListViewなどに必須ですが、もちろん「_id」を付けることもできます
ID,TITLE,AUTORはテーブルのカラム名です
CONTENT_URI:標準構成はcontent://+AUTHEORITY+テーブル名で、こちらはテーブル名をitbookと名付けます
CONTENT_TYPEとCONTENT_ITEM_TYPE:基本フォーマットはtype/subtypeです.Androidで定義されたMIMEは、事業者の規定に合致するカスタムMIMEであるため、typeに対して
vnd.android.cursor.dir //for multiple rows 
vnd.android.cursor.item //for single row

コード内の2行がそれぞれ要求に対応するのは、複数行のデータと1行のデータです.
DEFAULT_SORT:デフォルトのソートです.ここではtitleでソートします.
2、ContentProviderから受け継いだproviderを定義する
package com.ckt.myprovider.book;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;

import java.util.HashMap;
import java.util.StringTokenizer;

import dalvik.annotation.TestTarget;

public class BookProvider extends ContentProvider {

    private ContentResolver mContentResolver;
    private DBHelper mDBHelper;
    private SQLiteDatabase mDatabase;
    private static final int ITBOOK_LIST = 0;
    private static final int ITBOOK_ITEM = 1;
    private static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
    private static HashMap<String, String> mProjectionMap = new HashMap<>();

    static {
        mUriMatcher.addURI(BookContract.AUTHORITY, "itbook", ITBOOK_LIST);
        mUriMatcher.addURI(BookContract.AUTHORITY, "itbook/#", ITBOOK_ITEM);

        mProjectionMap.put(BookContract.ITBook.ID, BookContract.ITBook.ID);
        mProjectionMap.put(BookContract.ITBook.TITLE, BookContract.ITBook.TITLE);
        mProjectionMap.put(BookContract.ITBook.AUTHOR, BookContract.ITBook.AUTHOR);
    }


    public BookProvider() {
    }

    @Override
    public boolean onCreate() {
        Context context = getContext();
        mContentResolver = context.getContentResolver();
        mDBHelper = new DBHelper(context, DB_NAME, null, 1);
        return true;
    }

    @Override
    public String getType(Uri uri) {
        switch (mUriMatcher.match(uri)) {
            case ITBOOK_ITEM:
                return BookContract.ITBook.CONTENT_TYPE;

            case ITBOOK_LIST:
                return BookContract.ITBook.CONTENT_ITME_TYPE;

            default:
                throw new UnsupportedOperationException("Not yet implemented");
        }

    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
        mDatabase = mDBHelper.getReadableDatabase();
        SQLiteQueryBuilder builder = new SQLiteQueryBuilder();
        switch (mUriMatcher.match(uri)) {
            case ITBOOK_ITEM:
                long id = ContentUris.parseId(uri);
                builder.setTables(DB_TABLE);
                builder.setProjectionMap(mProjectionMap);
                builder.appendWhere(BookContract.ITBook.ID + " = " + id);
                break;

            case ITBOOK_LIST:
                builder.setTables(DB_TABLE);
                builder.setProjectionMap(mProjectionMap);
                break;

            default:
                throw new UnsupportedOperationException("Not yet implemented");
        }
        Cursor cursor = builder.query(mDatabase, projection, selection, selectionArgs, null, null, BookContract.ITBook.DEFAULT_SORT);
        //data change ,notify uri
        cursor.setNotificationUri(mContentResolver, uri);
        return cursor;

    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        mDatabase = mDBHelper.getReadableDatabase();
        Uri mNewUri = null;
        long id = 0;
        switch (mUriMatcher.match(uri)) {
            case ITBOOK_ITEM:
                throw new IllegalArgumentException("Error Uri: " + uri);

            case ITBOOK_LIST:
                id = mDatabase.insert(DB_TABLE, null, values);
                break;

            default:
                throw new UnsupportedOperationException("Not yet implemented");
        }

        //if successfully
        if (id > 0) {
            mNewUri = ContentUris.withAppendedId(uri, id);
        }
        //if error occurs
        else {
            throw new SQLiteException("Unable to insert");
        }
        //notify
        mContentResolver.notifyChange(uri, null);
        return mNewUri;
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        mDatabase = mDBHelper.getWritableDatabase();
        int count = 0;
        switch (mUriMatcher.match(uri)) {
            case ITBOOK_ITEM:
                long id = ContentUris.parseId(uri);
                selection = BookContract.ITBook.ID + " = " + id + " " + selection;
                count = mDatabase.delete(DB_TABLE, selection, selectionArgs);
                break;

            case ITBOOK_LIST:
                count = mDatabase.delete(DB_TABLE, selection, selectionArgs);
                break;

            default:
                throw new UnsupportedOperationException("Not yet implemented");
        }
        //notify all uri
        mContentResolver.notifyChange(uri, null);
        //successfully count > 0 , or count =0
        return count;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs) {
        mDatabase = mDBHelper.getWritableDatabase();
        int count = 0;
        switch (mUriMatcher.match(uri)){
            case ITBOOK_ITEM:
                long id = ContentUris.parseId(uri);
                selection = BookContract.ITBook.ID + " = " + id + " " + selection;
                count = mDatabase.update(DB_TABLE,values,selection,selectionArgs);
                break;

            case ITBOOK_LIST:
                count = mDatabase.update(DB_TABLE,values,selection,selectionArgs);
                break;

            default:
                throw new UnsupportedOperationException("Not yet implemented");
        }
        //notify all uris
        mContentResolver.notifyChange(uri,null);
        //successfully count > 0 . or count = 0
        return count;
    }

    private static String DB_NAME = "book.db";
    private static String DB_TABLE = "itbook";
    private static final String SQL_CREATE = "create table " +
            DB_TABLE +
            " ( " +
            BookContract.ITBook.ID + " integer primary key, " +
            BookContract.ITBook.TITLE + " text not null, " +
            BookContract.ITBook.AUTHOR + " text not null)";


    private static class DBHelper extends SQLiteOpenHelper {

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

        @Override
        public void onCreate(SQLiteDatabase db) {
            db.execSQL(SQL_CREATE);
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            db.execSQL("drop table if exists " + DB_TABLE);
            onCreate(db);
        }
    }
}

onCreate():この中で時間のかかる操作を実行しないでください.
getType():URIに基づいて1行を返すCONTENT_を決定するITEM_TYPEかマルチラインのCONTENT_TYPEは,URIを識別するためにUriMatcherクラスを用いた.
    private static final int ITBOOK_LIST = 0;
    private static final int ITBOOK_ITEM = 1;
    private static UriMatcher mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        mUriMatcher.addURI(BookContract.AUTHORITY, "itbook", ITBOOK_LIST);
        mUriMatcher.addURI(BookContract.AUTHORITY, "itbook/#", ITBOOK_ITEM);
    }

Query():SQLiteQueryBuilderを使用してクエリーを構築することを推奨します.クエリの最後にcursorを使用します.setNotificationUri(mContentResolver,uri)は、CursorLoaderなどのuriを傍受するクライアントにデータの更新を通知する
Insert()、update()、insert()は最後にmContentResolverを使用する必要があります.nofityChange(uri, null).
3.マニフェストファイルで定義
        <provider
            android:name=".book.BookProvider"
            android:authorities="com.ckt.myprovider.bookprovider"
            android:enabled="true"
            android:exported="true">
            <grant-uri-permission android:path="/itbook"/>
        </provider>
nameとauthoritiesは言うまでもなく、enabledはtrueのためにシステムがこのproviderを起動することを許可することを指し、exportedはtrueのために他のアプリケーションがこのproviderを使用することを許可することを指す.
4.カスタムプロバイダの使用
package com.ckt.myprovider;

import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.LoaderManager;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.CursorLoader;
import android.content.DialogInterface;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.AdapterView;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.Toast;

import com.ckt.myprovider.book.BookContract;

import static android.widget.Toast.LENGTH_LONG;


public class MainActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks<Cursor> {

    private static final String TAG = "MainActivity";
    private EditText mTitleEt, mAuthorEt;
    private ContentResolver mContentResolver;
    private LayoutInflater inflater;

    private ListView mListView;
    private SimpleCursorAdapter mAdapter;

    private String[] mProjection = {
            BookContract.ITBook.ID,
            BookContract.ITBook.TITLE,
            BookContract.ITBook.AUTHOR};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);
        init();

    }

    private void init() {
        inflater = LayoutInflater.from(getApplicationContext());
        mContentResolver = getContentResolver();
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                doInsert();
            }
        });

        mTitleEt = (EditText) findViewById(R.id.book_title);
        mAuthorEt = (EditText) findViewById(R.id.book_author);

        mListView = (ListView) findViewById(R.id.listview);
        mAdapter = new SimpleCursorAdapter(
                getApplicationContext(),
                R.layout.simple_item,
                null,
                new String[]{BookContract.ITBook.TITLE, BookContract.ITBook.AUTHOR},
                new int[]{R.id.item_title, R.id.item_author},
                0);
        mListView.setAdapter(mAdapter);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this);
                final String author = ((TextView) view.findViewById(R.id.item_author)).getText().toString();
                final String title = ((TextView) view.findViewById(R.id.item_title)).getText().toString();
                View myView = inflater.inflate(R.layout.dialog, null);
                final EditText mDialogAuthor = (EditText) myView.findViewById(R.id.book_author);
                mDialogAuthor.setText(author);
                final EditText mDialogTitle = (EditText) myView.findViewById(R.id.book_title);
                mDialogTitle.setText(title);
                builder.setTitle("Book")
                        .setView(myView)
                                //delete
                        .setNegativeButton("Delete", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                int count = mContentResolver.delete(
                                        BookContract.ITBook.CONTENT_URI,
                                        BookContract.ITBook.TITLE + "= ?",
                                        new String[]{title}
                                );
                                if (count > 0) {
                                    Toast.makeText(MainActivity.this, "Delete Successfully", LENGTH_LONG).show();
                                }
                            }
                        })
                                //update
                        .setPositiveButton("Update", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                ContentValues mUpdateValue = new ContentValues();
                                mUpdateValue.put(BookContract.ITBook.TITLE, mDialogTitle.getText().toString());
                                mUpdateValue.put(BookContract.ITBook.AUTHOR, mDialogAuthor.getText().toString());
                                int count = mContentResolver.update(
                                        BookContract.ITBook.CONTENT_URI,
                                        mUpdateValue,
                                        BookContract.ITBook.TITLE + "=?",
                                        new String[]{title}
                                );
                                if (count > 0) {
                                    Log.d(TAG, "Update, count = " + count);
                                    Toast.makeText(MainActivity.this, "Update Successfully", LENGTH_LONG).show();
                                }
                            }
                        })
                                //nothing
                        .setNeutralButton("Cancel", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {

                            }
                        });
                builder.create().show();
            }
        });

        getLoaderManager().initLoader(0, null, this);
    }

    private void doInsert() {
        String title = mTitleEt.getText().toString();
        String author = mAuthorEt.getText().toString();
        ContentValues mNewValues = new ContentValues();
        mNewValues.put(BookContract.ITBook.TITLE, title);
        mNewValues.put(BookContract.ITBook.AUTHOR, author);
        Log.d(TAG, "title = " + title + " ,author = " + author);
        if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(author)) {
            Uri mNewUri = mContentResolver.insert(BookContract.ITBook.CONTENT_URI, mNewValues);
            if (mNewUri != null) {
                String id = mNewUri.getPathSegments().get(1);
                long id1 = ContentUris.parseId(mNewUri);
                Log.d(TAG, "Insert , id = " + id + " ,id1 = " + String.valueOf(id1));
                mTitleEt.setText(null);
                mAuthorEt.setText(null);
            }
        }
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        CursorLoader cursorLoader = new CursorLoader(
                getApplicationContext(), BookContract.ITBook.CONTENT_URI,
                mProjection,
                null,
                null,
                BookContract.ITBook.DEFAULT_SORT
        );
        return cursorLoader;
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }
}

次の点について説明します.
ListViewのAdapterにデータをロードする場合は、LoaderのCursorLoaderを使用します.データ量の大きいクエリは非常に時間がかかるため、一般的に新しいスレッドを開き、データの変更を傍受して対応する変更を行う必要があります.
CursorLoaderのメリットの使用
(1)任意のActivityとFragmentを適用
(2)非同期ロードの提供
(3)データソースのモニタリング、データの変更、タイムリーな更新
(4)構成(画面方向など)が変更された場合、最後のLoaderのカーソルに再接続し、再クエリする必要はありません.
5.外部アプリケーションアクセスContentProvider権限定義
自分でContentProviderを書いて自分のアプリケーションでこれを使うのは、あまり役に立たないので、直接データベースを使えばいいのではないでしょうか.では、外部のアプリケーションにどのようにアクセスすればいいのでしょうか.
まず、カスタムproviderが読み書き権限を定義しない場合、デフォルトでは、外部アプリケーションは自分のproviderにアクセスできません.これは自分で試してみてください.
では問題が来て、どのように権限を定義しますか?
私たちのproviderアプリケーションのmanifestファイルでは
    <permission android:name="ckt.permission.READ_ITBOOK" />
    <permission android:name="ckt.permission.WRITE_ITBOOK" />

システムに申請する権限を定義しました
今私が使います
        <provider
            android:name=".book.BookProvider"
            android:authorities="com.ckt.myprovider.bookprovider"
            android:writePermission="ckt.permission.WRITE_ITBOOK"
            android:enabled="true"
            android:exported="true"
            android:readPermission="ckt.permission.READ_ITBOOK">
        </provider>

writePermissionとreadPermissionは私たちが使っている読み書き権限です.そうすれば、外部アプリケーションが権限を申請できればcontent uriでcontent providerを操作することができます.
6、外部アプリケーションがカスタムproviderにアクセスする
まず完全な権限を持つことです
    <uses-permission android:name="ckt.permission.READ_ITBOOK" />
    <uses-permission android:name="ckt.permission.WRITE_ITBOOK" />
package com.ckt.readprovider;

import android.app.LoaderManager;
import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {
    private ListView mListView;
    private SimpleCursorAdapter mAdapter;
    private String[] from = {BookContract.ITBook.TITLE, BookContract.ITBook.AUTHOR};
    private int[] to = {R.id.item_title, R.id.item_author};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        mListView = (ListView) findViewById(R.id.listview);
        mAdapter = new SimpleCursorAdapter(MainActivity.this, R.layout.simple_item, null, from, to, 0);
        mListView.setAdapter(mAdapter);
        getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
            @Override
            public Loader<Cursor> onCreateLoader(int id, Bundle args) {
                String[] projection = {BookContract.ITBook.ID, BookContract.ITBook.TITLE, BookContract.ITBook.AUTHOR};
                CursorLoader cursorLoader = new CursorLoader(MainActivity.this,
                        BookContract.ITBook.CONTENT_URI,
                        projection,
                        null,
                        null,
                        BookContract.ITBook.DEFAULT_SORT
                );
                return cursorLoader;
            }

            @Override
            public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
                mAdapter.swapCursor(data);
            }

            @Override
            public void onLoaderReset(Loader<Cursor> loader) {
                mAdapter.swapCursor(null);
            }
        });
    }


}
ここもListViewとCursorLoaderで、ほとんど何も言うことはありません.
多くのデータを露出したくない場合があります.他のアプリケーションにクエリーを提供したい場合は、アクセス権限を制限し、外部アプリケーションが特定のuriにアクセスしてデータをクエリーできるようにします.
        <provider
            android:name=".book.BookProvider"
            android:authorities="com.ckt.myprovider.bookprovider"
            android:grantUriPermissions="true"
            android:enabled="true"
            android:exported="true">
            <grant-uri-permission android:path="/itbook"/>
        </provider>

ここでは読み書き権限は設定されていません.grantUriPermissionでuriに一時的な権限を付与できる権限を定義し、を定義して、どのuriが私たちに与えられた権限なのかを決定しました.もちろん、私が書いた例には表が1つしかありません.もし表authorInfoを追加したら、この著者の情報は、公表したくありません.制限効果を達成できます
データを要求するアプリケーションについては、この権限を知らなければ、この権限を申請することはできません.完全な権限を持つアプリケーションで一時的なuri権限を申請するしかありません.このuri権限を申請しましたが、私たちはこのuriに対応するデータを操作するしかありません.
まず効果を見てみましょう
図の効果は、manifestファイルで権限を申請していないことです(この権限があることは知らないかもしれませんが)、readproviderというアプリケーションにはこの権限が読み取れることを知っています.このアプリケーションにintentを送信して、一時的なuri権限を得ることができ、さらにこのuriを操作してデータを取得することができます.しかし、このreadproviderは私たちに読む権限しか与えていないので、私たちはこのuriで直接update、delete、insertを使うことができません.この時、私たちはこのreadproviderのインタフェースを起動して、ユーザーに自分でこのインタフェースを操作させて、このようにユーザーが見ることができて、データの安全を保証します.
申請権限の適用tempermissionClient
package com.ckt.tempermissionclient;

import android.app.LoaderManager;
import android.content.ComponentName;
import android.content.CursorLoader;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.widget.SimpleCursorAdapter;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;

public class MainActivity extends AppCompatActivity {
    private ListView mListView;
    private SimpleCursorAdapter mAdapter;
    private Button mBtGetTemPer, mBtUpdate;
    private String[] from = {BookContract.ITBook.TITLE, BookContract.ITBook.AUTHOR};
    private int[] to = {R.id.item_title, R.id.item_author};
    private EditText mEtTitle,mEtAuthor;
    private static Uri uri;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);


        mListView = (ListView) findViewById(R.id.listview);
        mAdapter = new SimpleCursorAdapter(MainActivity.this, R.layout.simple_item, null, from, to, 0);
        mListView.setAdapter(mAdapter);
        mBtGetTemPer = (Button) findViewById(R.id.tem);
        mBtGetTemPer.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                intent.setAction("ckt.action.gettempermission");
                startActivityForResult(intent, 888);
            }
        });

        mBtUpdate = (Button) findViewById(R.id.update);
        mBtUpdate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent();
                Bundle bundle = new Bundle();
                bundle.putString("title",mEtTitle.getText().toString());
                bundle.putString("author", mEtAuthor.getText().toString());
                intent.putExtras(bundle);
                intent.setAction("ckt.action.updatebook");
                startActivityForResult(intent,886);
            }
        });

        mEtTitle = (EditText) findViewById(R.id.book_title);
        mEtAuthor = (EditText) findViewById(R.id.book_author);
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == 666) {
            Log.d("david", "requestcode == " + requestCode);
            uri = data.getData();
            getLoaderManager().initLoader(0, null, new LoaderManager.LoaderCallbacks<Cursor>() {
                @Override
                public Loader<Cursor> onCreateLoader(int id, Bundle args) {
                    String[] projection = {BookContract.ITBook.ID, BookContract.ITBook.TITLE, BookContract.ITBook.AUTHOR};
                    CursorLoader cursorLoader = new CursorLoader(MainActivity.this, uri, projection, null, null, BookContract.ITBook.DEFAULT_SORT);
                    return cursorLoader;
                }

                @Override
                public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
                    mAdapter.swapCursor(data);
                }

                @Override
                public void onLoaderReset(Loader<Cursor> loader) {
                    mAdapter.swapCursor(null);
                }
            });
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        getApplicationContext().revokeUriPermission(uri,Intent.FLAG_GRANT_READ_URI_PERMISSION);
    }
}

上記gif図で説明したように、このコードは説明されていません.readproviderの応答のactivityを見てみましょう.
package com.ckt.readprovider;

import android.content.ContentValues;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

public class Main2Activity extends AppCompatActivity {
    private String action;
    private Button mBtUpdate;
    private TextView mTvTitle,mTvAuthor;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        mTvTitle = (TextView) findViewById(R.id.read_title);
        mTvAuthor = (TextView) findViewById(R.id.read_author);
        Intent intent = getIntent();
        action = intent.getAction();
        if (("ckt.action.gettempermission").equals(action)) {
            grantPermission();
        } else if (("ckt.action.updatebook").equals(action)) {
            Bundle bundle = getIntent().getExtras();
            String title = bundle.getString("title");
            String author = bundle.getString("author");
            mTvTitle.setText(title);
            mTvAuthor.setText(author);
        }

        mBtUpdate = (Button) findViewById(R.id.update);

        mBtUpdate.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (("ckt.action.updatebook").equals(action)) {
                    Log.d("david", "insert ~");
                    Bundle bundle = getIntent().getExtras();
                    String title = bundle.getString("title");
                    String author = bundle.getString("author");
                    if (!TextUtils.isEmpty(title) && !TextUtils.isEmpty(author)) {
                        ContentValues contentValues = new ContentValues();
                        contentValues.put(BookContract.ITBook.TITLE, title);
                        contentValues.put(BookContract.ITBook.AUTHOR, author);
                        Log.d("david", "title = " + title + " ,author = " + author);
                        Uri uri = getContentResolver().insert(BookContract.ITBook.CONTENT_URI, contentValues);
                        if(uri != null){
                            Log.d("david","insert successfully");
                            finish();
                        }
                    }
                }
            }
        });
    }

    private void grantPermission() {
        Uri uri = BookContract.ITBook.CONTENT_URI;
        Intent intent = new Intent();
        intent.setData(uri);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        //intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        grantUriPermission("com.ckt.tempermissionclient", uri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
        setResult(666, intent);
        finish();
    }

}

これを見てみましょうsetFlagsとgrantUriPermissonは、すべて権限を付与していますが、どのような違いがありますか?
intent.setFlags:この権限は権限を要求する側のタスクスタックが消えるまで存在しています.つまり、再起動したりback buttonで返したりすると、このタスクスタックが消えてしまいます.自然に権限がありません.みんなで戻るボタンを押して、使う権限がありません.
このgrangUriPermissionは少しキックアスで、手動でContextを呼び出さなければなりません.revokeUriPermissionはuri権限を解放します
はい、基本的にcontent providerをカスタマイズするものはここまで応用されています.baiduとgoogleを検索して多くの文章を検索しました.私は比較的そろっていますが、googleで文章を検索するとき、内部記憶のファイルを外部アプリケーションにどのように提供するかという質問がありました.そこで私が行った記事でContentProviderのサブクラスFileProviderについて紹介しましたが、興味があれば見てみてください. 
私が書いた文章はざらざらしているかもしれませんが、要点がしっかりしていて、何か不備があったら、指摘して、お互いに交流して勉強してもいいですよ.