美文网首页
Android:ContentProvider总结

Android:ContentProvider总结

作者: 我在等你回复可你没回 | 来源:发表于2019-03-24 21:59 被阅读0次

    如何使用contentprovider?

    1.新建一个类继承于ContentProvider。
    public class BedWatchingProvider extends ContentProvider

    2.在AndroidManifest中定义她,并且定义他的authorities

            <provider
                android:name=".BedWatchingProvider"
                android:authorities="com.wenfengtou.bedwatching.bedwatchingprovider"
                android:enabled="true"
                android:exported="true"></provider>
    

    3.新建一个数据库帮助类
    private static class DatabaseOpenHelper extends SQLiteOpenHelper

    4.定义Uri匹配
    匹配bedwatching table
    sMatcher.addURI(AUTHORITY, "bedwatching", URI_Bed_Watching);

    5.在客户端使用contentResolver去获取

                    ContentResolver contentResolver = MainActivity.this.getContentResolver();
                    Uri uri = new Uri.Builder().scheme(ContentResolver.SCHEME_CONTENT).authority("com.wenfengtou.bedwatching.bedwatchingprovider")
                            .appendPath("bedwatching").build();
                    Cursor cursor = contentResolver.query(uri,null,null,null,null);
                    if (cursor.moveToFirst()) {
                        Log.i(TAG,"print " + cursor.getColumnIndex("name") + " " + cursor.getString(cursor.getColumnIndex("name")));
                    }
    

    android.database.CursorIndexOutOfBoundsException: Index -1 requested, with a size of 1 怎么解决?

    获取到数据库的Cursor后,需要首先移动一个游标。
    参考:https://blog.csdn.net/woshixuye/article/details/8260547
    https://blog.csdn.net/niuba123456/article/details/42638285

    QLiteDatabase: android.database.sqlite.SQLiteConstraintException: UNIQUE constraint failed 怎么解决?

    是因为重复写数据导致的。我的代码定义了INTEGER PRIMARY KEY AUTOINCREMENT。这个是主键,主键不能重复先的。例如0写了,不能再次直接写入0。
    参考:https://blog.csdn.net/wwzqj/article/details/7801702

    FileProvider与ContentProvider的区别?

    ContentProvider启动流程

    03-29 23:32:30.629  3485  3485 I BedWatchingProvider:   at com.wenfengtou.bedwatching.BedWatchingProvider.onCreate(BedWatchingProvider.java:58)
    03-29 23:32:30.629  3485  3485 I BedWatchingProvider:   at android.content.ContentProvider.attachInfo(ContentProvider.java:1748)
    03-29 23:32:30.629  3485  3485 I BedWatchingProvider:   at android.content.ContentProvider.attachInfo(ContentProvider.java:1723)
    03-29 23:32:30.629  3485  3485 I BedWatchingProvider:   at android.app.ActivityThread.installProvider(ActivityThread.java:5153)
    03-29 23:32:30.629  3485  3485 I BedWatchingProvider:   at android.app.ActivityThread.installContentProviders(ActivityThread.java:4748)
    03-29 23:32:30.629  3485  3485 I BedWatchingProvider:   at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4688)
    03-29 23:32:30.629  3485  3485 I BedWatchingProvider:   at android.app.ActivityThread.-wrap1(ActivityThread.java)
    

    publishContentProviders做了什么?

    在system_server进程生成一个hash变量mProviderMap,key是authority,value是ContentProviderRecord

    ContentResolver查找流程?

    确认与AMS进行binder通信的实体先,首先我们可以确认客户端和服务端的接口都是IContentProvider,这个类定义了客户端和服务端通用的接口,其实我们看哪个类继承与IInterface,就知道它是一个binder接口。

    public interface IContentProvider extends IInterface {
        public Cursor query(String callingPkg, Uri url, String[] projection, String selection,
                String[] selectionArgs, String sortOrder, ICancellationSignal cancellationSignal)
                        throws RemoteException;
        public String getType(Uri url) throws RemoteException;
        public Uri insert(String callingPkg, Uri url, ContentValues initialValues)
                throws RemoteException;
        public int bulkInsert(String callingPkg, Uri url, ContentValues[] initialValues)
                throws RemoteException;
        public int delete(String callingPkg, Uri url, String selection, String[] selectionArgs)
                throws RemoteException;
        public int update(String callingPkg, Uri url, ContentValues values, String selection,
                String[] selectionArgs) throws RemoteException;
        public ParcelFileDescriptor openFile(
                String callingPkg, Uri url, String mode, ICancellationSignal signal,
                IBinder callerToken)
                throws RemoteException, FileNotFoundException;
        public AssetFileDescriptor openAssetFile(
                String callingPkg, Uri url, String mode, ICancellationSignal signal)
                throws RemoteException, FileNotFoundException;
        public ContentProviderResult[] applyBatch(String callingPkg,
                ArrayList<ContentProviderOperation> operations)
                        throws RemoteException, OperationApplicationException;
        public Bundle call(
                String callingPkg, String method, @Nullable String arg, @Nullable Bundle extras)
                throws RemoteException;
        public ICancellationSignal createCancellationSignal() throws RemoteException;
    
        public Uri canonicalize(String callingPkg, Uri uri) throws RemoteException;
        public Uri uncanonicalize(String callingPkg, Uri uri) throws RemoteException;
    
        // Data interchange.
        public String[] getStreamTypes(Uri url, String mimeTypeFilter) throws RemoteException;
        public AssetFileDescriptor openTypedAssetFile(String callingPkg, Uri url, String mimeType,
                Bundle opts, ICancellationSignal signal) throws RemoteException, FileNotFoundException;
    
        /* IPC constants */
        static final String descriptor = "android.content.IContentProvider";
    
        static final int QUERY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
        static final int GET_TYPE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 1;
        static final int INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 2;
        static final int DELETE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 3;
        static final int UPDATE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 9;
        static final int BULK_INSERT_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 12;
        static final int OPEN_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 13;
        static final int OPEN_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 14;
        static final int APPLY_BATCH_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 19;
        static final int CALL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 20;
        static final int GET_STREAM_TYPES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 21;
        static final int OPEN_TYPED_ASSET_FILE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 22;
        static final int CREATE_CANCELATION_SIGNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 23;
        static final int CANONICALIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 24;
        static final int UNCANONICALIZE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 25;
    }
    

    谁实现这个接口就是binder实体,所以ContentProviderNative就是binder实体,但是它是抽象的,所以不能实例化,肯定有一个类继承与它,作为最终的binder实体。

    abstract public class ContentProviderNative extends Binder implements IContentProvider {
    

    这个 最终的类就是Transport,为什么要中间搞一个抽象类呢?是类Transport还扩充了额外的功能,例如权限限制等。

    class Transport extends ContentProviderNative {
    
    

    ContentProvider如何自定义权限?

    在AndroidManifest中定义

        <permission android:name="com.wenfengtou.bedwatching.permission.READ_PROVIDER"
            android:protectionLevel="normal" />
    
    
            <provider
                android:name=".BedWatchingProvider"
                android:authorities="com.wenfengtou.bedwatching.bedwatchingprovider"
                android:enabled="true"
                android:exported="true"
                android:permission="com.wenfengtou.bedwatching.permission.READ_PROVIDER"></provider>
    

    如何获取contentprovider的应用没有申请这个权限就会报错

    03-31 20:58:05.226  8733  8733 E AndroidRuntime: Caused by: java.lang.SecurityException: Permission Denial: opening provider com.wenfengtou.bedwatching.BedWatchingProvider from ProcessRecord{bda6cd8 8733:com.example.wenfengtou.myapplication/u0a60} (pid=8733, uid=10060) requires com.wenfengtou.bedwatching.permission.READ_PROVIDER or com.wenfengtou.bedwatching.permission.READ_PROVIDER
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.os.Parcel.readException(Parcel.java:1599)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.os.Parcel.readException(Parcel.java:1552)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:3550)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.app.ActivityThread.acquireProvider(ActivityThread.java:4778)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.app.ContextImpl$ApplicationContentResolver.acquireUnstableProvider(ContextImpl.java:2018)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.content.ContentResolver.acquireUnstableProvider(ContentResolver.java:1468)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.content.ContentResolver.query(ContentResolver.java:475)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.content.ContentResolver.query(ContentResolver.java:434)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at com.example.wenfengtou.myapplication.MainActivity.onResume(MainActivity.java:34)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1258)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.app.Activity.performResume(Activity.java:6312)
    03-31 20:58:05.226  8733  8733 E AndroidRuntime:    at android.app.ActivityThread.performResumeActivity(ActivityThread.java:3092)
    

    这个定位源码是在AMS中函数
    getContentProviderImpl - checkContentProviderPermissionLocked

    除了这个地方拦截权限,还有另一个地方
    /frameworks/base/core/java/android/content/ContentProvider.java
    enforceReadPermissionInner
    这个函数是跑在ContentProvider进程中的。

    ContentProvider的数据是如何传递的?

    public interface IBulkCursor extends IInterface
    
    public final class CursorToBulkCursorAdaptor extends BulkCursorNative
    
    
    /frameworks/base/core/java/android/content/ContentProviderNative.java
    
                    case QUERY_TRANSACTION:
                    {
                        data.enforceInterface(IContentProvider.descriptor);
    
                        String callingPkg = data.readString();
                        Uri url = Uri.CREATOR.createFromParcel(data);
    
                        // String[] projection
                        int num = data.readInt();
                        String[] projection = null;
                        if (num > 0) {
                            projection = new String[num];
                            for (int i = 0; i < num; i++) {
                                projection[i] = data.readString();
                            }
                        }
    
                        Bundle queryArgs = data.readBundle();
                        IContentObserver observer = IContentObserver.Stub.asInterface(
                                data.readStrongBinder());
                        ICancellationSignal cancellationSignal = ICancellationSignal.Stub.asInterface(
                                data.readStrongBinder());
    
                        Cursor cursor = query(callingPkg, url, projection, queryArgs, cancellationSignal);
                        if (cursor != null) {
                            CursorToBulkCursorAdaptor adaptor = null;
    
                            try {
                                adaptor = new CursorToBulkCursorAdaptor(cursor, observer,
                                        getProviderName());
                                cursor = null;
    
                                BulkCursorDescriptor d = adaptor.getBulkCursorDescriptor();
                                adaptor = null;
    
                                reply.writeNoException();
                                reply.writeInt(1);
                                d.writeToParcel(reply, Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                            } finally {
                                // Close cursor if an exception was thrown while constructing the adaptor.
                                if (adaptor != null) {
                                    adaptor.close();
                                }
                                if (cursor != null) {
                                    cursor.close();
                                }
                            }
                        } else {
                            reply.writeNoException();
                            reply.writeInt(0);
                        }
    
                        return true;
                    }
    

    创建共享内存的代码如下
    /frameworks/base/libs/androidfw/CursorWindow.cpp

    CursorWindow::CursorWindow(const String8& name, int ashmemFd,
            void* data, size_t size, bool readOnly) :
            mName(name), mAshmemFd(ashmemFd), mData(data), mSize(size), mReadOnly(readOnly) {
        mHeader = static_cast<Header*>(mData);
    }
    
    
    status_t CursorWindow::create(const String8& name, size_t size, CursorWindow** outCursorWindow) {
        String8 ashmemName("CursorWindow: ");
        ashmemName.append(name);
    
        status_t result;
        int ashmemFd = ashmem_create_region(ashmemName.string(), size);  //这里创建了共享内存
        if (ashmemFd < 0) {
            result = -errno;
        } else {
            result = ashmem_set_prot_region(ashmemFd, PROT_READ | PROT_WRITE);
            if (result >= 0) {
                void* data = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, ashmemFd, 0);
                if (data == MAP_FAILED) {
                    result = -errno;
                } else {
                    result = ashmem_set_prot_region(ashmemFd, PROT_READ);
                    if (result >= 0) {
                        CursorWindow* window = new CursorWindow(name, ashmemFd,
                                data, size, false /*readOnly*/);
                        result = window->clear();
                        if (!result) {
                            LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
                                    "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
                                    window->mHeader->freeOffset,
                                    window->mHeader->numRows,
                                    window->mHeader->numColumns,
                                    window->mSize, window->mData);
                            *outCursorWindow = window;
                            return OK;
                        }
                        delete window;
                    }
                }
                ::munmap(data, size);
            }
            ::close(ashmemFd);
        }
        *outCursorWindow = NULL;
        return result;
    }
    

    参考:https://blog.csdn.net/new_abc/article/details/8167973

    ContentProvider的权限怎么控制?

    1.设置android:readPermission,设置android:writePermission
    2.设置path-permission,注意android:readPermission的优先级高于path-permission,即如果客户端已经有android:readPermission的权限,则不检验path-permission。如果不满足android:readPermission的权限,则会再看path-permission是否满足。
    参考
    http://androidxref.com/6.0.0_r1/xref/packages/providers/DownloadProvider/AndroidManifest.xml#72

    checkHoldingPermissionsLocked这个对于uri临时授权关键。
    ams检查是用checkUriPermission

    FileProvider是什么?

    FileProvider继承与ContentProvider,用Content://代替原来的file://。

    相关文章

      网友评论

          本文标题:Android:ContentProvider总结

          本文链接:https://www.haomeiwen.com/subject/jwmpvqtx.html