欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android基础总结篇之八:创建及调用自己的ContentProvider

程序员文章站 2024-03-31 16:45:16
今天我们来讲解一下如何创建及调用自己的contentprovider。 在前面两篇文章中我们分别讲了如何读写联系人和短消息,相信大家对于contentprovider...

今天我们来讲解一下如何创建及调用自己的contentprovider。

在前面两篇文章中我们分别讲了如何读写联系人和短消息,相信大家对于contentprovider的操作方法已经有了一定程度的了解。在有些场合,除了操作contentprovider之外,我们还有可能需要创建自己的contentprovider,来提供信息共享的服务,这就要求我们很好的掌握contentprovider的创建及使用技巧。下面我们就由表及里的逐步讲解每个步骤。

在正式开始实例演示之前,我们先来了解以下两个知识点:

授权:

在android中,每一个contentprovider都会用类似于域名的字符串来注册自己,我们成为授权(authority)。这个唯一标识的字符串是此contentprovider可提供的一组uri的基础,有了这个基础,才能够向外界提供信息的共享服务。

授权是在androidmanifest.xml中完成的,每一个contentprovider必须在此声明并授权,方式如下:

<provider android:name=".someprovider" 
  android:authorities="com.your-company.someprovider"/> 

上面的<provider>元素指明了contentprovider的提供者是“someprovider”这个类,并为其授权,授权的基础uri为“com.your-company.someprovider”。有了这个授权信息,系统可以准确的定位到具体的contentprovider,从而使访问者能够获取到指定的信息。这和浏览web页面的方式很相似,“someprovider”就像一台具体的服务器,而“com.your-company.someprovider”就像注册的域名,相信大家对这个概念并不陌生,由此联想一下就可以了解contentprovider授权的作用了。(需要注意的是,除了android内置应用程序之外,第三方程序应尽量使用以上方式的完全限定的授权名。)

mime类型:

就像网站返回给定url的mime(multipurpose internet mail extensions,多用途internet邮件扩展)类型一样(这使浏览器能够用正确的程序来查看内容),contentprovider还负责返回给定uri的mime类型。根据mime类型规范,mime类型包含两部分:类型和子类型。例如:text/html,text/css,text/xml等等。

android也遵循类似的约定来定义mime类型。

对于单条记录,mime类型类似于:

vnd.android.cursor.item/vnd.your-company.content-type

而对于记录的集合,mime类型类似于:

vnd.android.cursor.dir/vnd.your-company.comtent-type

其中的vnd表示这些类型和子类型具有非标准的、供应商特定的形式;content-type可以根据contentprovider的功能来定,比如日记的contentprovider可以为note,日程安排的contentprovider可以为schedule,等等。

了解了以上两个知识点之后,我们就结合实例来演示一下具体的过程。

我们将会创建一个记录person信息的contentprovider,实现对person的crud操作,访问者可以通过下面路径操作我们的contentprovider:
android基础总结篇之八:创建及调用自己的ContentProvider

访问者可以通过“[base_uri]/persons”来操作person集合,也可以通过“[base_uri]/persons/#”的形式操作单个person。
我们创建一个person的contentprovider需要两个步骤:

1.创建personprovider类:

我们需要继承contentprovider类,实现oncreate、query、insert、update、delete和gettype这几个方法。具体代码如下:

package com.scott.provider; 
 
import android.content.contentprovider; 
import android.content.contenturis; 
import android.content.contentvalues; 
import android.content.urimatcher; 
import android.database.cursor; 
import android.database.sqlite.sqlitedatabase; 
import android.net.uri; 
 
public class personprovider extends contentprovider { 
 
  private static final urimatcher matcher; 
  private dbhelper helper; 
  private sqlitedatabase db; 
   
  private static final string authority = "com.scott.provider.personprovider"; 
  private static final int person_all = 0; 
  private static final int person_one = 1; 
   
  public static final string content_type = "vnd.android.cursor.dir/vnd.scott.person"; 
  public static final string content_item_type = "vnd.android.cursor.item/vnd.scott.person"; 
   
  //数据改变后立即重新查询 
  private static final uri notify_uri = uri.parse("content://" + authority + "/persons"); 
   
  static { 
    matcher = new urimatcher(urimatcher.no_match); 
     
    matcher.adduri(authority, "persons", person_all);  //匹配记录集合 
    matcher.adduri(authority, "persons/#", person_one); //匹配单条记录 
  } 
   
  @override 
  public boolean oncreate() { 
    helper = new dbhelper(getcontext()); 
    return true; 
  } 
 
  @override 
  public string gettype(uri uri) { 
    int match = matcher.match(uri); 
    switch (match) { 
    case person_all: 
      return content_type; 
    case person_one: 
      return content_item_type; 
    default: 
      throw new illegalargumentexception("unknown uri: " + uri); 
    } 
  } 
   
  @override 
  public cursor query(uri uri, string[] projection, string selection, string[] selectionargs, string sortorder) { 
    db = helper.getreadabledatabase(); 
    int match = matcher.match(uri); 
    switch (match) { 
    case person_all: 
      //doesn't need any code in my provider. 
      break; 
    case person_one: 
      long _id = contenturis.parseid(uri); 
      selection = "_id = ?"; 
      selectionargs = new string[]{string.valueof(_id)}; 
      break; 
    default: 
      throw new illegalargumentexception("unknown uri: " + uri); 
    } 
    return db.query("person", projection, selection, selectionargs, null, null, sortorder); 
  } 
 
  @override 
  public uri insert(uri uri, contentvalues values) { 
    int match = matcher.match(uri); 
    if (match != person_all) { 
      throw new illegalargumentexception("wrong uri: " + uri); 
    } 
    db = helper.getwritabledatabase(); 
    if (values == null) { 
      values = new contentvalues(); 
      values.put("name", "no name"); 
      values.put("age", "1"); 
      values.put("info", "no info."); 
    } 
    long rowid = db.insert("person", null, values); 
    if (rowid > 0) { 
      notifydatachanged(); 
      return contenturis.withappendedid(uri, rowid); 
    } 
    return null; 
  } 
 
  @override 
  public int delete(uri uri, string selection, string[] selectionargs) { 
    db = helper.getwritabledatabase(); 
    int match = matcher.match(uri); 
    switch (match) { 
    case person_all: 
      //doesn't need any code in my provider. 
      break; 
    case person_one: 
      long _id = contenturis.parseid(uri); 
      selection = "_id = ?"; 
      selectionargs = new string[]{string.valueof(_id)}; 
    } 
    int count = db.delete("person", selection, selectionargs); 
    if (count > 0) { 
      notifydatachanged(); 
    } 
    return count; 
  } 
 
  @override 
  public int update(uri uri, contentvalues values, string selection, string[] selectionargs) { 
    db = helper.getwritabledatabase(); 
    int match = matcher.match(uri); 
    switch (match) { 
    case person_all: 
      //doesn't need any code in my provider. 
      break; 
    case person_one: 
      long _id = contenturis.parseid(uri); 
      selection = "_id = ?"; 
      selectionargs = new string[]{string.valueof(_id)}; 
      break; 
    default: 
      throw new illegalargumentexception("unknown uri: " + uri); 
    } 
    int count = db.update("person", values, selection, selectionargs); 
    if (count > 0) { 
      notifydatachanged(); 
    } 
    return count; 
  } 
 
  //通知指定uri数据已改变 
  private void notifydatachanged() { 
    getcontext().getcontentresolver().notifychange(notify_uri, null);     
  } 
} 

在personprovider中,我们定义了授权地址为“com.scott.provider.personprovider”,相信大家在前面也有所了解了。基于这个授权,我们使用了一个urimatcher对其路径进行匹配,“[base_uri]/persons"和“[base_uri]/persons/#”这两种路径我们在上面也介绍过,分别对应记录集合和单个记录的操作。在query、insert、update和delete方法中我们根据urimatcher匹配结果来判断该uri是操作记录集合还是单条记录,从而采取不同的处理方法。在gettype方法中,我们会根据匹配的结果返回不同的mime类型,这一步是不能缺少的,比如我们在query方法中有可能是查询全部集合,有可能是查询单条记录,那么我们返回的cursor或是集合类型,或是单条记录,这个跟gettype返回的mime类型是一致的,就好像浏览网页一样,指定的url返回的信息是什么类型,那么浏览器就应该接收到对应的mime类型。另外,我们注意到,上面代码中,在insert、update、delete方法中都调用了notifydatachanged方法,这个方法中仅有的一步操作就是通知“[base_uri]/persons"的访问者,数据发生改变了,应该重新加载了。

在我们的personprovider中,我们用到了person、dbhelper类,代码如下:

package com.scott.provider; 
 
public class person { 
  public int _id; 
  public string name; 
  public int age; 
  public string info; 
   
  public person() { 
  } 
   
  public person(string name, int age, string info) { 
    this.name = name; 
    this.age = age; 
    this.info = info; 
  } 
} 
[java] view plain copy
package com.scott.provider; 
 
import android.content.context; 
import android.database.sqlite.sqlitedatabase; 
import android.database.sqlite.sqliteopenhelper; 
 
public class dbhelper extends sqliteopenhelper { 
 
  private static final string database_name = "provider.db"; 
  private static final int database_version = 1; 
   
  public dbhelper(context context) { 
    super(context, database_name, null, database_version); 
  } 
 
  @override 
  public void oncreate(sqlitedatabase db) { 
    string sql = "create table if not exists person" + 
        "(_id integer primary key autoincrement, name varchar, age integer, info text)"; 
    db.execsql(sql); 
  } 
 
  @override 
  public void onupgrade(sqlitedatabase db, int oldversion, int newversion) { 
    db.execsql("drop table if exists person"); 
    oncreate(db); 
  } 
} 

最后,要想让这个contentprovider生效,我们需要在androidmanifest.xml中声明并为其授权,如下所示:

<provider android:name=".personprovider" 
  android:authorities="com.scott.provider.personprovider" 
  android:multiprocess="true"/> 

其中,android:multiprocess代表是否允许多进程操作。另外我们也可以为其声明相应的权限,对应的属性是:android:permission。

2.调用personprovider类:

完成了person的contentprovider后,下面我们来看一下如何访问它。这一步我们在mainactivity中完成,看下面代码:

package com.scott.provider; 
 
import java.util.arraylist; 
 
import android.app.activity; 
import android.content.contentresolver; 
import android.content.contenturis; 
import android.content.contentvalues; 
import android.database.cursor; 
import android.database.cursorwrapper; 
import android.net.uri; 
import android.os.bundle; 
import android.os.handler; 
import android.os.message; 
import android.view.view; 
import android.widget.listview; 
import android.widget.simplecursoradapter; 
 
 
public class mainactivity extends activity { 
   
  private contentresolver resolver; 
  private listview listview; 
   
  private static final string authority = "com.scott.provider.personprovider"; 
  private static final uri person_all_uri = uri.parse("content://" + authority + "/persons"); 
   
  private handler handler = new handler() { 
    public void handlemessage(message msg) { 
      //update records. 
      requery(); 
    }; 
  }; 
   
  @override 
  public void oncreate(bundle savedinstancestate) { 
    super.oncreate(savedinstancestate); 
    setcontentview(r.layout.main); 
 
    resolver = getcontentresolver(); 
    listview = (listview) findviewbyid(r.id.listview); 
     
    //为person_all_uri注册变化通知 
    getcontentresolver().registercontentobserver(person_all_uri, true, new personobserver(handler)); 
  } 
   
  /** 
   * 初始化 
   * @param view 
   */ 
  public void init(view view) { 
    arraylist<person> persons = new arraylist<person>(); 
     
    person person1 = new person("ella", 22, "lively girl"); 
    person person2 = new person("jenny", 22, "beautiful girl"); 
    person person3 = new person("jessica", 23, "sexy girl"); 
    person person4 = new person("kelly", 23, "hot baby"); 
    person person5 = new person("jane", 25, "pretty woman"); 
     
    persons.add(person1); 
    persons.add(person2); 
    persons.add(person3); 
    persons.add(person4); 
    persons.add(person5); 
 
    for (person person : persons) { 
      contentvalues values = new contentvalues(); 
      values.put("name", person.name); 
      values.put("age", person.age); 
      values.put("info", person.info); 
      resolver.insert(person_all_uri, values); 
    } 
  } 
   
  /** 
   * 查询所有记录 
   * @param view 
   */ 
  public void query(view view) { 
//   uri persononeuri = contenturis.withappendedid(person_all_uri, 1);查询_id为1的记录 
    cursor c = resolver.query(person_all_uri, null, null, null, null); 
     
    cursorwrapper cursorwrapper = new cursorwrapper(c) { 
       
      @override 
      public string getstring(int columnindex) { 
        //将简介前加上年龄 
        if (getcolumnname(columnindex).equals("info")) { 
          int age = getint(getcolumnindex("age")); 
          return age + " years old, " + super.getstring(columnindex); 
        } 
        return super.getstring(columnindex); 
      } 
    }; 
     
    //cursor须含有"_id"字段 
    simplecursoradapter adapter = new simplecursoradapter(this, android.r.layout.simple_list_item_2, 
        cursorwrapper, new string[]{"name", "info"}, new int[]{android.r.id.text1, android.r.id.text2}); 
    listview.setadapter(adapter); 
     
    startmanagingcursor(cursorwrapper); //管理cursor 
  } 
   
  /** 
   * 插入一条记录 
   * @param view 
   */ 
  public void insert(view view) { 
    person person = new person("alina", 26, "attractive lady"); 
    contentvalues values = new contentvalues(); 
    values.put("name", person.name); 
    values.put("age", person.age); 
    values.put("info", person.info); 
    resolver.insert(person_all_uri, values); 
  } 
   
  /** 
   * 更新一条记录 
   * @param view 
   */ 
  public void update(view view) { 
    person person = new person(); 
    person.name = "jane"; 
    person.age = 30; 
    //将指定name的记录age字段更新为30 
    contentvalues values = new contentvalues(); 
    values.put("age", person.age); 
    resolver.update(person_all_uri, values, "name = ?", new string[]{person.name}); 
     
    //将_id为1的age更新为30 
//   uri updateuri = contenturis.withappendedid(person_all_uri, 1); 
//   resolver.update(updateuri, values, null, null); 
  } 
   
  /** 
   * 删除一条记录 
   * @param view 
   */ 
  public void delete(view view) { 
    //删除_id为1的记录 
    uri deluri = contenturis.withappendedid(person_all_uri, 1); 
    resolver.delete(deluri, null, null); 
     
    //删除所有记录 
//   resolver.delete(person_all_uri, null, null); 
  } 
   
  /** 
   * 重新查询 
   */ 
  private void requery() { 
    //实际操作中可以查询集合信息后adapter.notifydatasetchanged(); 
    query(null); 
  } 
} 

我们看到,在上面的代码中,分别对应每一种情况进行测试,相对较为简单。我们主要讲一下registercontentobserver这一环节。

在前面的personprovider我们也提到,在数据更改后,会向指定的uri访问者发出通知,以便于更新查询记录。大家注意,仅仅是contentprovider出力还不够,我们还需要在访问者中注册一个contentobserver,才能够接收到这个通知。下面我们创建一个

personobserver:
package com.scott.provider; 
 
import android.database.contentobserver; 
import android.os.handler; 
import android.os.message; 
import android.util.log; 
 
public class personobserver extends contentobserver { 
 
  public static final string tag = "personobserver"; 
  private handler handler; 
   
  public personobserver(handler handler) { 
    super(handler); 
    this.handler = handler; 
  } 
   
  @override 
  public void onchange(boolean selfchange) { 
    super.onchange(selfchange); 
    log.i(tag, "data changed, try to requery."); 
    //向handler发送消息,更新查询记录 
    message msg = new message(); 
    handler.sendmessage(msg); 
  } 
} 

这样一来,当contentprovider发来通知之后,我们就能立即接收到,从而向handler发送一条消息,重新查询记录,使我们能够看到最新的记录信息。

最后,我们要在androidmanifest.xml中为mainactivity添加mime类型过滤器,告诉系统mainactivity可以处理的信息类型:

<!-- mime类型 --> 
<intent-filter> 
  <data android:mimetype="vnd.android.cursor.dir/vnd.scott.person"/> 
</intent-filter> 
<intent-filter> 
  <data android:mimetype="vnd.android.cursor.item/vnd.scott.person"/> 
</intent-filter> 

这样就完成了访问者的代码,我们来看一下效果:
android基础总结篇之八:创建及调用自己的ContentProvider

鉴于操作类型太多,我在这里就不再展示了,大家可以自己试一试。

原文链接:http://blog.csdn.net/liuhe688/article/details/7050868

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。