Android应用之间数据的交互(一)获取系统应用的数据

栏目: Android · 发布时间: 5年前

内容简介:在Android应用开发中我们常常需要和其他应用进行交互,之前对这些问题没有仔细了解过,现在来做一下总结。Android应用之间数据的交互方式:下面介绍获取系统应用的数据

在Android应用开发中我们常常需要和其他应用进行交互,之前对这些问题没有仔细了解过,现在来做一下总结。

Android应用之间数据的交互方式:

  • 获取系统应用的数据
  • 提供数据给其他应用
  • 应用之间的分享

下面介绍获取系统应用的数据

实例分析

以获取联系人数据为例,代码如下:

Cursor cursor=getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
        if (cursor!=null){
            while(cursor.moveToNext()){
                //获取联系人的名字
                String name=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
                //获取联系人的电话号码
                String number=cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
                Log.d("============"," name: "+name+"  number="+number);
            }
            cursor.close();
        }
复制代码

ContentResolver 类的 query(Uri uri,String[] projection,String selection,String[] selectionArgs,String sortOrder) 方法参数的介绍:

uri 用来指明要访问的数据的位置

projection 访问的列,如果为null,则表示访问所有列

selection 用于指定查询条件

selectionArgs 查询参数

sortOrder 排序顺序

基本流程

那么 ContentResolver 是怎么通过 query 方法来获取联系人的数据的呢?下面我们来分析 ContentResolver 的源码:

//1
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);
    }
//2
public final @Nullable Cursor query(@RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable String selection,
            @Nullable String[] selectionArgs, @Nullable String sortOrder,
            @Nullable CancellationSignal cancellationSignal) {
        //把查询参数封装在Bundle中
        Bundle queryArgs = createSqlQueryBundle(selection, selectionArgs, sortOrder);
        return query(uri, projection, queryArgs, cancellationSignal);
    }
//3
public final @Nullable Cursor query(final @RequiresPermission.Read @NonNull Uri uri,
            @Nullable String[] projection, @Nullable Bundle queryArgs,
            @Nullable CancellationSignal cancellationSignal) {
        Preconditions.checkNotNull(uri, "uri");
        //获取不稳定的提供者 IContentProvider是IPC接口用来与内容提供者进行数据交互
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        if (unstableProvider == null) {
            return null;
        }
        IContentProvider stableProvider = null;
        Cursor qCursor = null;
        try {
            ...//省略些不重要的源码
            try {
                qCursor = unstableProvider.query(mPackageName, uri, projection,
                        queryArgs, 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, queryArgs, 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, queryArgs);

            // Wrap the cursor object into CursorWrapperInner object.
            final IContentProvider provider = (stableProvider != null) ? stableProvider
                    : acquireProvider(uri);
            final CursorWrapperInner wrapper = new CursorWrapperInner(qCursor, provider);//CursorWrapperInner是内部类
            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 {//释放资源
           ...
        }
    }
    
    public final IContentProvider acquireUnstableProvider(Uri uri) {
        if (!SCHEME_CONTENT.equals(uri.getScheme())) {//SCHEME_CONTENT为content,如果uri的scheme为null则返回null
            return null;
        }
        String auth = uri.getAuthority();
        if (auth != null) {
            return acquireUnstableProvider(mContext, uri.getAuthority());//这里调用的是ApplicationContentResolver的acquireUnstableProvider方法
        }
        return null;
    }
复制代码

ContentResolver 的源码中 query 有三个重载方法1,2,3,在实例中我们调用重载方法1,方法1中则直接调用方法2。在方法2中,封装了数据到 Bundle 中,并调用了方法3,真正的实现在方法3中。方法3中的核心方法是 acquireUnstableProvider(Uri uri) ,它获取 IContentProvider (一个IPC 接口),并通过 IContentProvider 来获取联系人的数据。而 acquireUnstableProvider(Uri uri) 调用的是 acquireUnstableProvider(Context c, String name) 方法,它是个抽象方法,具体的实现类是 ApplicationContentResolver

ApplicationContentResolveracquireUnstableProvider 方法源码如下:

@Override
 protected IContentProvider acquireUnstableProvider(Context c, String auth) {
            return mMainThread.acquireProvider(c,
                  ContentProvider.getAuthorityWithoutUserId(auth),
                  resolveUserIdFromAuthority(auth), false);
       }
复制代码

ApplicationContentResolveracquireUnstableProvider 方法调用了 ActivityThreadacquireProvider 方法,源码如下:

public final IContentProvider acquireProvider(
 Context c, String auth, int userId, boolean stable) {
 // 判断缓存中是否有对应的IContentProvider,有则直接返回
final IContentProvider provider = acquireExistingProvider(c, auth, userId, stable);
       if (provider != null) {
           return provider;
        }
       
        // There is a possible race here.  Another thread may try to acquire
        // the same provider at the same time.  When this happens, we want to ensure
        // that the first one wins.
        // Note that we cannot hold the lock while acquiring and installing the
        // provider since it might take a long time to run and it could also potentially
        // be re-entrant in the case where the provider is in the same process.
        ContentProviderHolder holder = null;
        try {
            //通过AMS获取ContentProviderHolder, 处理在不同进程的情况,
            //如果在同一进程就让`holder.provider = null;`
            holder = ActivityManager.getService().getContentProvider(
                    getApplicationThread(), auth, userId, stable);
        } catch (RemoteException ex) {
            throw ex.rethrowFromSystemServer();
        }
       //失败
       if (holder == null) {
            Slog.e(TAG, "Failed to find provider info for " + auth);
            return null;
        }

        // Install provider will increment the reference count for us, and break
        // any ties in the race.
        //如果在同一进程,则进行相应的处理
        holder = installProvider(c, holder, holder.info,
                true /*noisy*/, holder.noReleaseNeeded, stable);
        return holder.provider;
    }
复制代码

ApplicationContentResolver 通过调用 ActivityThread的acquireProvider 方法来获取 ContentProvider ; ActivityThread 先会判断缓存中是否存在要求的 IContentProvider , 如果存在就直接返回,如果不存在就调用AMS的 getContentProviderImpl()installProvider 来获取。

关于AMS获取IContentProvider的详细流程,可以看 ContentProvider启动过程分析

ContactsContract详解

ContactsContract 是存储联系人的数据表的常量字段,它有几个常用的内部类如下:

  • ContactsContract.Data:数据表的常量,包含与原始联系人关联的数据点。 数据表的每一行通常用于存储单条联系信息(例如电话号码)及其相关元数据(例如,它是工作号码还是家庭号码)
  • ContactsContract.CommonDataKinds:用于定义存储在ContactsContract.Data表中的公共数据类型的容器。
  • ContactsContract.Contacts:联系人表的常量,包含代表同一个人的每个原始联系人聚合的记录

联系人数据是存储在数据库中(具体位置在data/data/com.android.providers.contacts/databases中),根据其MIME类型来判断其位置所代表的意义。如 ContactsContract.CommonDataKinds.Phone.NUMBERContactsContract.CommonDataKinds.Email.ADDRESS 都表示 data1 ,但是在数据库中不同的MIME类型的 data1 表示不同的数据。

数据库中 data 表的字段和数据如下(字段太多,只截取了部分)

Android应用之间数据的交互(一)获取系统应用的数据

MIME类型如下

Android应用之间数据的交互(一)获取系统应用的数据

通过 MIME 类型来获取所需要的数据

Uri p = ContactsContract.Data.CONTENT_URI;
        Cursor id = getActivity().getContentResolver().query(p,new String[]{ContactsContract.Data.CONTACT_ID},null,null,null);
        while (id.moveToNext()){
            Cursor cursor=getActivity().getContentResolver().query(p, null,ContactsContract.Data.CONTACT_ID+"= ?", new String[]{id.getString(id.getColumnIndex(ContactsContract.Data.CONTACT_ID))}, null);
            Person person = new Person();
            byte[] image = null;
            StringBuilder builder = new StringBuilder();
                while (cursor.moveToNext()){
                    String mime = cursor.getString(cursor.getColumnIndex(ContactsContract.Data.MIMETYPE));
                    Log.d("===========","mime = "+mime);
                    if (mime.equals(ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)){
                        builder.append("name = "+cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)));
                        builder.append("number = "+cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
                    }else if (mime.equals(ContactsContract.CommonDataKinds.Photo.CONTENT_ITEM_TYPE)){
                    //二进制数据一般放在data15中
                        image=cursor.getBlob(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Photo.DATA15));
                    }else if (mime.equals(ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)){
                        builder.append("email = "+cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Email.ADDRESS)));
                    }else if (mime.equals(ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE)){
                        builder.append("address = "+cursor.getString(cursor.getColumnIndex(ContactsContract.
                                CommonDataKinds.StructuredPostal.STREET)));
                    }
                }
            String con=builder.toString();
            Log.d("===========",con);
            cursor.close();
            }
        id.close();
复制代码

ContentResolver() 还有 insertdeleteupdate 方法,其用法与 query 类似,这里不再介绍。

获取其他系统应用数据

CalendarContract

CalendarContract 是存储日历和事件相关信息字段表,与 ContactsContract 类似。 CalendarContract 也有几个内部类,常用的是 CalendarContract.Events ,包含诸如事件标题,位置,开始时间,结束时间等信息。 CalendarContract.Events 的使用如下:

Uri p = CalendarContract.Events.CONTENT_URI;
            Cursor cursor=getActivity().getContentResolver().query(p, null,null, null, null);
            while (cursor.moveToNext()){
                StringBuilder builder = new StringBuilder();
               builder.append("标题 : "+cursor.getString(cursor.getColumnIndex(CalendarContract.Events.TITLE))+"\n")
                       .append("起始时间 :"+cursor.getString(cursor.getColumnIndex(CalendarContract.Events.DTSTART))+"\n")
                       .append("结束时间 :"+cursor.getString(cursor.getColumnIndex(CalendarContract.Events.DTEND))+"\n")
                       .append("描述 : "+cursor.getString(cursor.getColumnIndex(CalendarContract.Events.DESCRIPTION))+"\n");
                contents.add(builder.toString());
            }
        cursor.close();
复制代码

更多关于 CalendarContract 的使用,可以查看官方文档。

MediaStore

MediaStore 包含内部和外部存储设备上所有可用媒体的元数据。其内部类如下:

  • MediaStore.Audio:集装箱所有的音频内容。
  • MediaStore.Files:媒体提供程序表,包含媒体存储中所有文件的索引,包括非媒体文件。
  • MediaStore.Images:包含所有可用图像的元数据
  • interface MediaStore.MediaColumns:大多数MediaProvider表的公共字段
  • MediaStore.Video:包含所有可用视频的元数据
List<String> contents = new ArrayList<>();
        Uri p1 = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
        Cursor cursor1=getActivity().getContentResolver().query(p1, null,null, null, null);
        while (cursor1.moveToNext()){
            StringBuilder builder = new StringBuilder();
            builder.append("文件名 : "+cursor1.getString(cursor1.getColumnIndex(MediaStore.Images.Media.TITLE))+"\n")
                    .append("描述 :"+cursor1.getString(cursor1.getColumnIndex(MediaStore.Images.Media.DESCRIPTION))+"\n")
                    .append("大小 :"+cursor1.getString(cursor1.getColumnIndex(MediaStore.Images.Media.SIZE))+"\n")
                    .append("位置 : "+cursor1.getString(cursor1.getColumnIndex(MediaStore.Images.Media.DATA))+"\n")
                    .append("文件修改时间:"+cursor1.getString(cursor1.getColumnIndex(MediaStore.Images.Media.DATE_MODIFIED))+"\n")
                    .append("DISPLAY_NAME :"+cursor1.getString(cursor1.getColumnIndex(MediaStore.Images.Media.DISPLAY_NAME))+"\n")
                    .append("时间 : "+cursor1.getString(cursor1.getColumnIndex(MediaStore.Images.Media.DATE_TAKEN))+"\n");
            contents.add(builder.toString());
        }
        cursor1.close();
复制代码

Settings

SettingsXXXContract 不同,它是通过xml文件来存储数据,在文件 /data/system/users/0/ 目录下,获取设置的方式如下:

StringBuilder builder = new StringBuilder();
        ContentResolver contentResolver=getActivity().getContentResolver();
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            builder.append("wifi是否开启:"+Settings.Global.getString(contentResolver,Settings.Global.WIFI_ON)+"\n")
                   .append("数据流量是否开启:"+Settings.Global.getString(contentResolver,Settings.Global.DATA_ROAMING)+"\n")
                   .append(Settings.System.NOTIFICATION_SOUND+" "+Settings.System.getString(contentResolver,Settings.System.NOTIFICATION_SOUND)).append("\n")
                    .append(Settings.System.SCREEN_BRIGHTNESS+" "+Settings.System.getString(contentResolver,Settings.System.SCREEN_BRIGHTNESS)).append("\n")
                    .append(Settings.System.TEXT_SHOW_PASSWORD+" "+Settings.System.getString(contentResolver,Settings.System.TEXT_SHOW_PASSWORD)).append("\n");
        }
        Log.d("==============",builder.toString());
复制代码

更多关于 Setting 的内容可以看 Android系统APP之SettingsProvider

注意:以上的操作都是需要申请权限的

参考文章:


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

编写高质量代码:改善Python程序的91个建议

编写高质量代码:改善Python程序的91个建议

张颖、赖勇浩 / 机械工业出版社 / 2014-6 / 59.00元

在通往“Python技术殿堂”的路上,本书将为你编写健壮、优雅、高质量的Python代码提供切实帮助!内容全部由Python编码的最佳实践组成,从基本原则、惯用法、语法、库、设计模式、内部机制、开发工具和性能优化8个方面深入探讨了编写高质量Python代码的技巧与禁忌,一共总结出91条宝贵的建议。每条建议对应Python程序员可能会遇到的一个问题。本书不仅以建议的方式从正反两方面给出了被实践证明为......一起来看看 《编写高质量代码:改善Python程序的91个建议》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具