ContentProvider与sqlit的搭配使用

上一篇文章Android持久化存储的几种方式已经讲诉了Android开发中文件存储、网络存储和SharedPreferences这三种持久化存储的方式,以及大致讲了什么是ContentProvider和它的使用场景。那么这篇文章我们就来具体讲诉一下如何使用ContentProvider与sqlit搭配,实现与一个APP对另一个APP的数据增删改查。

首先我们使用SQLiteOpenHelper这个类来创建一个数据库,并创建一个储存个人信息的People_Message的表。

public class MyDatabaseHelper extends SQLiteOpenHelper {
    //sql语句,用来创建表。
    public static final String People_Message="create table people("
            +"num integer primary key autoincrement,"
            +"name text,"
            +"sex text)";
    Context mContext;
    public MyDatabaseHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        //调用父类构造方法,创建数据库。第一个参数是context。第二个参数是数据库的名字,第三个参CursorFactory指定在执行查询时获得一个游标实例的工厂类,设置为null,代表使用系统默认的工厂类,第四个参数是数据库的版本号,不能小于一。
        super(context, name, factory, version);
        mContext=context;

    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        //创建表,执行sql语句
        db.execSQL(People_Message);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        //如果需要升级数据库,会调用该方法。可以用来删除表或者添加表。
    }
}

上面是一个继承了SQLiteOpenHelper类的类。在构造方法中会调用父类构造方法,创建一个数据库。然后在onCreate中使用 db.execSQL()这个方法执行sql语句,进行表创建。
然后我们需要在主线程中创建一个MyDatabaseHelper对象,这样就创建好了数据库和表(如果数据库和表已经存在,将不会创建)。

mMyDatabaseHelper=new MyDatabaseHelper(getApplicationContext(),"people_message",null,1);

然后我们需要得到一个SQLiteDatabase 对象,这个对象可以用来操作数据库。

//得到可以读写数据库的SQLiteDatabase
db=mMyDatabaseHelper.getWritableDatabase();
//得到只读数据库的SQLiteDatabase
//db=mMyDatabaseHelper.getReadableDatabase();   

上面代码可以,我们可以通过刚才创造的SQLiteOpenHelper对象来获取SQLiteDatabase。其中有两种获取方式,一个可以对数据库进行写入,另一个只读。
SQLiteDatabase这个对象中有几个方法,是对修改数据库的几种方式进行了包装。
从数据库中查询数据
db.query(String table,String[] columns,String selection,String[] selectionArgs,String groupBy,String having,String orderBy,String limit)
参数意义:
table:表名
columns:需要得到的列名数组
selection:条件字句,相当于where
selectionArgs:条件字句,条件的数组
groupBy:分组列名
having:分组条件
orderBy:排序列名
limit:查询限制

往数据库中插入数据
db.insert(String table,String nullColumnHack,ContentValues values)
参数意义:
table:表名
nullColumnHack:空列的默认值
values:插入的数据,key-value类型的集合

把数据库中指定的数据删除
db.delete(String table,String whereClause,String[] whereArgs)
参数意义:
table:表名
whereClause:删除条件
whereArgs:删除条件值数组

修改数据库中指定的数据
db.update(String table,ContentValues values,String whereClause, String[] whereArgs)
参数意义:
table:表名
values:修改的数据,key-value类型的集合
whereClause:修改条件
whereArgs:修改条件值数组

执行具体的sql语句
db.execSQL(“sql语句”)

db.XXXX的方法还有许多,如果上面的不能满足你的要求,你可以看看源码,选择自己需要的方法。
上述方法中都有很多参数,除了db.execSQL()中的参数是你需要执行的sql语句外,其他的参数都有很多,这里不细讲,使用的时候大家可以再学习。

上面我们已经建立了一个数据库并且创建了一个表。下面就是需要对表进行操作了。这里我们使用ContentProvider来对表进行操作。

我们创建一个类继承与ContentProvider,并在AndroidManifast中进行注册。

public class MyContentProvider extends ContentProvider
{
    public MyContentProvider()
    {
    }

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs)
    {
        // Implement this to handle requests to delete one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public String getType(Uri uri)
    {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values)
    {
        // TODO: Implement this to handle requests to insert a new row.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public boolean onCreate()
    {
        // TODO: Implement this to initialize your content provider on startup.
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder)
    {
        // TODO: Implement this to handle query requests from clients.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs)
    {
        // TODO: Implement this to handle requests to update one or more rows.
        throw new UnsupportedOperationException("Not yet implemented");
    }
}

上面代码可以看见,除了无参构造函数之外,还会自动生成几个重写方法,这几个方法就是上面我们所说的增删改查几个方法,我们可以在对应函数中就可以自己编写对数据库的操作了。
我们发现上面的几个方法中第一个参数类型都是Uri,这个参数代表要操作的数据。我们先来简单说一下Uri的构造,理解一下Uri具体的用法。

Uri 通用资源标志符(Universal Resource Identifier)

uri及基本结构是 [scheme:][//authority][path][?query][#fragment] 这个五个部分,但是除了[scheme:][//authority]必须有之外,其他的每一个部分都可以不需要。但是顺序不能改变。
拿一个例子来说
uri=http://www.java2s.com:8080/yourpath/fileName.htm?stove=10&path=32&id=4#harvic
对于这个uri来说,我们来解析一下它的各部分结构。
[scheme:]=http 在//之前都属于[scheme:]
[//authority]=www.java2s.com:8080 主机名 //之后[path]之前
[path]=yourpath/fileName.htm 具体的路径 主机名之后[?query]之前都属于[path]
[?query]=?stove=10&path=32&id=4 ?号之后 #之前
[#fragment]=#harvic #之后都属于[#fragment]
(上面只是对Uri结构的简单的介绍,如果详细了解可以点击Uri详解之——Uri结构与代码提取查看)

上面就是Uri的具体结构,我们通过具体的Uri就可以访问我们需要访问的数据了,对于ContentProvider来说,用来访问它的Uri有具体的格式。
[scheme:]=content
[//authority]=主机名,在AndroidManifest文件中可以对具体ContentProvider的authorities属性进行修改和查看,这里我们上面定义ContentProvider的authorities为”test_contentProvider”。

<provider
            android:name=".contentProvider.MyContentProvider"
            android:authorities="test_contentProvider"
            android:enabled="true"
            android:exported="true">
provider>

[path]就是我们表的名字,在这个事例程序中就是”people”。
所以这个ContentProvider的指定Uri就是

Uri PEO=Uri.parse("content://test_contentProvider/people");

那么我们来看看完整的MyContentProvider类应该是怎样的

public class MyContentProvider extends ContentProvider
{
    MyDatabaseHelper mMyDatabaseHelper;
    SQLiteDatabase db;
    UriMatcher sMatcher;
    Uri PEO=Uri.parse("content://test_contentProvider/people");
    public MyContentProvider()
    {
    }
    //用于匹配传入的URI,如果不匹配返回-1
    public int getUriMatcher(Uri uri){
        return sMatcher.match(uri);
    }
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs)
    {
        //删除数据
        if(getUriMatcher(uri)==1){
            db.delete("people",selection,selectionArgs);
            getContext().getContentResolver().notifyChange(PEO,null);
            return 1;
        }
        return 0;
    }

    @Override
    public String getType(Uri uri)
    {
        // TODO: Implement this to handle requests for the MIME type of the data
        // at the given URI.
        throw new UnsupportedOperationException("Not yet implemented");
    }

    @Override
    public Uri insert(Uri uri, ContentValues values)
    {
        //判断传入的URI是否匹配,匹配的话将数据插入数据库
        if (getUriMatcher(uri)==1){
            db.insert("people",null,values);
            getContext().getContentResolver().notifyChange(PEO,null);
        }
        return null;
    }

    @Override
    public boolean onCreate()
    {
        //打开数据库,得到db
        mMyDatabaseHelper=new MyDatabaseHelper(getContext(),"data",null,1);
        //得到可以读写数据库的SQLiteDatabase
        db=mMyDatabaseHelper.getWritableDatabase();
        //UriMatcher.NO_MATCH表示不匹配任何Uri的返回码-1
        sMatcher=new UriMatcher(UriMatcher.NO_MATCH);
        //添加一个匹配规则:第一个参数contentProvider的authority值,第二个参数数据表名,第三个参数 匹配后返回值
        sMatcher.addURI("test_contentProvider","people",1);
        return false;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder)
    {
        //查询数据
        if (getUriMatcher(uri)==1){
            Cursor cursor=db.query("people",null,selection,selectionArgs,null,null,sortOrder);
            return cursor;
        }
        return  null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
                      String[] selectionArgs)
    {
        //更新数据
        if (getUriMatcher(uri)==1){
            db.update("people",values,selection,selectionArgs);
            getContext().getContentResolver().notifyChange(PEO,null);
            return 1;
        }
        return  0;
    }

1.首先我们在onCreate构造函数中,创建或打开要操作的数据库,得到用来操作数据库的db。
2.接着我们为new出一个UriMatcher对象,给这个对象添加一个URi(可以同时添加多个,只要返回码不一样),这个Uri就是用来访问当前MyContentProvider的指定Uri,通过对ContentProvider时传入的URI进行校验,判断是否可以能够匹配,根据匹配的返回码,得知具体需要操作的数据。
3.在增删改查的四个方法中,进行具体的数据操作,这篇文章主要是对数据库的增删改查操作,所以直接使用db.xxxx()方法来进行操作。
4.在增删改完成之后,如果对数据进行了修改,使用getContext().getContentResolver().notifyChange(PEO,null)这段代码,对指定的URI添加一个通知,通知当前URI的数据有修改,便于ContentObserver进行监听。

上面就构造出了一个自定义的ContentProvider。它的指定Uri”content://test_contentProvider/people”。
我们初始化数据库,向数据库中插入十条数据,便于显示和查询。

public class PeopleActivity extends AppCompatActivity
{

    MyDatabaseHelper mMyDatabaseHelper;
    SQLiteDatabase db;
    MyContentProvider mMyContentProvider;
    ListView mListView;
    List mPeopleBeens;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_people);
        mListView=(ListView)findViewById(R.id.list);
        mPeopleBeens=new ArrayList<>();
        mMyDatabaseHelper=new MyDatabaseHelper(getApplicationContext(),"data",null,1);
        //得到可以读写数据库的SQLiteDatabase
        db=mMyDatabaseHelper.getWritableDatabase();
        PeopleBeen p;
        for(int i=0;i<10;i++){
            p=new PeopleBeen();
            p.setNum(10+i);
            p.setName("王"+i);
            p.setSex(((i%3==0)?"女":"男"));
            ContentValues values = new ContentValues();
            values.put("num", p.getNum());
            values.put("name", p.getName());
            values.put("sex",p.getSex());
            db.insert("people",null, values);

        }
        Cursor cursor=db.query("people",null,null,null,null,null,null);
        while (cursor.moveToNext()){
            PeopleBeen pp=new PeopleBeen();
            pp.setNum(cursor.getInt(cursor.getColumnIndex("num")));
            pp.setName(cursor.getString(cursor.getColumnIndex("name")));
            pp.setSex(cursor.getString(cursor.getColumnIndex("sex")));
            mPeopleBeens.add(pp);
        }
        //mMyContentProvider=new MyContentProvider();
        mListView.setAdapter(new ArrayAdapter(this, android.R.layout.simple_expandable_list_item_1, getData()));
    }
    private List getData(){

        List data = new ArrayList();
        for(PeopleBeen p:mPeopleBeens){
            data.add(p.getNum()+"   名字:"+p.getName()+"   性别:"+p.getSex());
        }

        return data;
    }
    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        db.close();;
    }
}

运行结果:
ContentProvider与sqlit的搭配使用_第1张图片

现在我们新建一个项目,在另一个项目中使用这个ContentProvider。

public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
    EditText ed_num;
    EditText ed_name;
    EditText ed_sex;
    Button bt_cx;
    Button bt_cr;
    Button bt_xg;
    Button bt_sc;
    TextView tv_jg;
    ContentResolver mContentResolver;
    PersonOberserver personOberserver;
    Uri PEO=Uri.parse("content://test_contentProvider/people");
    Handler mHandler=new Handler(){
        @Override
        public void handleMessage(Message msg)
        {
            super.handleMessage(msg);
            Toast.makeText(getApplication(),"修改成功",Toast.LENGTH_LONG).show();
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ed_num=(EditText)findViewById(R.id.et_num);
        ed_name=(EditText)findViewById(R.id.et_name);
        ed_sex=(EditText)findViewById(R.id.et_sex);
        bt_cx=(Button)findViewById(R.id.bt_cx);
        bt_cr=(Button)findViewById(R.id.bt_cr);
        bt_xg=(Button)findViewById(R.id.bt_xg);
        bt_sc=(Button)findViewById(R.id.bt_sc);
        tv_jg=(TextView)findViewById(R.id.tv_jg);
        bt_cx.setOnClickListener(this);
        bt_cr.setOnClickListener(this);
        bt_xg.setOnClickListener(this);
        bt_sc.setOnClickListener(this);
        //初始化ContentResolver,用来与ContentProvider建立联系
        mContentResolver=getContentResolver();
        //初始化ContentOberserver,用来监听指定URI数据的改变。
        personOberserver=new PersonOberserver(mHandler);
        //注册监听,当指定URI发生改变时,personOberserver中的onChange()会得到执行。
        mContentResolver.registerContentObserver(PEO,true,personOberserver);
    }

    @Override
    public void onClick(View view)
    {
        switch (view.getId()){
            case R.id.bt_cr:
                //插入数据
                ContentValues values = new ContentValues();
                values.put("num", Integer.parseInt(ed_num.getText().toString()));
                values.put("name", ed_name.getText().toString());
                values.put("sex",ed_sex.getText().toString());
                mContentResolver.insert(PEO,values);
                break;
            case R.id.bt_cx:
                //根据编号查询数据
                tv_jg.setText("");
                Cursor cursor = mContentResolver.query(PEO, null, "num=?", new String[]{ed_num.getText().toString()}, null);
                while (cursor.moveToNext()){
                    tv_jg.setText("编号:"+cursor.getInt(cursor.getColumnIndex("num"))+"   姓名:"+cursor.getString(cursor.getColumnIndex("name"))+"   性别:"+cursor.getString(cursor.getColumnIndex("sex")));
                }
                break;
            case R.id.bt_sc:
                //根据编号删除数据
                int result=mContentResolver.delete(PEO,"num=?",new String[]{ed_num.getText().toString()});
                break;
            case R.id.bt_xg:
                //根据编号修改数据
                ContentValues values_xg = new ContentValues();
                values_xg.put("name", ed_name.getText().toString());
                values_xg.put("sex", ed_sex.getText().toString());
                mContentResolver.update(PEO,values_xg,"num=?",new String[]{ed_num.getText().toString()});
                break;

        }
    }

    @Override
    protected void onDestroy()
    {
        super.onDestroy();
        mContentResolver.unregisterContentObserver(personOberserver);
    }
}


public class PersonOberserver extends ContentObserver
{
    Handler mHandler;


    public PersonOberserver(Handler handler)
    {
        super(handler);
        mHandler=handler;
    }

    @Override
    public void onChange(boolean selfChange)
    {
        Log.e("dd","dd");
        Message message=new Message();
        mHandler.sendMessage(message);
    }
}

我们看上面的代码,首先在onCreate周期函数中,初始化了ContentResolver,ContentResolver叫做内容解析者,当外部应用需要对ContentProvider中的数据进行添加、删除、修改和查询操作时,可以使用ContentResolver 类来完成。ContentResolver 中也拥有insert(),delete(),update(),query()这四种方法,里面的参数和数据库操作时使用的增删改查方法的参数差不多。这四种方法对应的就是ContentProvider的四种数据操作方法
接着初始化了PersonOberserver对象,这个对象是一个可以用来监听特定URI数据变化的对象。我们用registerContentObserver方法来给ContentResolver注册一个PersonOberserver,这样,当指定URI的数据发生改变时,PersonOberserver中的onChange方法就会得到调用。
我们为四个button绑定了各自的监听事件,在监听事件中,我们使用了mContentResolver.xxxxx()方法来进行增删改查,它们的参数中都有一个URI参数,而这个URI参数就是找到指定ContentProvider的标识。有了这个URI,我们才能通过ContentResolver去执行指定ContentProvider中的方法,并得到相应的数据。

另一个app的演示结果
查:
ContentProvider与sqlit的搭配使用_第2张图片

增:
ContentProvider与sqlit的搭配使用_第3张图片
ContentProvider与sqlit的搭配使用_第4张图片

你可能感兴趣的:(Android)