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

Android ContentResolver的使用 本文介绍了ContentResolver的使用方法,包括查询数据、插入数据、更新数据和删除数据。通过ContentResolver,我们可以方便地与ContentProvider进行数据交互,实现数据的读取和修改。它提供了一组方法,用于查询、插入、更新和删除ContentProvider中的数据。本文将详细介绍ContentResolver的使用方法,并提供相应的源代码示例。首先,需要构建一个Uri对象,指定要查询的数据的位置和条件。除了使用Uri查询数据外,还可以使用SQL语句来查询数据。 阅读详情
ContentResolver 内容解析者(解析器)
一、ContentProvider(内容提供者)简介:
 
(一)、引入:
         数据库在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 DBHelperextends SQLiteOpenHelper{
 
public DBHelper(Contextcontext,int versionCode){
super(context,"users.db",null,versionCode);
}
@Override
public void onCreate(SQLiteDatabasedb) {
// 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(SQLiteDatabasedb,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 StringAUTHORITY ="com.user.contentprovider.users";
 
// 声明访问当前应用下的数据库中哪些资源,给这些访问资源声明code标识
private static final int CODE_USER = 0;
private static final int CODE_ORDER = 6;
 
// 声明完整的资源访问Uri的匹配器--UriMatcher,实例化它并增加资源访问的Uri
private static UriMatcheruriMatcher;
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 DBHelperdbHelper;
 
@Override
public boolean onCreate() {
// TODO 初始化ConentProvider组件,实例化数据库操作工具类
dbHelper =new DBHelper(getContext(), 1);//第二个参数是版本号
return true;// 成功返回true
}
 
@Override
public Cursor query(Uriuri, String[]projection, Stringselection, String[]selectionArgs, StringsortOrder) {
// 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(Uriuri, ContentValuesvalues) {
// 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(Uriuri, Stringselection, 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(Uriuri, ContentValuesvalues, Stringselection, 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(Uriuri) {
// 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);
   }
}





Android中ContentProvider和Uri详解 它提供了一种结构化的方式来管理应用程序之间的数据访问,并提供了一组标准化的API,使得其他应用程序可以方便地访问和操作这些数据。本文将详细介绍ContentProvider和Uri的概念,并提供相应的源代码示例。ContentProvider提供了一种标准化的方式来管理数据,而Uri则用于定位和访问具体的数据。ContentProvider可以管理结构化的数据,例如数据库表,也可以管理非结构化的数据,例如文件。通过解析指定的Uri,我们可以获取到ContentProvider的实例,并进行相应的操作。 阅读详情

相关推荐

Android之学习笔记 Contacts (一)ContentResolver query 参数详解

1.获取联系人姓名 一个简单的例子,这个函数获取设备上所有的联系人ID和联系人NAME。 [java] view plaincopy public void fetchAllContacts() {       ContentResolver contentResolver = this.getContentResolver();

码莎拉蒂 1780

Android基础——ContentProvider和contentResolver

ContentProviderContentProvider是什么?权限运行时权限 ContentProvider是什么? 利用ContentProvider可实现在不同应用程序之间的数据共享,并保证被访问数据的安全性。 权限 普通权限:不会直接威胁到用户安全和隐私的权限,这部分权限会自动授权,不再需要手动操作 危险权限:可能会触及用户隐私或对设备安全性造成影响的权限,必须要由用户手动授权,否则无法使用。危险权限需要进行运行时权限处理 运行时权限 ...

松仔Log的博客 1万+

STM32H750实战:用TIM+DMA驱动ADC/DAC实现音频回环(附CubeMX配置避坑指南)

本文详细介绍了在STM32H750上构建高保真、低延迟音频链路的实战方法。通过配置TIM定时器作为硬件节拍器,精准触发ADC采集与DAC播放,并利用DMA实现数据的自动搬运,全程无需CPU干预。文章重点剖析了CubeMX配置中的关键步骤与常见“坑点”,如ADC的Conversion Data Management模式选择、DMA的FIFO与Burst模式配置,以及STM32H7系列必须开启ADC全局中断等,并提供了完整的代码实现与调试验证指南。

1057

android contentResolver的使用

今天,简单讲讲android 如何使用contentResolver。 这个contentResolver其实是属于ContentProvider 的内容,我之前一直没有用到android的ContentProvider ,所以昨天看到contentResolver时,居然不知道contentResolver怎么使用,后来,在网上查找了资料,最终解决了问题。这里记录一下。 在A

谢岩的博客 2万+

从原理到调试:手把手教你用Matlab解码互补格雷码与相移码(附数据与常见错误排查)

本文详细介绍了如何使用Matlab解码互补格雷码与相移码,涵盖从原理到调试的全过程。通过实际案例和代码示例,帮助读者掌握结构光三维重建中的相位解包裹技术,并提供了常见错误排查与优化建议,提升解码效率与准确性。

weixin_29090917的博客 218

直线型一阶倒立摆1---从理论到实践的桥梁

本文深入探讨了直线型一阶倒立摆在控制理论中的核心地位,从非线性控制、鲁棒性到实时反馈等关键问题。通过实际案例和硬件搭建指南,帮助读者理解从理论到实践的过渡,特别适合控制工程初学者和研究人员。文章还提供了算法选型建议和硬件避坑手册,是掌握倒立摆控制的实用指南。

weixin_29047545的博客 317

Android ContentResolver

*registerContentObserver(Uri uri, boolean notifyForDescendants, ContentObserver observer)**方法的第四个参数是flags,用于指定注册ContentObserver的标志。ContentResolver.registerContentObserver()方法除了常用的异步方法之外,还提供了一些同步方法,用于阻塞当前线程,直到观察到的Uri发生变化。这两个方法的第一个参数是Uri,用于指定要观察的Uri。

嗯... 5429

俄罗斯方块 C++控制台版

一、创建方块 wstring tetromino[7]; tetromino[0].append(L"..X...X...X...X."); // Tetronimos 4x4 tetromino[1].append(L"..X..XX...X....."); tetromino[2].append(L".....XX..XX....."); tetromino[3].append(L"..X..XX..X......"); tetromino...

一只大鸽子的博客 1622

Android 学习笔记 Contacts (一)ContentResolver query 参数详解

1.获取联系人姓名 一个简单的例子,这个函数获取设备上所有的联系人ID和联系人NAME。 public void fetchAllContacts() { ContentResolver contentResolver = this.getContentResolver(); Cursor cursor = contentResolver.query(android.provid

Dake 8万+

基于紫光同创FPGA的adf网表文件使用和黑匣子设置

这个时候就可以选择将你的网表文件给到他使用,就是相当于把你的功能模块封装成了一个黑匣子一样,只有外部端口,没有内部代码;,在TOP层的子模块声明里面需要加上/*synthesis syn_black_box */进行黑匣子设置,此时子层 adf 文件作为黑盒是不会综合的,在 device map 时会将顶层综合结果与子层 adf 文件合并传递至布局布线阶段;这些都设置完毕后呢,就可以对工程进行编译和综合了,综合完毕后会生成一个.adf的网表文件,我们要使用的就是这个文件;我这里是把两个子模块的添加了哈;

H2061722的博客 964

Spring AI Alibaba实战训练营-16 NL2SQL (自然语言转 SQL)应用开发指南

本文介绍了一个基于AI的智能BI应用开发项目,主要实现自然语言转SQL(NL2SQL)功能,包含对话模块、MCP模块、向量数据管理模块。项目采用Spring AI Alibaba框架,集成通义千问大模型,支持内存向量库和AnalyticDB向量数据库两种存储方案。重点展示了chat模块的三个控制器实现:AnalyticNl2SqlController用于生产环境,SimpleChatController适用于开发测试,Nl2sqlForGraphController支持基于图的复杂对话流程。系统通过将业务知

program哲学 1585

andorid 学习之ContentProvider 和 ContentResolver 使用笔记

ContentProvider 和 ContentResolver 使用

wsdbtx的博客 795

向华为学习——解读158页 华为敏捷园区网技术解决方案【附全文阅读】

华为敏捷园区网解决方案通过策略随行和体验随身技术,满足移动办公场景下的网络需求。该方案采用敏捷交换机和防火墙自动执行策略配置,实现用户身份认证与业务策略的无缝衔接,且策略配置与IP地址解耦,简化了管理流程。相比传统技术,该方案有效解决了用户分级、QoS保障、无线接入等关键问题,确保用户在移动过程中获得一致的网络体验。

cdfunlove的博客 379

Android ContentResolver query 参数详解

1.获取联系人姓名 一个简单的例子,这个函数获取设备上所有的联系人ID和联系人NAME。 public void fetchAllContacts() { ContentResolver contentResolver = this.getContentResolver(); Cursor cursor = contentResolver.query(android.provid

zihchao的专栏 1333

假新闻检测论文分享(25)L-DefenseExplainable Fake News Detection With Large Language Model

现有的假新闻检测工作主要集中在通过深度网络结合多种信息来学习假新闻的潜在特征,如可信度、立场、传播模式、额外知识、域外数据等。尽管他们在探测上取得了成功,但受限于他们的黑箱性质,他们无法提供任何对公众至关重要的理由。为解决这一问题,一些研究致力于可解释假新闻检测(EFND),旨在同时生成准确性预测和解释。许多工作通过注意力机制在相关报道中注意突出特别的单词、短语或句子来提供可解释性。然而,这些工作只发现了对最终预测有高贡献的区域,缺乏直观和可信的解释。

weixin_41964296的博客 730

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Android中ContentResolver的query方法中参数的详细解析

例:获取联系人姓名 一个简单的例子,这个函数获取设备上所有的联系人ID和联系人NAME。 public void fetchAllContacts() {       ContentResolver contentResolver = this.getContentResolver();       Cursor cursor = contentResolver.query(a

flymoon1201的专栏 3074

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

Android:联系人Contacts之ContentResolver query 参数详解

注:本片整理自 http://blog.youkuaiyun.com/wssiqi/article/details/8132603 1.获取联系人姓名 一个简单的例子,这个函数获取设备上所有的联系人ID和联系人NAME。   1 public void fetchAllContacts() { 2 ContentResolver contentResolver = this.ge

CallRock的博客 564

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

ContentResolver query 参数详解

1.获取联系人姓名 一个简单的例子,这个函数获取设备上所有的联系人ID和联系人NAME。 [java]view plaincopy publicvoidfetchAllContacts(){ ContentResolvercontentResolv...

chuanao8829的博客 265

Android 11+本地音乐播放新姿势:告别传统文件扫描,拥抱MediaStore API

本文详细解析了Android 11及以上版本中,本地音乐播放开发如何从传统的文件扫描方式转向使用MediaStore API。针对分区存储新规,文章提供了权限适配策略、使用ContentResolver高效查询音频库的完整代码示例,并推荐结合ExoPlayer构建现代化播放引擎,以实现更安全、高性能且兼容未来的音乐播放应用。

grpc6streamer的博客 856

Android Contacts (一)ContentResolver query 参数详解

1.获取联系人姓名 一个简单的例子,这个函数获取设备上所有的联系人ID和联系人NAME。 [java] view plaincopy public void fetchAllContacts() {       ContentResolver contentResolver = this.getContentResolver();       

mrleeapple的专栏 673
上一篇: JavaScript常用方法记录
下一篇: Android 补间、属性动画----重新认识Android(12)
AND_YOU_with_ME
博客等级 码龄11年 9粉丝 53原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值