Android ContentResolver 内容解析者(解析器)----重新认识Android(11)

ContentResolver 内容解析者(解析器)
一、Content Provider (内容 提供者 )简介:
 
(一)、引入:
          数据库在Android当中是私有的 ,不能将数据库设为WORLD_READABLE, 每个数据库都只能创建它的包访问 。这意味着 只有创建这个数据库的应用程序才可访问它 。也就是说不能跨越进程和包,直接访问别的应用程序的数据库。那么如何在应用程序间交换数据呢?可以使用ContentProvider(内容提供者)来实现。这样就更加安全可靠(想从我家借东西,你不能直接进来拿,要我拿给你).
为什么要暴露数据呢?系统中的电话簿,短信,媒体库等数据,其他app希望访问.
 
(二)、ContentProvider的功能和意义:
        为了在应用程序之间交换数据,Android提供了 ContentProvider ,它是不同应用程序之间进行数据交换的标准API。
当一个应用程序需要把自己的数据暴露给其他应用程序使用时,该应用程序可以通过提供ContentProvider来实现;
        而其他应用程序需要使用这些数据时,不管提供数据的应用程序是否启动,可以通过 ContentResolver 来操作ContentProvider暴露的数据。包括增加数据insert()、删除数据delete()、修改数据update()、查询数据query()等。
        虽然大部分使用ContentProvider操作的数据都来自于数据库,但是也可以来自于文件、SharedPreferences、XML或网络等其他存储方式。
 
(三)、核心类:
1、 ContentProvider 内容提供者 :(需要暴露数据的A应用程序--类似于服务器端)
可以通过继承一个 ContentProvider 抽象类 将自己的数据暴露出去;
A应用通过Uri向外暴露数据。只要将A应用安装到手机上。无论是否运行,其他应用都可以从A应用获取数据。
外界不知道,也无需知道A应用暴露的数据在A应用当中是如何存储的,(是用数据库存储还是用文件存储,还是通过网上获得),外界可以通过一套标准的接口 读取或修改A应用里的数据.
   
2、 ContentResolver  内容解析者 :(访问操作A应用所暴露的数据--类似于客户端)
外界的程序通过 ContentResolver 可以访问 ContentProvider 提供的数据;
 
3、 Uri统一资源标识符: Uri是ContentResolver和ContentProvider进行数据交换的标识。
例如一个Uri是  content://com.qf.day16_contentprovider_t/user/5
Uri  的标准前缀 (协议protocal) :以“ content:// ”作为前缀,表示该数据由   ContentProvider  管理。
Uri  的 authority 部分(授权,权限):" com.qf.day16_contentprovider_t "该部分是暴露数据的app的包名。(要求小写)。
Uri  的 path 部分(路径):  " user " 用于判断请求的路径(哪些数据被请求)。
被请求的特定记录的 id 值[可选的]: "5" 如果请求不仅限于某个单条数据,该部分及其前面的斜线应该删除。
将一个字符串转换成Uri的方式:   Uri uri = Uri.parse("..............")
【备注:】URI、URL的区别:
首先,URI,是 uniform resource identifier ,统一资源标识符,用来唯一的标识一个资源。
URL是 uniform resource locator ,统一资源定位器, 它是一种具体的URI ,即URL可以用来标识一个资源,而且还指明了如何 定位 这个资源。
        也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL则是具体的资源标识的方式。可以认为 URL是一种具体的URI ,它不仅唯一标识资源,而且还提供了定位该资源的信息。URI是一种语义上的抽象概念,可以是绝对的,也可以是相对的,
而URL则必须提供足够的信息来定位,所以,是绝对的。


二、使用 ContentResolver  管理通话记录:
查询手机通话记录,可以利用上下文菜单删除通话记录
整个通话记录列表lv
上下文菜单中的删除项:action_delete
通话记录编号tv_id
对方电话号码tv_number
通话开始时间tv_date
通话类型(呼出/呼入/未接)tv_type
注意需要申请权限,否则运行会出现异常:WRITE_CALL_LOG,READ_CALL_LOG
/**
 * ContentProvider内容提供者 1.主要用于不同的应用程序之间,共享数据的。
A:ContentProvider:向外提供数据的
 * 用于通过Uri向外暴露数据。只要将应用安装到手机上。无论是否运行,都可以获取数据。
B:ContentResolver:获取并解析数据
 * 用于解析通过ContentProvider暴露 出来的数据。 A应用暴露出的是Uri,B应用使用该Uri就可以访问A应用暴露出来的数据了。
 * =====================================================
 * 本例用于实现通过ContentResovler来获取手机中通话记录
 * 通话记录存储在内部存储的系统应用目录中:data/data/com.android.providers.contacts/contacts2.db
 * 对外暴露的Uri:"content://call_log/calls"
 * step1:通过 getContentResolver ()获取ContentResovler对象
 * step2:获取通话记录的Uri:"content://call_log/calls"
 * step3:执行查询:ContentResovler对象.query(uri,查询的字段, 条件,条件的参数,排序依据);
 * step4:添加权限: READ_CALL_LOG  读取通话记录, WRITE_CALL_LOG  写通话记录 通话记录表的核心字段" _id ",
 * " number "电话号码, " date "时间(毫秒值), " type ": 1:呼入 2:呼出 3:未接
 */
public class Main3Activity extends AppCompatActivity {

    // step2:提供要解析数据对应的Uri,就是电话记录的uri
    private Uri uri_callLog = CallLog.Calls.CONTENT_URI;// 系统提供的常量,表示通话记录对外暴露的Uri
    // 上面常量的值就是 Uri.parse("content://call_log/calls")得到的结果.
    private ContentResolver contentResolver;
    private ListView lv_listview;
    private MyCursorAdapter adapter;// 自定义的适配器类对象

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);
        lv_listview = (ListView) findViewById(R.id.lv_listview);
        // step1:获取ContentResolver
        contentResolver = getContentResolver();
        queryCallLog();// 调用自定义的方法,用于查询并显示通话记录
        // 注册 上下文菜单:可以删除某个通话记录
        registerForContextMenu(lv_listview);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        getMenuInflater().inflate(R.menu.call_menu, menu);// 上下文菜单,只有一个"删除"选项
    }

    // 上下文菜单点击时间处理
    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterView.AdapterContextMenuInfo menuInfo = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        int position = menuInfo.position;// 获得弹出上下文菜单时所选中的条目的位置
        Cursor cursor = adapter.getCursor();// 调用CursorAdapter的获取cursor对象的方法
        cursor.moveToPosition(position);// 把cursor作为数据源,移动到指定位置
        int id = cursor.getInt(cursor.getColumnIndex("_id"));// 通话记录表calls中的字段
        switch (item.getItemId()) {
            case R.id.action_delete:
                // 删除一条记录
                int count = deleteCallLog(id);// 调用自定义的删除通话记录的方法
                if (count > 0) {
                    Toast.makeText(Main3Activity.this, "删除成功", Toast.LENGTH_SHORT).show();
                } else {
                    Toast.makeText(Main3Activity.this, "删除失败", Toast.LENGTH_SHORT).show();
                }
                break;
            default:
                break;
        }
        queryCallLog();// 调用自定义的重新查询的方法
        return super.onContextItemSelected(item);
    }

    /**
     * 删除一条指定的通话记录
     *
     * @param id
     * @return
     */
    public int deleteCallLog(int id) {
        int count = contentResolver.delete(uri_callLog, "_id=?", new String[]{id + ""});// 也可以第二个参数传"_id="+id,第三参数传null
        return count;
    }

    /**
     * 通过contentResolver,查询uri所指向的通话记录数据
     * 第一个参数:要操作的应用暴露出的uri。指向通话记录的数据
     * 第二个参数:要查询的字段
     * 第三个参数:查询的条件
     * 第四个参数:条件的参数值
     * 第五个参数:排序
     */
    // 查询数据
    public void queryCallLog() {
        // step3:通过contentResolver获取数据
        //Uri uri,String[] projection列名数组,String selection查询条件,String[] selectionArgs替换占位符的数组,String sortOrder
                Cursor cursor = contentResolver.query(uri_callLog, new String[]{"_id", "number", "date", "type"}, null, null, "date desc");
        // SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
        // R.layout.item_listview, cursor, new String[] { "_id", "number",
        // "date", "type" }, new int[] { R.id.tv_id,
        // R.id.tv_number, R.id.tv_date, R.id.tv_type },
        // CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);//这样填进去日期就只能显示成毫秒数了,不美观

        // 因此可以使用继承CursorAdapter的自定义适配器来适配Cursor数据
        // 父类CursorAdapter没有无参的构造方法,必须如此传参:
        adapter = new MyCursorAdapter(this, cursor, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); // 最后一个参数表示,如果数据源发生变化则在后面学习的Loader中可以动态更新.
        lv_listview.setAdapter(adapter);
    }

    // 自定义的继承抽象类CursorAdapter的适配器
    class MyCursorAdapter extends CursorAdapter {
        // 继承抽象类,重写两个抽象方法
        public MyCursorAdapter(Context context, Cursor c, int flags) {
            super(context, c, flags);// 父类没有无参的构造方法,必须如此传参
        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
            // 指定布局文件,生成一个条目的View对象,注意,在内部类中,上下文是MainActivity.this
            return LayoutInflater.from(Main3Activity.this).inflate(R.layout.item_listview, null);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {
            // 填充数据到每个条目的内部组件上,类似于继承BaseAdapter中重写getView()方法
            TextView tv_id = (TextView) view.findViewById(R.id.tv_id);
            TextView tv_number = (TextView) view.findViewById(R.id.tv_number);
            TextView tv_date = (TextView) view.findViewById(R.id.tv_date);
            TextView tv_type = (TextView) view.findViewById(R.id.tv_type);
            // 从cursor获取数据,显示到textview上
            tv_id.setText("" + cursor.getInt(cursor.getColumnIndex("_id")));
            tv_number.setText(cursor.getString(cursor.getColumnIndex("number")));
            long dateNum = cursor.getLong(cursor.getColumnIndex("date"));// 获取以毫秒表示的date数据
            Date date = new Date(dateNum);// 利用毫秒数构建Date对象
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            tv_date.setText(sdf.format(date));
            int typeNum = cursor.getInt(cursor.getColumnIndex("type"));
            if (typeNum == 1) {
                tv_type.setText("呼入");
            } else if (typeNum == 2) {
                tv_type.setText("呼出");
            } else {
                tv_type.setText("未接");
            }
        }
    }
}

/**
 * 短信息的程序的数据库文件:/data/data/com.android.providers.telephony/mmssms.db
 * Uri:content://sms
 * step1:获取contentResolver对象
 * step2:获取短信息的Uri
 * step3:查询,插入
 * step4:添加权限: READ_SMS , WRITE_SMS
 * @author  Administrator
 * 核心字段: _id , address 电话号码, body 信息文本, type 类型(1.接收;2.发送), date 时间(毫秒)
 */
四、使用 ContentResolver  查询 联系人
(一)、 [总结]使用ContentResolver 操作数据的步骤:
1、调用Context的 getContentResolver ()方法获得ContentResolver 对象;
2、调用ContentResolver 的 query() 方法查询数据。
· Cursor  query (Uri  uri , String[]  projection , String where, String[] whereArgs, String sortOrder)
参数解释:
String[]    projection :表示select语句中需要查询的所有的字段组成的字符串数组。
String   where:表示带有 占位符 的where子句组成的字符串;
String[]   whereArgs:表示 替换 where参数中 占位符 的数据组成的字符串数组;
String   sortOrder:表示select语句中的order by子句组成的字符串;
 
(二)、  联系人中 管理ContentProvider的几个Uri: 
1、联系人的Uri==>      content://com.android.contacts/ raw_contacts  
2、电话/邮件地址等数据的Uri==>  content://com.android.contacts/ data
 
数据库位置 :/data/data/com.android.providers.contacts/databases/contacts2.db
用可视化工具打开(例如SQLite Expert)
核心表:
1.raw_contacts原始联系人表
"_id":联系人的id
"display_name":联系人的姓名 
2.data数据表
"raw_contact_id":作为外键参照raw_contacts表的_id字段
"data1":具体数据,例如手机号,email,姓名等
"mimetype_id":数据的大类型,例如
1:email; 5:电话; 7:姓名; 在mimetypes表中对应于_id字段
3.mimetypes表
"_id":类型id
"mimetype":具体类型,例如_id为7的mimetype是 vnd.android.cursor.item/name
1 电子邮件
2 即时通讯
3 昵称
4 组织,单位
5 电话号码
6 邮编
7 名字
8 地址
9 身份证识别号
10 头像
11 组群
/**
 * 操作手机的联系人: 应用程序数据所在的位置:/data/data/com.android.providers/contacts/contacts2.db
 * step1:获取ContentResolver对象
 * step2:提供Uri 1、联系人的Uri==>
 * content://com.android.contacts/raw_contacts
 * 2、电话/邮件地址等数据的Uri==> content://com.android.contacts/data
 * step3:执行查询
 * step4: 添加权限 : READ_CONTACTS
 *
 * @author  Administrator
 */
public class MainActivity extends Activity implements OnClickListener {
private Uri uri_raw_contact = Uri.parse("content://com.android.contacts/raw_contacts");// raw_contacts表
private Uri uri_data = Uri.parse("content://com.android.contacts/data");
private ContentResolver contentResolver;
private ListView lv_listview;
private Button btn_queryData; 
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contentResolver = getContentResolver();
initView();// 自定义的获取组件引用和注册监听器的方法
queryContacts();// 自定义的查询联系人的方法
}
 
public void initView() {
lv_listview = (ListView) findViewById(R.id.lv_listview);
btn_queryData = (Button) findViewById(R.id.btn_queryData);
btn_queryData.setOnClickListener(this);
}
 
/**
 * 获取联系人的信息,并且显示到listview上。 用SimpleAdapter进行适配
 */
public void queryContacts() {
List<Map<String, String>> list = getDataContacts();// 自定义的方法,获取联系人的信息List
// 仅用于展示信息,可以直接使用SimpleAdapter
SimpleAdapter adapter = new SimpleAdapter(this, list, R.layout.item_listview,
new String[] { "_id", "display_name", "phone", "email" },
new int[] { R.id.tv_id, R.id.tv_display_name, R.id.tv_phone, R.id.tv_email });
lv_listview.setAdapter(adapter);
}
 
// 自定义的用于查询联系人的方法
public List<Map<String, String>> getDataContacts() {
List<Map<String, String>> list = new ArrayList<Map<String, String>>();//初始化结果集
// A:查询raw_contact表,为了得到_id,display_name.
Cursor cursor = contentResolver.query(uri_raw_contact, new String[] { "_id", "display_name" }, null, null,null);
while (cursor.moveToNext()) {
Map<String, String> map = new HashMap<String, String>();
String _id = cursor.getString(cursor.getColumnIndex("_id"));
String display_name = cursor.getString(cursor.getColumnIndex("display_name"));
map.put("_id", _id);
map.put("display_name", display_name);
// B:根据查询到_id,查询data表,获取到data1, mimetype字段:
// select data1,mimetype from data where raw_contact_id = _id:
Cursor cursor2 = contentResolver.query(uri_data, new String[] { "data1", "mimetype" }, "raw_contact_id=?",new String[] { _id }, null);
// 存储的是_id对应的联系人的信息:
String email = "";
String phone = "";
String address = "";
 
while (cursor2.moveToNext()) {
String data = cursor2.getString(cursor2.getColumnIndex("data1"));
String mimetype = cursor2.getString(cursor2.getColumnIndex("mimetype"));// 虽然data表没有这个字段,只有mimetype_id字段,但此处必须这样获取,且获取回来的是一个类似于"vnd.android.cursor.item/email_v2"的字符串
if ("vnd.android.cursor.item/email_v2".equals(mimetype)) {
email = email + "|" + data;// 如果mimetype的值为email,表示此处的data中的数据是电子邮箱
} else if ("vnd.android.cursor.item/phone_v2".equals(mimetype)) {// 电话(有可能有手机的,座机的,家庭的,单位的...)
phone = phone + "|" + data;
} else if ("vnd.android.cursor.item/postal-address_v2".equals(mimetype)) {// 地址
address = address + "|" + data;
}
}
map.put("phone", phone);
map.put("email", email);
map.put("address", address);
 
list.add(map);//把一个联系人的所有信息存入map后,再将map存入list
}
return list;
}
// 点击按钮,实现查询功能
@Override
public void onClick(View v) {
queryContacts();//调用自定义的方法
}
}
一、自定义ContentProvider:
  (一)、操作步骤:
1、自己编写一个类,必须继承自 ContentProvider 类;
2、实现ContentProvider类中所有的抽象方法;
    需要实现: onCreate () 、 getType()  、 query () 、 insert () 、 update ()、 delete () 等方法。
【备注:】
ContentProvider暴露出来的数据和方法是给其他应用程序来调用。
其他应用程序通过ContentResolver对象调用 query () 、 insert () 、 update ()、 delete () 等。
3、定义ContentProvider的Uri。这个Uri是ContentResolver对象执行CRUD操作时重要的参数;
4、使用UriMatcher对象映射Uri返回代码 ;【超纲】
5、在AndroidMainfest.xml文件中使用<provider>标签注册ContentProvider。
 
(二)、ContentProvider类中的六个抽象方法:
1、boolean  onCreate ()  初始化provider
  注意没有ContentResolver试图访问你的Provider之前,它不会创建出来.
2、Uri  insert (Uri uri, ContentValues values)  插入新数据到ContentProvider
3、int  delete (Uri uri, String selection, String[] selectionArgs) 从ContentProvider中删除数据
4、int  update (Uri uri, ContentValues values, String selection, String[] selectionArgs) 更新ContentProvider已经存在的数据
5、Cursor  query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)  返回数据给调用者
6、String  getType (Uri uri)  返回ContentProvider数据的MIME类型
(三)、在清单文件中声明注册ContentProvider:
<provider android:name=".WordsContentProvider"  
android:authorities ="com.qf.wordscontentprovider"  
android:exported ="true"  
 />
 
// android:name 属性的值:ContentProvider类的子类的完整路径;
// android:authorities 属性的值:对应于content:URI中的authority部分。
// android:exported 属性是否允许其他应用调用。如果是false,则该ContentProvider不允许其他应用调用。
 
【备注:】
        ContentProvider是 单例模式 的,当多个应用程序通过使用ContentResolver 来操作使用ContentProvider 提供的数据时,ContentResolver 调用的数据操作会委托给同一个ContentProvider 来处理。这样就能保证数据的一致性。
数据库工具类DBHelper.java
import  android.content.Context;
import  android.database.sqlite.SQLiteDatabase;
import  android.database.sqlite.SQLiteOpenHelper;
public   class  DBHelper extends   SQLiteOpenHelper {
 
public  DBHelper(Context context , int   versionCode ){
super ( context , "users.db" , null , versionCode );
}
@Override
public   void  onCreate(SQLiteDatabase db ) {
// TODO  初始化创建表
db . execSQL ( "create table t_user(_id integer primary key ,uname,upass,money)" );
db . execSQL ( "create table t_order(_id integer primary key ,product_name,price,user_id)" );
//对于 sqlite 来说,只要是integer的主键,则是自动增长的,无需用 autoincrement 指定
db . execSQL ( "insert into t_user(uname,upass,money) values('disen','123',1000)" );
db . execSQL ( "insert into t_user(uname,upass,money) values('jack','234',10000)" );
}
 
@Override
public   void  onUpgrade(SQLiteDatabase db , int   oldVersion , int   newVersion ) {
// TODO   升级数据库(删除旧表,创建新表)
if ( newVersion > oldVersion ){
db .execSQL( "drop table if exists t_user" );
db .execSQL( "drop table if exists t_order" );
onCreate( db );
}
}
 
}
在AndroidManifest.xml中的<application>标签中:
<!-- 注册ContentProvider组件,
  android: authorities 属性声明当前组件的唯一标识,在Resolver中使用uri时,它作为中间部分,后面还要跟着path
   android:exported="true" 设置当前组件可以被外部应用访问 -->
   < provider   android:name = "com.user.contentprovider.UserContentProvider"
    android:authorities = "com.user.contentprovider.users"
    android:exported = "true" />
 
UserContentProvider.java
 
public   class   UserContentProvider   extends  ContentProvider {
 
// 声明当前ContentProvider组件的唯一标识(Authority),注:必须使用小写字母
private   static   final  String AUTHORITY  = "com. user .contentprovider.users" ;
 
// 声明访问当前应用下的数据库中哪些资源,给这些访问资源声明code标识
private   static   final   int   CODE_USER  = 0;
private   static   final   int   CODE_ORDER  = 6;
 
// 声明完整的资源访问 Uri 的匹配器--UriMatcher,实例化它并增加资源访问的 Uri
private   static  UriMatcher uriMatcher ;
static  {
uriMatcher  = new  UriMatcher(UriMatcher. NO_MATCH ); // 值是-1
//UriMatcher. addURI (String authority, String path, int code)
// 用Resolver访问时的 Uri
// content://com.qf.contentprovider.users/users
uriMatcher .addURI( AUTHORITY , "users" , CODE_USER );
 
// content://com.qf.contentprovider.users/orders
uriMatcher .addURI( AUTHORITY , "orders" , CODE_ORDER ); //假设是订单表
}
  /**
 * 自定义的数据库工具类
 */
private  DBHelper dbHelper ;
 
@Override
public   boolean  onCreate() {
// TODO  初始化ConentProvider组件,实例化数据库操作工具类
dbHelper  = new  DBHelper( getContext (), 1);//第二个参数是版本号
return   true ; // 成功返回true
}
 
@Override
public  Cursor query(Uri uri , String[] projection , String selection , String[] selectionArgs , String sortOrder ) {
// TODO  查询数据表
SQLiteDatabase db  = dbHelper .getReadableDatabase(); // 先以读写方式打开数据库,一旦磁盘空间满了,
// 会继续尝试以只读方式打开数据库。
Cursor cursor  = null ; // 查询的结果
// 通过 Uri 匹配器,判断当前请求的 Uri 是访问哪一资源的code
switch  ( uriMatcher .match( uri )) {
case   CODE_USER : // 查询t_users用户表的数据:
cursor  = db .query( "t_user" , projection , selection , selectionArgs , null , null , sortOrder );
break ;
//case CODE_ORDER:.....
}
return   cursor ;
}
 
@Override
public  Uri insert(Uri uri , ContentValues values ) {
// TODO  向数据表中插入数据
SQLiteDatabase db  = dbHelper .getReadableDatabase(); // 先以读写方式打开数据库,一旦磁盘空间满了,
// 会继续尝试以只读方式打开数据库。
if  ( uriMatcher .match( uri ) == CODE_USER ) {//如果是插入t_user表
long   id  = db .insert( "t_user" , null , values );
db .close();
return   ContentUris. withAppendedId ( uri , id ); // 将给定的id追加到路径末尾返回
}
//else if()....
return   null ;
}
 
@Override
public   int  delete(Uri uri , String selection , String[] selectionArgs ) {
// TODO  删除数据表中数据
SQLiteDatabase db  = dbHelper .getReadableDatabase();
if  ( uriMatcher .match( uri ) == CODE_USER ) {
int   cnt  = db .delete( "t_user" , selection , selectionArgs );
db .close();
return   cnt ;//返回收到影响的行数
}
return  0;
}
 
@Override
public   int  update(Uri uri , ContentValues values , String selection , String[] selectionArgs ) {
// TODO  更新数据表中的数据
SQLiteDatabase db  = dbHelper .getReadableDatabase();
if  ( uriMatcher .match( uri ) == CODE_USER ) {
int   cnt  = db .update( "t_user" , values , selection , selectionArgs );
db .close();
return   cnt ;//返回收到影响的行数
}
return  0;
}
@Override
public  String getType(Uri uri ) {
// TODO  Auto-generated method stub
return   null ;
}
}
在另外一个app中利用ContentResolver来使用自定义的ContentProvider暴露出来的数据:
public class MainActivity extends Activity{
         private TextView tv_info;
         private ContentResolver resolver;
         private Uri uri = Uri.parse("content://com.user.contentprovider.users/users");
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          // TODO Auto-generated method stub
           super.onCreate(savedInstanceState);
           setContentView(R.layout.layout_main);
            tv_info = (TextView)findViewById(R.id.tv_info);
           resolver = getContentResolver();
      }
        public void click(View v){
             switch (v.getId()) {
             case R.id.bt_query:
              //建表时的语句:create table t_user(_id integer primary key,uname,upass,money)
              query();//调用自定义的查询所有数据的方法
              break;
            case R.id.bt_insert:
              insert();//调用自定义的插入数据的方法
              break;
            case R.id.bt_update:
            //请自行实现
             break;
          default:
            break;
        }
     }
     private void insert() {
       //调用自定义的插入数据的方法(硬编码)
        ContentValues values = new ContentValues();
          values.put("uname", "zhang");
        values.put("upass", "321");
          values.put("money", "99");
        Uri nUri = resolver.insert(uri, values);//sqlite会自动指定主键id
       long newId = ContentUris.parseId(nUri);//获取新插入的id
      Toast.makeText(this, ""+newId, 0).show();
   }
   private void query() {
    //自定义的查询所有数据的方法
     Cursor c = resolver.query(uri , new String[]{"_id","uname","upass","money"}, null, null,null);
    String text = "";
    while(c.moveToNext()){
          text +=c.getString(0)+","+c.getString(1)+","+c.getString(2)+","+c.getString(3)+"\n";
    }
     tv_info.setText(text);
   }
}





评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值