内容简介:在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
。
ApplicationContentResolver
的 acquireUnstableProvider
方法源码如下:
@Override protected IContentProvider acquireUnstableProvider(Context c, String auth) { return mMainThread.acquireProvider(c, ContentProvider.getAuthorityWithoutUserId(auth), resolveUserIdFromAuthority(auth), false); } 复制代码
在 ApplicationContentResolver
的 acquireUnstableProvider
方法调用了 ActivityThread
的 acquireProvider
方法,源码如下:
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.NUMBER
和 ContactsContract.CommonDataKinds.Email.ADDRESS
都表示 data1
,但是在数据库中不同的MIME类型的 data1
表示不同的数据。
数据库中 data
表的字段和数据如下(字段太多,只截取了部分)
MIME类型如下
通过 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()
还有 insert
、 delete
、 update
方法,其用法与 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
Settings
与 XXXContract
不同,它是通过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
注意:以上的操作都是需要申请权限的
参考文章:
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- 大数据应用 资源控制在大数据和云计算平台中的应用
- 恒丰银行——基于大数据技术的数据仓库应用建设
- 数据中台初探与应用实践
- 关联数据入门――RDF应用
- 流式数据处理在百度数据工厂的应用与实践
- Redis:基础数据类型应用场景
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。