Android ContentProvider数据共享全解析

本文探讨了Android ContentProvider的实现原理,通过分析查询过程,包括ContentResolver的query方法、Provider实例获取、缓存机制以及跨进程数据传输。示例中通过访问图库图片来学习ContentProvider的工作流程,涉及SQLiteCursor、CursorWindow以及JNI在数据交换中的角色。
摘要由CSDN通过智能技术生成

     前两天处理了那个beam分享的问题单之后,回头来想想,蓝牙进程为什么能读取到文件并分享给另一个手机呢?Android手机本身是LInux系统,每个文件都有相应的读、写、执行权限的,如果权限不符合是无法访问该文件的,而在那个问题单的处理过程当中,我们可以看到,就是使用了Uri标识文件,然后给蓝牙进程赋予读权限,最终蓝牙进程肯定就是通过ContentProvider来实现文件的读取并共享的。问题单虽然处理完了,但是对于ContentProvider还是只了解面貌,底层到底是如何实现的,我们还是不清楚,也就是考虑到这个,所以本节课我们来深究一下ContentProvider的实现原理。

     之前也读过老罗关于ContentProvider的研究的博客了,写的非常好,非常细致,大家也可以学习一下,但是还是要说明一下,只是读别人的博客,自己不研究,不跟踪代码,那么学习还是非常肤浅的,基本上就非常模糊。好了,我们这节课呢实现很简单,就是通过访问图库中的图片,来学习一下ContentProvider的实现原理。客户端很简单,就一个方法,其他的调用界面我就不写了,大家可以自己写一下:

    public static void queryAllImages(Context context) {
        ArrayList
    
    
     
      names = new ArrayList
     
     
      
      ();
        ArrayList
      
      
       
        descs = new ArrayList
       
       
        
        ();
        ArrayList
        
        
          datas = new ArrayList 
         
           (); Uri uri = Media.EXTERNAL_CONTENT_URI; Cursor cursor = context.getContentResolver().query(uri, null, null, null, null); Log.i(TAG, "query images, " + uri + ", " + uri.toString()); while (cursor.moveToNext()) { String name = cursor.getString(cursor.getColumnIndex(Media.DISPLAY_NAME)); String desc = cursor.getString(cursor.getColumnIndex(Media.DESCRIPTION)); byte[] data = cursor.getBlob(cursor.getColumnIndex(Media.DATA)); names.add(name); descs.add(desc); datas.add(Arrays.toString(data)); mDatas.add(data); } Log.i(TAG, "names = " + names); Log.i(TAG, "datas = " + datas); } 
          
        
       
       
      
      
     
     
    
    
     我在这个方法和系统层framework当中都加了一些日志打印,从输出的日志当中,我们可以看到最后获取回来的blob数据是一个byte[]数组,存储的全部是十进制的整数,这些数组中的内容代码什么呢?

     
     我们可以把这些十进制的数据先转换为十六进制,然后再将十六进制的数据转换成字符串,就可以看到它到底是什么内容了。


     从转换成的字符串结果当中,很清楚的可以看到,它正是我们当前取回来的图片文件在手机上的存储路径。 

     这个方法当中的目的也非常明确,就是通过Resolver来查询图库中Media.EXTERNAL_CONTENT_URI匹配的所有的图片,然后在返回的结果中把数据读出来,看一下返回来的数据是什么,这样就完成了。那么根据这样的目的,我们也就把这节课分为三个步骤来讲:1、context.getContentResolver().query(uri, null, null, null, null)调用完成,返回一个Cursor对象;2、cursor.moveToNext()到底干了什么?;3、cursor.getBlob(cursor.getColumnIndex(Media.DATA))是如何把数据取回来的。后面两个步骤大家可能有疑问,这两步也要单独拿出来分析?先不着急,大家细心看完就知道了,其实这两步是非常重要的,数据的获取就是通过这两步来完成的。

     1、context.getContentResolver().query(uri, null, null, null, null)返回Cursor对象

     context.getContentResolver()是由ContextImpl实现的,它返回的就是成员变量mContentResolver,而mContentResolver是一个ApplicationContentResolver对象,是在ContextImpl定义的一个内部类,而query方法是由父类ContentResolver来实现的,注意query方法的修饰符为final,即此方法不允许子类自己实现,那么我们就来看一下query方法的实现:

    public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable String selection,
            @Nullable String[] selectionArgs, @Nullable String sortOrder) {
        return query(uri, projection, selection, selectionArgs, sortOrder, null);
    }

    /**
     * Query the given URI, returning a {@link Cursor} over the result set
     * with optional support for cancellation.
     * 

* For best performance, the caller should follow these guidelines: *

  • *
  • Provide an explicit projection, to prevent * reading data from storage that aren't going to be used.
  • *
  • Use question mark parameter markers such as 'phone=?' instead of * explicit values in the {@code selection} parameter, so that queries * that differ only by those values will be recognized as the same * for caching purposes.
  • *
*

* * @param uri The URI, using the content:// scheme, for the content to * retrieve. * @param projection A list of which columns to return. Passing null will * return all columns, which is inefficient. * @param selection A filter declaring which rows to return, formatted as an * SQL WHERE clause (excluding the WHERE itself). Passing null will * return all rows for the given URI. * @param selectionArgs You may include ?s in selection, which will be * replaced by the values from selectionArgs, in the order that they * appear in the selection. The values will be bound as Strings. * @param sortOrder How to order the rows, formatted as an SQL ORDER BY * clause (excluding the ORDER BY itself). Passing null will use the * default sort order, which may be unordered. * @param cancellationSignal A signal to cancel the operation in progress, or null if none. * If the operation is canceled, then {@link OperationCanceledException} will be thrown * when the query is executed. * @return A Cursor object, which is positioned before the first entry, or null * @see Cursor */ public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder, @Nullable CancellationSignal cancellationSignal) { Preconditions.checkNotNull(uri, "uri"); IContentProvider unstableProvider = acquireUnstableProvider(uri); if (unstableProvider == null) { return null; } IContentProvider stableProvider = null; Cursor qCursor = null; try { long startTime = SystemClock.uptimeMillis(); ICancellationSignal remoteCancellationSignal = null; if (cancellationSignal != null) { cancellationSignal.throwIfCanceled(); remoteCancellationSignal = unstableProvider.createCancellationSignal(); cancellationSignal.setRemote(remoteCancellationSignal); } try { qCursor = unstableProvider.query(mPackageName, uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); } catch (DeadObjectException e) { // The remote process has died... but we only hold an unstable // reference though, so we might recover!!! Let's try!!!! // This is exciting!!1!!1!!!!1 unstableProviderDied(unstableProvider); stableProvider = acquireProvider(uri); if (stableProvider == null) { return null; } qCursor = stableProvider.query(mPackageName, uri, projection, selection, selectionArgs, sortOrder, remoteCancellationSignal); } if (qCursor == null) { return null; } // Force query execution. Might fail and throw a runtime exception here. qCursor.getCount(); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder); // Wrap the cursor object into CursorWrapperInner object. final IContentProvider provider = (stableProvider != null) ? stableProvider : acquireProvider(uri); final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider); stableProvider = null; qCursor = null; return wrapper; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; } finally { if (qCursor != null) { qCursor.close(); } if (cancellationSignal != null) { cancellationSignal.setRemote(null); } if (unstable
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

红-旺永福

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值