一 内容提供者
背景:
之前提到过内容提供者就是在访问数据的时候 那么它因何诞生
我们之前的数据访问像SQLite之类访问都是在当前应用程序访问
那则么能行 而其他应用程序之间的访问 就需要这一组件的帮助
画的有点粗糙
但大致就是这样工作的
B通过ContentResolver类访问A中ContentProvider暴露(共享)的数据
A通过ContentResolver类将结果返回给B
1.ContentResolver类 就是中介
2.ContentProvider实质上就是内容提供者
3.通过 uri 唯一标识 建立连接
那什么是uri呢?
这个就是Android中内容访问的 uri固定格式
content:// 包名 路径
当然当你访问Android中自有的东西例如通讯录人家的uri都封装好了直接用就行
那看看程序中的内容提供者是怎样的
是的就是一个java继承类
但是你会发现清单文件中出现了这个:
那光创建了 咋访问数据呢并且显示出来呢
简简单单三步走:
1.通过parse()方法解析uri
Uri uri=Uri.parse(你定义的或者自带的 uri标识);
2.query()方法查询数据(需要先创建ContentResolver对象)
ContentResolver resolver=context.getContentResolver();
Cursor cursor=resolver.query(5个参数)
3.while循环遍历游标
while(cursor.moveToNext()){
cursor对应的getInt()或者getString()方法获取然后输出
}
cursor.close()关闭释放
query中的五个参数
1.uri
2.查询内容
3.查询条件
4.配合参数
5.升序或者降序排序
这里还有多学一招 就是多个数据时 要使用到UriMatcher类
再来看看内容提供者的一个例子 读取手机通讯录
这里就不写layout文件美化了 也不录入数据
就直接在手机中添加联系人 然后log打印出来 主要看功能
后面一个综合例子 会写的
package com.example.four_content;
import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;
public class ContactActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getPermissions();
}
//关于危险权限 如申请(位置 日历 照相机 联系人 存储卡 传感器 麦克风 电话 短信)权限
//不仅需要在清单文件中静态申请权限 还要在代码中动态申请权限
//获取手机通讯录数据之前 需要申请读取手机通讯录的权限 所以需要在getPermissions()方法中申请获取权限
// 并重写onRequestPermissionsResult()方法 读取权限是否申请成功信息
String[] permissions;
public void getPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //版本是否大于6.0 Android 6.0开始,应用需要在运行时请求一些敏感权限
//初始化permissions数组,仅包含一个权限:读取联系人权限
permissions = new String[]{"android.permission.READ_CONTACTS"};
//创建一个ArrayList来存储尚未被授予的权限
ArrayList<String> list = new ArrayList<>();
//遍历permissions数组,并检查每个权限是否已经被授予
for (int i = 0; i < permissions.length; i++) {
if (ActivityCompat.checkSelfPermission(this, permissions[i]) != PackageManager.PERMISSION_GRANTED) {
list.add(permissions[i]);
//如果list中有未被授予的权限,则请求这些权限。1是请求代码,用于在onRequestPermissionsResult方法中识别这个特定的权限请求
if (list.size() > 0) {
ActivityCompat.requestPermissions(this, list.toArray(new String[list.size()]), 1);
} else {
setData();
}
} else {
setData();
}
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//检查请求代码是否为1,即之前getPermissions()方法中请求的权限
if (requestCode == 1) {
for (int i = 0; i < permissions.length; i++) {
//如果请求的是读取联系人权限,并且该权限已经被授予,则显示“读取成功”的Toast消息
if (permissions[i].equals("android.permission.READ_CONTACTS") && grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "读取成功", Toast.LENGTH_SHORT).show();
setData();
} else {
Toast.makeText(this, "读取失败", Toast.LENGTH_SHORT).show();
}
}
}
}
public void setData(){
ContentResolver contentResolver = getContentResolver();
Cursor cursor = contentResolver.query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
while (cursor.moveToNext()){
@SuppressLint("Range") String id = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts._ID));
@SuppressLint("Range") String name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
@SuppressLint("Range") int anInt = Integer.parseInt(cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)));
if (anInt>0){
Cursor cursor1 = contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + id, null, null);
while (cursor1.moveToNext()){
@SuppressLint("Range") String num = cursor1.getString(cursor1.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Log.i("高远", "setData: "+num);
}
cursor1.close();
}
}
cursor.close();
}
}
清单文件中加入:静态访问权限
<uses-permission android:name="android.permission.READ_CONTACTS" />
log打印
注意注意 上述代码是有误的
参考:https://www.cnblogs.com/tiancaige/p/10051563.html
就是遍历那里
contentResolver.query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, ContactsContract.CommonDataKinds.Phone.CONTACT_ID + " = " + id, null, null);
selectiong "=" 查询条件要加引号
二 内容观察者
背景:
那当访问其他应用程序数据时候 共享的数据是否发生变化了
我们就要使用到内容观察者ContentObserver来通过指定的uri来观察
1.当B操作A中数据发生变化时候 先ContentProvider调用ContentResolver的notifyChange()方法 发送消息中心数据变化的信息
2.C(内容观察者)得知消息中心数据变化时 触发ContentObserver的onChange()方法
关于内容观察者如何使用:
1.创建内容观察者 实质上是写一个继承ContentObserver的java类
2.注册内容观察者 通过ContentResolver的registerContentObserver()方法注册
3.取消注册 在onDestroy()方法中通过ContentResolver的unregisterContentObserver()方法
来看一个综合例子 监测数据的变化
三个玩意:
1.A内容提供者
2.B操作A
3.C内容观察者
跳过layout布局了 大致样式:
1.创建一个SQLite数据库
package com.example.contents;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class Myhelper extends SQLiteOpenHelper {
//构造方法,调用该方法创建一个person.db数据库
public Myhelper(Context context) {
super(context, "gao.db", null, 1);
}
@Override
public void onCreate(SQLiteDatabase db) {
//创建该数据库的同时新建一个info表,表中有_id,name这两个字段
db.execSQL("create table info(_id integer primary key autoincrement, name varchar(20))");
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
2.编写A ContentProvider内容提供者
package com.example.contents;
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 MyContentProvider extends ContentProvider {
//定义一个uri路径的匹配器,如果路径匹配不成功返回-1
private static UriMatcher mUriMatcher = new UriMatcher(-1);
private static final int SUCCESS = 1; //匹配路径成功时的返回码
private Myhelper helper; //数据库操作类的对象
//添加路径匹配器的规则
static {
mUriMatcher.addURI("com.example.contents", "info", SUCCESS);
}
@Override
public boolean onCreate() { //当内容提供者被创建时调用
helper = new Myhelper(getContext());
return false;
}
/**
* 查询数据操作
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
//匹配查询的Uri路径
int code = mUriMatcher.match(uri);
if (code == SUCCESS) {
SQLiteDatabase db = helper.getReadableDatabase();
return db.query("info", projection, selection, selectionArgs,
null, null, sortOrder);
} else {
throw new IllegalArgumentException("路径不正确,无法查询数据!");
}
}
/**
* 添加数据操作
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
int code = mUriMatcher.match(uri);
if (code == SUCCESS) {
SQLiteDatabase db = helper.getReadableDatabase();
long rowId = db.insert("info", null, values);
if (rowId > 0) {
Uri insertedUri = ContentUris.withAppendedId(uri, rowId);
//提示数据库的内容变化了
getContext().getContentResolver().notifyChange(insertedUri, null);
return insertedUri;
}
db.close();
return uri;
} else {
throw new IllegalArgumentException("路径不正确,无法插入数据!");
}
}
/**
* 删除数据操作
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int code = mUriMatcher.match(uri);
if (code == SUCCESS) {
SQLiteDatabase db = helper.getWritableDatabase();
int count = db.delete("info", selection, selectionArgs);
//提示数据库的内容变化了
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
db.close();
return count;
} else {
throw new IllegalArgumentException("路径不正确,无法随便删除数据!");
}
}
/**
* 更新数据操作
*/
@Override
public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
int code = mUriMatcher.match(uri);
if (code == SUCCESS) {
SQLiteDatabase db = helper.getWritableDatabase();
int count = db.update("info", values, selection, selectionArgs);
//提示数据库的内容变化了
if (count > 0) {
getContext().getContentResolver().notifyChange(uri, null);
}
db.close();
return count;
} else {
throw new IllegalArgumentException("路径不正确,无法更新数据!");
}
}
//返回MIME类型的数据 文档文件字节流格式 (例如windows中的.txt .jpg文件)
@Override
public String getType(Uri uri) {
return null;
}
}
3.编写B 操作A中共享的数据
package com.example.contents;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
public class BmakeA_Activity extends AppCompatActivity implements
View.OnClickListener {
private ContentResolver resolver;
private Uri uri;
private ContentValues values;
private Button btnInsert;
private Button btnUpdate;
private Button btnDelete;
private Button btnSelect;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_contents);
initView(); //初始化界面
createDB(); //创建数据库
}
private void initView() {
btnInsert = findViewById(R.id.btn_insert);
btnUpdate = findViewById(R.id.btn_update);
btnDelete = findViewById(R.id.btn_delete);
btnSelect = findViewById(R.id.btn_select);
btnInsert.setOnClickListener(this);
btnUpdate.setOnClickListener(this);
btnDelete.setOnClickListener(this);
btnSelect.setOnClickListener(this);
}
private void createDB() {
//创建数据库并向info表中添加3条数据
Myhelper helper = new Myhelper(this);
SQLiteDatabase db = helper.getWritableDatabase();
for (int i = 0; i < 3; i++) {
ContentValues values = new ContentValues();
values.put("name", "itcast" + i);
db.insert("info", null, values);
}
db.close();
}
@Override
public void onClick(View v) {
//得到一个内容提供者的解析对象
resolver = getContentResolver();
//获取一个Uri路径
uri = Uri.parse("content://com.example.contents/info");
//新建一个ContentValues对象,该对象以key-values的形式来添加数据到数据库表中
values = new ContentValues();
if (v.getId() == R.id.btn_insert) {
Random random = new Random();
values.put("name", "add_itcast" + random.nextInt(10));
Uri newuri = resolver.insert(uri, values);
Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show();
Log.i("数据库应用", "添加");
}
if (v.getId() == R.id.btn_delete) {
//返回删除数据的条目数
int deleteCount = resolver.delete(uri, "name=?",
new String[]{"itcast0"});
Toast.makeText(this, "成功删除了" + deleteCount + "行",
Toast.LENGTH_SHORT).show();
Log.i("数据库应用", "删除");
}
if (v.getId() == R.id.btn_select) {
List<Map<String, String>> data = new ArrayList<Map<String, String>>();
//返回查询结果,是一个指向结果集的游标
Cursor cursor = resolver.query(uri, new String[]{"_id", "name"},
null, null, null);
//遍历结果集中的数据,将每一条遍历的结果存储在一个List的集合中
while (cursor.moveToNext()) {
Map<String, String> map = new HashMap<String, String>();
map.put("_id", cursor.getString(0));
map.put("name", cursor.getString(1));
data.add(map);
}
//关闭游标,释放资源
cursor.close();
Log.i("数据库应用", "查询结果:" + data.toString());
}
if (v.getId() == R.id.btn_update) {
//将数据库info表中name为itcast1的这条记录更改为name是update_itcast
values.put("name", "update_itcast");
int updateCount = resolver.update(uri, values, "name=?",
new String[]{"itcast1"});
Toast.makeText(this, "成功更新了" + updateCount + "行",
Toast.LENGTH_SHORT).show();
Log.i("数据库应用", "更新");
}
}
}
4.创建C(内容观察者)程序
注意是一个新的项目:
package com.example.contentobserver;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 该uri路径指向数据库应用中的数据库info表
Uri uri = Uri.parse("content://com.example.contents/info");
//注册内容观察者,参数uri指向要监测的数据库info表,
//参数true定义了监测的范围,最后一个参数是一个内容观察者对象
getContentResolver().registerContentObserver(uri, true,
new MyObserver(new Handler()));
}
private class MyObserver extends ContentObserver {
public MyObserver(Handler handler) {//handler 是一个消息处理器。
super(handler);
}
@Override
//当info表中的数据发生变化时则执行该方法
public void onChange(boolean selfChange) {
Log.i("监测数据变化", "有人动了你的数据库!");
super.onChange(selfChange);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
//取消注册内容观察者
getContentResolver().unregisterContentObserver(new MyObserver(
new Handler()));
}
}
5.测试
先启动操作数据和内容提供者 数据情况如图
然后启动内容观察者
当点击事件后
内容提供者:
数据改变:
当数据变化时 都会调用 此方法 然后发送给数据中心数据变化的消息
getContext().getContentResolver().notifyChange(uri, null);
ok了至此结束
标签:51,uri,public,new,import,搞懂,null,Android,android From: https://www.cnblogs.com/gaodiyuanjin/p/18236491