当前位置: 移动技术网 > 移动技术>移动开发>Android > android基础总结篇之八:创建及调用自己的ContentProvider

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

2019年07月24日  | 移动技术网移动技术  | 我要评论
今天我们来讲解一下如何创建及调用自己的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:

访问者可以通过“[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> 

这样就完成了访问者的代码,我们来看一下效果:

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

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

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

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网