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

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

参考文章:


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

查看所有标签

猜你喜欢:

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

产品型社群

产品型社群

李善友 / 机械工业出版社 / 2015-3-1 / CNY 69.00

传统模式企业正在直面一场空前的“降维战争”, 结局惨烈,或生或死。 传统模式很难避免悲惨下场, 诺基亚等昔日庞然大物轰然倒塌, 柯达发明了数码成像技术却依然破产, 新商业的兴起到底遵循的是什么模式? 微信轻而易举干掉了运营商的短信业务, “好未来”为何让传统教育不明觉厉? 花间堂为什么不是酒店,而是入口? 将来不会有互联网企业与传统企业之分, ......一起来看看 《产品型社群》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具