1、ContentProvider是什么?
ContentProvider是安卓平台中,在不同应用程序之间实现数据共享的一种机制。一个应用程序如果需要让别的程序可以操作自己的数据,即可采用这种机制。
2、ContentProvider能做什么事?
共享数据:比如短信应用就共享了所有短信的数据,我们可以用代码操作这些数据,如下:
getContentResolver().query(Uri.parse("content://sms/inbox");
3、ContentProvider要怎么用?
这里以一个简单的demo来演示用法。
(1)新建程序A,在程序A中新建一个类继承ContentProvider
public class MyProvider extends ContentProvider{ static final String TABLE_NAME = "test"; static final String ID = "id"; static final String CONTENT = "content"; @Override public boolean onCreate() { SQL.addTable(TABLE_NAME, ID, CONTENT); SQL sql = SQL.init(getContext()); sql.createTable(); sql.insert(TABLE_NAME, "001", "NeiRon"); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQL sql = SQL.init(getContext()); Cursor c = sql.query(TABLE_NAME, new String[]{ID}, new String[]{"001"}); return c; } }
这个ContentProvider在初始化的时候新建了一个数据库,并且新建了一张表,插入了一行数据;
调用query查询的时候我写死了,直接查整张表;(懒)
SQL是我写的一个数据库工具类,比较懒就直接拖过来用了,不用在意。
(2)在manifest中定义MyProvider
<provider android:name="com.example.testb.MyProvider" android:authorities="com.linin.test" android:exported="true" > </provider>
android:authorities的参数可以自己定义,只是个标识而已。
注意:android:exported="true"的意思是允许其他程序调用本程序的MyProvider,默认是false;网上很多教程都没提到这个,原因是在2.3之前的系统不添加也能被其他程序调用,不知道为什么,反正我是被这个坑惨了!QAQ
(3)新建程序B,在程序B中查询MyProvider共享的数据;
注意Uri的格式是:content://自定义的标识[/表名][/ID]
自定义标识其实就是程序A的android:authorities;
表名=数据库表名;ID=_id参数;
(如果不太熟悉的,建议去百度一下)
注意:实际上只要自定义标识对上了,就能调用MyProvider的query方法,之后就根据传过来的Uri进行后续的操作;我这个demo因为没对Uri做任何操作,形同虚设,所以怎么传都无所谓(保证content://com.linin.test开头就行);
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView tv = (TextView) findViewById(R.id.text); ContentResolver cr = getContentResolver(); Uri uri = Uri.parse("content://com.linin.test"); Cursor c = cr.query(uri, null, null, null, null); if (c!=null&&c.moveToNext()) { tv.setText(c.getString(c.getColumnIndex(CONTENT))); } c.close(); c = null; }
运行结果:在程序B中获取到了程序A共享的数据,并显示出来,如下:
4、一些其他的东西?
网上的一些教程有提到UriMatcher、ContentUris这两个工具类,其实吧,这两个类其实可有可无,一个是为了判断查询全部表数据还是查询单条数据,一个是拼接Uri的。好吧,对于习惯用正则又喜欢字符串+字符串拼接的我来说没太必要,不过还是了解一下吧。
(1)UriMatcher,从名字就可以看出这个工具类其实就是用了正则来判断的。用法如下,在ContentProvider中初始化:
private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static{ uriMatcher.addURI("com.linin.test", "test", 0);//代表查询整张test表 uriMatcher.addURI("com.linin.test", "test/#", 1);//代表查询test表的单条数据 }
然后在getType中判断Uri的数据类型:
public String getType(Uri uri) { Log.e("test","getType!!!"+uri.toString()); int witch = uriMatcher.match(uri); switch (witch) { case 0://查询整张表 return "vnd.android.cursor.dir/test"; case 1://查询单条数据 return "vnd.android.cursor.item/test"; } return null; }
那么问题就来了:我们什么时候要用到这个getType呢?
实际上,我在程序B中获取到程序A的共享数据后,getType一次也没被调用。
其实只要在程序B中把getContentResolver().getType(uri)打印出来就知道了,ContentResolver中的getType会根据传入的Uri与每个ContentProvider匹配,匹配后调用其getType获取该数据类型。
那么问题又来了:这样做有什么意义?
好吧其实意义不大。到这里其实我已经醉了,网上的教程对getType的说法模糊不清,越看越乱。接下来我就以我自己的理解来介绍下getType吧!
看我写的代码就知道了,getType的作用只有一个,就是返回数据的类型,返回类型的作用当然也只有一个,用来判断!
于是我们可以在query方法中这样写:
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { String type = getType(uri); if (type.equals("vnd.android.cursor.dir/test")) { //多行数据类型 }else if (type.equals("vnd.android.cursor.item/test")) { //单行数据类型 } return c; }
作用很明显了,就是用来判断数据类型的(多行or单行)!
一些聪明的小伙伴可能发现了:那我直接把getType里面的判断搬出来不就行了?
嗯。。。确实在MyProvider里面是可以这样写,不过也得考虑其他情况,比如说在我的程序B中,如果不用getContentResolver().getType(uri)去获取数据类型的话,那我就又要初始化一次UriMatcher了,毕竟UriMatcher的初始化是在程序A的MyProvider中完成的。
呼呼呼,总算强行解释通了~~
(2)ContentUris,一个神奇的工具,他可以做到这样:
Uri uri = Uri.parse("content://com.linin.test/test"); Uri resultUri = ContentUris.withAppendedId(uri, 10); 结果: resultUri-->content://com.linin.test/test/10 等价于: Uri resultUri = Uri.parse("content://com.linin.test/test/10");
他也可以做到这样:
Uri uri = Uri.parse("content://com.linin.test/test/10"); long id = ContentUris.parseId(uri); 结果: id-->10
说实话,我不知道这个工具类有什么存在的必要。。。了解了就行,用不用随意吧。