大家好,今天我们来讲解ContentProvider和数据库的区别是他们之间的联系.
四大组件之一
1.ContentProvider是如何实现数据共享的?
4.要记得进行清单文件注册:
注册要加上作者标记authorities(自定义的):
2.为什么要使用ContentProvider?它和sql在实现上有什么区别?
3.说说ContentProvider,ContentResolver,ContentObserver之间的关系?
ContentObserver:内容观察者,另外的一个app(可以是不同于上述两个app)可以监听数据改变的消息
4.如何访问assets资源目录下的文件?
针对AndroidStudio,和Eclipse的不同,AS没有资产文件夹,要自己创建,点击一下连接进行访问.
5.高并发情况下,如何进行数据库查询?
(广泛回答)
内容提供者的案例代码
分成3个程序
/**
* 提示创建两分法+构造(写个上下文的就可以)
*/
public class DBhelper extends SQLiteOpenHelper {
public DBhelper(Context context) {
//定义数据库 名字 和 版本,选择
super(context, "mycount.db", null, 1);
}
/**
* 定义表-通过sql语句
* 1.表名+主键+各个列名+类型
* 2.数据库执行语句
* 3.然后就可以单元测试一下是否创建成功了.即创建DBhelper对象,然后对象.获取读/写即可创建并获取数据库
*/
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "create table count (_id integer primary key autoincrement , name verchar , money integer )";
db.execSQL(sql);
}
//此方法调动的时机: 上面的1变成更大数的时候
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
/**
* 新建类继承内容提供者,注册并写作者标记,然后要求重写所有增删改查方法,写完先进行单元测试
*/
public class MyContentProvider extends ContentProvider {
//必须先创建URI匹配器,传入内置的码 ,设置成静态的为了初始化
static UriMatcher mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
//增加标记,和外在标记匹配
//参数一: 作者名,注册的时候对应也要写这个标记;参数二:一般写表名,为了其他程序的直接访问;参数3:匹配上之后返回的状态码
mMatcher.addURI("this.bank.authority", "count", 101);
//等下解析者,匹配的uri是:content//作者名+/+表名
}
//每个方法都要创建数据库帮助对象,然后实现进行增删改查功能,这些都是提供给-其他程序的"解析者"的,所以"不进行""具体参数操作",只是进行"方法操作"
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
//如果匹配就给操作,就是状态码相同
if (mMatcher.match(uri) == 101) {
DBhelper dh = new DBhelper(getContext());
SQLiteDatabase db = dh.getWritableDatabase();
db.insert("count", null, values);//只是做好-提供给解析者的方法即可
} else {
//不匹配就抛异常,不合法参数异常
throw new IllegalArgumentException("口令错误");
}
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
//同样需要匹配才能操作
if (mMatcher.match(uri) == 101) {
DBhelper dh = new DBhelper(getContext());
SQLiteDatabase db = dh.getWritableDatabase();
db.delete("count", selection, selectionArgs);//对应删除
//提供给内容观察者的
//获取解析.通知改变,因为外部app通过解析者操作内容提供者,所以在这里得到解析,并当解析的时候,发出消息
getContext().getContentResolver().notifyChange(uri,null);//第二个参数指谁能收到,不指定的话那只要匹配uri的都能收到.
} else {
//不匹配就抛异常,不合法参数异常
throw new IllegalArgumentException("口令错误");
}
return 0;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
//同样需要匹配才能操作
if (mMatcher.match(uri) == 101) {
DBhelper dh = new DBhelper(getContext());
SQLiteDatabase db = dh.getWritableDatabase();
db.update("count", values, selection, selectionArgs);//对应即可
} else {
//不匹配就抛异常,不合法参数异常
throw new IllegalArgumentException("口令错误");
}
return 0;
}
/**
* 返回一个游标
*/
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
//同样需要匹配才能操作
if (mMatcher.match(uri) == 101) {
DBhelper dh = new DBhelper(getContext());
SQLiteDatabase db = dh.getReadableDatabase();//查询只需要read即可
Cursor cursor = db.query("count", projection, selection, selectionArgs, null, null, sortOrder);//有的就对应填上即可.
//返回游标
return cursor;
} else {
//不匹配就抛异常,不合法参数异常
throw new IllegalArgumentException("口令错误");
}
}
@Override
public boolean onCreate() {
return false;
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
}
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initDatabase();
}
private void initDatabase() {
DBhelper dBhelper = new DBhelper(this);
dBhelper.getWritableDatabase();
}
}
代码如下:
public class ApplicationTest extends ApplicationTestCase {
public ApplicationTest() { super(Application.class); }
//测试创建帮助类,定义数据库+版本+表名+列名
//获取可读/写,完成创建.这一步可以写在MainActivity的onCreate里
public void testDB() {
DBhelper dBhelper = new DBhelper(getContext());
dBhelper.getWritableDatabase();
}
public void testInsert() {
//获取解析对象,和内容提供者匹配,从而达到操作数据库的目的
//如果是在Activity直接get,这里就要通过上下文get
ContentResolver resolver = getContext().getContentResolver();
//注意Uri的字符串构成是:content+:+//+作者名+/+表名,格式不能忘有content:
Uri uri = Uri.parse("content://this.bank.authority/count");
ContentValues values = new ContentValues();
values.put("name", "小明");
values.put("money", 12300);
//注意:这样连续添加会小明被覆盖掉,只插入了小强,因为不是个集合,解决方法是分开
values.put("name", "小强");
values.put("money", 14000);
resolver.insert(uri, values);
values.put("name", "小王");
values.put("money", 12300);
resolver.insert(uri, values);
values.put("name", "小张");
values.put("money", 12300);
resolver.insert(uri, values);
}
//注意条件参数的对应01
public void testQuery() {
Uri uri = Uri.parse("content://this.bank.authority/count");
ContentResolver contentResolver = getContext().getContentResolver();
//第五个;null:默认升序;DESC:降序,根据后续需要
Cursor cursor = contentResolver.query(uri, new String[]{"name","money"}, null, null,null);
while (cursor.moveToNext()) {
//0,1对应上面的第一第二个参数
String name = cursor.getString(0);
String money = cursor.getString(1);
System.out.println("name : "+name+" money : "+money);
}
//关闭游标
cursor.close();
}
//删除,重点是 列名要加问号,不然报异常
public void testDelete() {
ContentResolver contentResolver = getContext().getContentResolver();
Uri uri = Uri.parse("content://this.bank.authority/count");
//加问号不然提示异常
contentResolver.delete(uri, "name=?", new String[]{"小强"});
contentResolver.delete(uri, "name=?", new String[]{"小张"});
// contentResolver.delete(uri, null,null);//写成这样意味着全部删除
}
public void testUpdate() {
Uri uri = Uri.parse("content://this.bank.authority/count");
ContentResolver contentResolver = getContext().getContentResolver();
ContentValues values = new ContentValues();
values.put("name", "小明");
contentResolver.update(uri, values, "name=?", new String[]{"小张"});
// contentResolver.update(uri, values, null, null);//写成这样意味着全部替换
}
}
通过内容解析者注册即可
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//获取解析
//注册内容观察者
//此uri变化就会做出通知
Uri uri = Uri.parse("content://this.bank.authority/count");
//参数二,满足前半段的uri也获取通知
getContentResolver().registerContentObserver(uri,true,new myContentObserver(new Handler()));
}
class myContentObserver extends ContentObserver {
public myContentObserver(Handler handler) {
super(handler);
}
@Override
public void onChange(boolean selfChange) {
System.out.println("银行的客户和钱少了");
super.onChange(selfChange);
}
}
}
运行过程:
打印效果:
07-06 09:14:13.203 20859-20859/? I/System.out: 银行的客户和钱少了
07-06 09:14:13.203 20859-20859/? I/System.out: 银行的客户和钱少了
总结,大家了解真个交互过程就能完全理解这个版块了