Android基础总结之八:ContentProvider
文章出处:http://blog.csdn.net/shift_wwx
请转载的朋友标明出处~~
前言:终于有时间写关于四大组件的最后一个,其实也是最简单的一个。但是将其放在最后,一是没有时间研究这个,二是对其中的有些细节还是模棱两可。
一、简介
能够作为android的四大组件之一,可以想象到其重要性和频繁性。
回顾一下android中数据存储的方式有很多种:
(1) SharePreferences 通过api进行get、put操作 ----进程内部使用,可以实现资源共享,局限性比较大
可以参考:SharedPreference 实现不同进程间的数据共享
(2) 通过file进行一些输入输出流控制 ----能实现进程间的共享,但是使用比较麻烦,个人不太喜欢用
(3) 通过SQLite进行数据库的读写 ----进程内部使用,可以实现资源共享,局限性比较大
(4) ContentProvider 进行数据的存储 ----可以实现进程间的共享,可以和SQLite配合使用(当然也可以用其他几个存储方式,稍后补充上实例),一般实现的是大型数据操作
(5) 网络存储数据 ----比较麻烦,有局限性
(6) system.prop 也可以进行简单的数据存储 ----有局限性,有default值,可以临时修改,重启后就会恢复默认值或重新设置
使用ContentProvider共享数据的好处是统一了数据访问方式,可以存储 一些图片等的数据。而这些其他是有局限性的。
二、使用
网上有很多文章会举出一些例子,比较详细,这里我只是做个简单的说明,后期会继续补充ContentProvider中使用到的相关类以及注意点
1. AndroidManifest.xml
<application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" > <provider android:name="MyProvider" android:exported="true" android:authorities="com.shift.provider.testprovider" > </provider> </application>
android:authorities是必须要说明的,是进程间通信的纽带。ContentProvider 采用了authorities(主机名/域名)对它进行唯一标识,你可以把 ContentProvider看作是一个网站(想想,网站也是提供数据者),authorities 就是他的域名
android:exported="true" 也是比较重要的,这个属性用于指示该应用是否能被其他程序应用组件调用或跟他交互。取值为(true | false),如果设置成true,则能够被调用或交互,否则不能;设置为false时,只有同一个应用程序的组件或带有相同用户ID的应用程序才能启动或绑定该服务。
它的默认值是依赖于该服务所包含的过滤器:
(1)如果没有过滤器则意味着该服务只能通过指定明确的类名来调用,也就是说该服务只能在应用程序内部使用(因为其他外部使用者不会知道该服务的类名),此时它的默认值是false
(2)如果至少包含了一个过滤器,则意味着该服务可以给外部的其他应用提供服务,因此默认值是true。
如果没有exported这个属性就会出现下面的log:
E/MessageQueue-JNI( 4967): java.lang.SecurityException: Permission Denial: opening provider com.shift.provider.MyProviderfrom ProcessRecord{41d730e0 4967:com.shift.demo/u0a57} (pid=4967, uid=10057) that is not exported from uid 10056E/MessageQueue-JNI( 4967): at android.os.Parcel.readException(Parcel.java:1465)E/MessageQueue-JNI( 4967): at android.os.Parcel.readException(Parcel.java:1419)E/MessageQueue-JNI( 4967): at android.app.ActivityManagerProxy.getContentProvider(ActivityManagerNative.java:2848)E/MessageQueue-JNI( 4967): at android.app.ActivityThread.acquireProvider(ActivityThread.java:4415)E/MessageQueue-JNI( 4967): at android.app.ContextImpl$ApplicationContentResolver.acquireProvider(ContextImpl.java:2224)E/MessageQueue-JNI( 4967): at android.content.ContentResolver.acquireProvider(ContentResolver.java:1378)E/MessageQueue-JNI( 4967): at android.content.ContentResolver.insert(ContentResolver.java:1184)E/MessageQueue-JNI( 4967): at com.shift.demo.MyTestActivity.onClick(MyTestActivity.java:109)E/MessageQueue-JNI( 4967): at android.view.View.performClick(View.java:4438)E/MessageQueue-JNI( 4967): at android.view.View.onKeyUp(View.java:8241)E/MessageQueue-JNI( 4967): at android.widget.TextView.onKeyUp(TextView.java:5628)
2. 继承ContentProvider
我做了一个简单的例子,在每一个override函数中加上log,方便自我的发挥,自我感觉没有必要写的很详细,如果谁想详细了解,建议看MediaProvider。
package com.shift.provider;import android.content.ContentProvider;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;public class MyProvider extends ContentProvider{private static final String TAG = "MyProvider";private static final String authorities = "com.shift.provider.testprovider";@Overridepublic int delete(Uri uri, String selection, String[] selectionArgs) {MLOG.d(TAG, "====delete=====");return 0;}@Overridepublic String getType(Uri uri) {MLOG.d(TAG, "====getType=====");return null;}@Overridepublic Uri insert(Uri uri, ContentValues values) {MLOG.d(TAG, "====insert=====");return null;}@Overridepublic boolean onCreate() {MLOG.d(TAG, "====onCreate=====");return false;}@Overridepublic Cursor query(Uri uri, String[] projection, String selection,String[] selectionArgs, String sortOrder) {MLOG.d(TAG, "====query=====");return null;}@Overridepublic int update(Uri uri, ContentValues values, String selection,String[] selectionArgs) {MLOG.d(TAG, "====update=====");return 0;}}其中的MLOG我自己写的一个log类,通过宏控制是否输出log,release code的时候就可以关掉,这样log会干净一点。这里就不贴了。
3. 测试用的Activity
private ContentResolver mContentResolver; private static final Uri uri = Uri.parse("content://com.shift.provider.testprovider/test"); @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); initTest1(); initTest2(); } /** * test for provider * do some command as insert, update, delete, query by provider */ private void initTest2(){ Button insert = (Button)findViewById(R.id.insert); Button update = (Button)findViewById(R.id.update); Button delete = (Button)findViewById(R.id.delete); Button query = (Button)findViewById(R.id.query); insert.setOnClickListener(this); update.setOnClickListener(this); delete.setOnClickListener(this); query.setOnClickListener(this); mContentResolver = getContentResolver(); } @Override public void onClick(View view) { int id = view.getId(); switch (id) { case R.id.insert: ContentValues cv = new ContentValues(); cv.put("name", "hehe1"); mContentResolver.insert(uri, cv); break; case R.id.update: break; case R.id.delete: break; case R.id.query: break; default: break; } }
程序很简单,在点击insert的时候,log就出现了:
I/ActivityManager( 3943): Start proc com.shift.provider for content provider com.shift.provider/.MyProvider: pid=5114 uid=10056 gids={50056}D/MyProvider( 5114): ====onCreate=====D/MyProvider( 5114): ====insert=====
三、注意点
1. activity 中是通过ContentResolver 与ContentProvider进行交互,比较郁闷的是理论上应该通过ContentProvider的一个实例来调用insert等接口,为什么会通过ContentResolver呢?
看了源代码应该就知道原因了:
public final Uri insert(Uri url, ContentValues values) { IContentProvider provider = acquireProvider(url); if (provider == null) { throw new IllegalArgumentException("Unknown URL " + url); } try { long startTime = SystemClock.uptimeMillis(); Uri createdRow = provider.insert(mPackageName, url, values); long durationMillis = SystemClock.uptimeMillis() - startTime; maybeLogUpdateToEventLog(durationMillis, url, "insert", null /* where */); return createdRow; } catch (RemoteException e) { // Arbitrary and not worth documenting, as Activity // Manager will kill this process shortly anyway. return null; } finally { releaseProvider(provider); } }
2. ContentProvider 在初次被调用的时候会进入onCreate,以后不会重复调用
/** * After being instantiated, this is called to tell the content provider * about itself. * * @param context The context this provider is running in * @param info Registered information about this content provider */ public void attachInfo(Context context, ProviderInfo info) { attachInfo(context, info, false); }
3. 关于Uri
可以参考:《android 中的Uri》
四、实例
1. ContentProvider中用SharePreferences
我们以insert 、query为例。
ContentProvider中实现put的代码:
private static final String uri_path = "content://com.shift.provider.testprovider/"; @Overridepublic Uri insert(Uri uri, ContentValues values) {MLOG.d(TAG, "====insert=====");String key = uri.toString().replace(uri_path, "");String value = values.getAsString("name");SharedPreferences sp = getContext().getSharedPreferences(SHARE_PRE_NAME, Context.MODE_PRIVATE);sp.edit().putString(key, value).commit();return null;}
activity中实现put的代码:
private void insert(){String key = "name";ContentValues cv = new ContentValues();cv.put(key, "hehe1");mContentResolver.insert(Uri.parse(uri_path+key), cv);}
有人觉得query就比较麻烦了,因为用query的话,contentProvider返回的是Cursor,这样怎么办了?
其实这个实现不了,我们可以换一个接口,只要能获取正确的值就ok。可以用getType来实现。
ContentProvider中实现get代码:
@Overridepublic String getType(Uri uri) {MLOG.d(TAG, "====getType=====");String key = uri.toString().replace(uri_path, "");SharedPreferences sp = getContext().getSharedPreferences(SHARE_PRE_NAME, Context.MODE_PRIVATE);return sp.getString(key, "error");}
activity中的代码:
private void query(){String key = "name";String value = mContentResolver.getType(Uri.parse(uri_path+key));MLOG.d(TAG, "====value===="+value);}
2. ContentProvider中用SQLite实现数据存储
还是以insert 和query为例:
ContentProvider中代码:
@Overridepublic Uri insert(Uri uri, ContentValues values) {MLOG.d(TAG, "====insert=====");mDatabase.insert("test", null, values);return null;} @Override public boolean onCreate() { MLOG.d(TAG, "====onCreate====="); DataBaseHelper baseHelper = new DataBaseHelper(getContext(), "my_db", 1); mDatabase = baseHelper.getWritableDatabase(); mDatabase.execSQL("create table if not exists test(id integer primary key not null, name text)"); return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { MLOG.d(TAG, "====query====="); return mDatabase.query("test", null, selection, selectionArgs, null, null, sortOrder); }
activity中代码:
private void insert(){ContentValues cv = new ContentValues();cv.put("id", 1);cv.put("name", "hehe1");mContentResolver.insert(Uri.parse(uri_path), cv);} private void query(){ Cursor cursor = mContentResolver.query(Uri.parse(uri_path), null, null, null, null); if(cursor.moveToFirst()){ do{ int id = cursor.getInt(cursor.getColumnIndex("id")); String name = cursor.getString(cursor.getColumnIndex("name")); MLOG.d(TAG, "==id=="+id+"====name==="+name); }while(cursor.moveToNext()); } }
更多相关文章
- 安卓实现扫一扫识别数字
- android下使用gdb调试工具笔记
- Android人脸识别app——基于Face++,MVP+Retofit+RxJava+Dagger高
- Android(安卓)ListView一些应该知道的事
- 工具篇 之 Android(安卓)WIFI ADB 实战
- 关于Android直接连MySQL获取数据的真机测试的一些坑
- Android存储路径解析
- 【Android(安卓)开发】 : Activity之间传递数据的几种方式
- MapMe