"ContentProvider和数据库"的区别和联系专题-面试必问点大总结

大家好,今天我们来讲解ContentProvider和数据库的区别是他们之间的联系.

四大组件之一

1.ContentProvider是如何实现数据共享的?

  • 1.在Android中,为了把自己程序的数据(一般是数据库)提供给其他应用程序,就通过ContentProvider提供的方法.
  • 2.内容提供者可认为是程序间共享数据的接口,新建一个类继承ContentProvider.
  • 3.按要求重写insert,delete,update,query方法(用于数据库的操作).
  • 4.要记得进行清单文件注册:

    • 注册要加上作者标记authorities(自定义的):

      
      
  • 5.其他程序通过内容解析者ContentResoler的对象进行增删改查

2.为什么要使用ContentProvider?它和sql在实现上有什么区别?

  • 1.ContentProvider 屏蔽了数据存储的细节,内部实现透明化,用户只需关心uri即可(是否匹配)
  • 2.ContentProvider能实现不同app的数据共享,sql 只能是自己程序才能访问
  • 3.Contentprovider还能增删本地的文件,xml等信息

3.说说ContentProvider,ContentResolver,ContentObserver之间的关系?

  • ContentProvider:内容提供者,定义增删改查(方法)和数据库关联;
  • ContentResolver:内容解析者,一个app里边用于获取另一个app的数据(进行增删查改的具体数据操作)
  • ContentObserver:内容观察者,另外的一个app(可以是不同于上述两个app)可以监听数据改变的消息

    • 1.getContentResolver.notifyChange(uri):在内容提供者里面的各个方法添加,这样就能发出消息
    • 2.getContentResolver.registerContentOberver():进行监听注册,一个想观察内容变化的app,在观察者创建时就注册

4.如何访问assets资源目录下的文件?

针对AndroidStudio,和Eclipse的不同,AS没有资产文件夹,要自己创建,点击一下连接进行访问.

写了一篇案例,让你完全了解Assets和数据库的操作(点我访问)


5.高并发情况下,如何进行数据库查询?

(广泛回答)

  • 1.不要关联过多的表查询
  • 2.减少链接时间
  • 3.采用索引
  • 4.将查询到的数据进行缓存策略处理.

内容提供者的案例代码

分成3个程序

  • 银行应用程序(内容提供者(数据库)所在程序)
  • 行长应用程序(相当于另一个app,通过内容解析者对银行进行操作)
  • 观察者应用程序(当银行内容改变就会得到信息)

一. 银行(内容提供者)程序代码如下

  • 数据库打开帮助类代码
/**
 * 提示创建两分法+构造(写个上下文的就可以)
 */
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;
    }
}

  • MainActivity.java代码(就是拿来进行数据库创建而已,过程测试已经通过)
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();
    }
}

二. 行长(获取内容提供者里边的内容)程序代码如下**

  • (PS,这个程序就不用创建了,直接在刚才的银行App里面进行单元测试,就等同于执行行长app了)

代码如下:

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);
        }
    }
}

运行过程:

  • 运行内容观察者app
  • 运行银行(内容提供+数据库)app
  • 运行行长app(银行测试类)里面的删除方法

打印效果:

07-06 09:14:13.203 20859-20859/? I/System.out: 银行的客户和钱少了
07-06 09:14:13.203 20859-20859/? I/System.out: 银行的客户和钱少了

为了方便大家的更直观的理解我画出一个图

总结,大家了解真个交互过程就能完全理解这个版块了

  • uri是他们识别的信号
  • 内容解析者是他们沟通的桥梁;

你可能感兴趣的:(Android)