Android 通过ContentProvider数据库更新UI

简介:

这篇用到的知识点有几个:SQLiteOpenHelper,Cursor,CursorAdapter,ContentProvider,ContentObserver

对于数据库的操控一般都是用SQLiteOpenHelper,创建该类实例,可以得到一个SQLiteDatabase,而实际上操作数据库用的还是这个。

(一)我们先看下SQLiteOpenHelper这个类的实例


package com.bvin.study.observer;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper{
    
    public DBHelper(Context context){
        super(context,"cache",null,Config.DATA_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // TODO Auto-generated method stub
        db.execSQL("create table person(_id integer primary key autoincrement,name string,sex string,age integer)");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // TODO Auto-generated method stub
        
    }

    
    
}


这个类主要负责数据库的创建和版本的更新管理。
一般情况下,是可以过去这个类的实例也就是SQLiteDatabase实例去做增删查改的工作。SQLiteDatabase已经封装了对数据库的各种操作方法,而且还提供了一个db.execSQL(sql, bindArgs)方法来执行原生sql语句,有人说这种方式比它封装的方法更高效。但是execSQL()是不返回值的,下面看下那些个方法都返回些什么值。

SQLiteDatabase

1.public long insert (String table, String nullColumnHack, ContentValues values)

这里是向一个表插入数据,values有把要插入的数据和表中的字段对应起来,返回的是插入到表中该行所在得行id。

2.public int update (String table, ContentValues values, String whereClause, String[] whereArgs)

这个方法后面的是按照什么样的条件去更新符合条件的记录,返回更新的记录所在行。

3.public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, StringorderBy, String limit)

返回查询到的结果集

4.public int delete (String table, String whereClause, String[] whereArgs)

返回删除的哪行

而查询可以 用异步查询AsyncQueryHandler ,其实这个类也可以执行增删查该,分别override每个方法来做出响应处理

(二)ContentProvider

这是android四大组件之一,不需要手动实例化,只要在Androidmanifest.xml里面把ContentProvider注册上去就行。

 <provider android:name=".MyProvide" android:authorities="com.bvin.study.observer.MyProvide"/>


android:name即指定类名,android:authorities即绑定唯一的标识符,而这个一定要和URI的一样。

ContentProvider的uri形式跟域名很像:content://authorities//path,这个uri是非常重要的东西。

path可以表示某张表再后面可以指定某一行的某个字段,像/person/18/name就表示person表中第18个人的name

ui叫统一资源定位符,这样就唯一标识了这个资源,别的应用程序就可以用你的数据了,android系统本身就提供了大量的provider如短信,联系人等

 

好,uri讲完了就轮到ContentProvider了。

ContentProvider一般不主动构造,它跟Activity一样提供了一个入口方法。光有ContentProvider不行啊,所谓巧妇难为无米之炊啊。所以米在哪里呢,米就用我们做好的DBHelper。DBHelper获取数据库SQLiteDatabase,这个才是真正的数据库。再ContentProvider里覆盖操作数据库的各种方法,然后用SQLiteDatabase去执行各个操作。

 看看ContentProvider内部的实现

package com.bvin.study.observer;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;

public class MyProvide extends ContentProvider{
    
    DBHelper dbHelper;

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        
        long rowId = -1;
        rowId =dbHelper.getWritableDatabase().insert(Config.TABLE_NAME, null, values);
        Uri cururi = Uri.withAppendedPath(Config.CONTENT_URI, rowId+"");
        this.getContext().getContentResolver().notifyChange(uri,null);
        return cururi;
    }

    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        dbHelper = new DBHelper(getContext());
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    
}

这里仅用了插入这个方法,但外部是怎么调用的呢?用到是个叫ContentResolver,这个类可以通过Context上下文获取,ContentResolver也有insert(),update(),query().delete()方法,但ContentResolver的方法是暴露在外面的方法,真正执行的是ContetnProvider。ContentResolver就像一辆车它会前进,后退,转弯,刹车,而ContetnProvider就是司机,外面看到的车在转弯在刹车都是司机在操作,而这些功能司机有吗?没有司机不能代表车转弯刹车,这些核心的功能是汽车方向盘和刹车的功能,所以SQLiteDatabase就是方向盘咯,这个比喻可能不是很恰当。

 (三)ContentResolver

前面的都是功能接口服务的提供,接下来我们看如何去调用。这里用了一个ListActivity来调用以上定义的服务和数据可视化,主界面是个Listview,对数据库的操作用菜单来作用,四个菜单分别是增删查改。

@Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // TODO Auto-generated method stub
        
        menu.add(0, 0, 0, "添加");
        menu.add(0, 1, 1, "查询");
        menu.add(0, 2, 2, "更改");
        menu.add(0, 3, 3, "删除");
        
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // TODO Auto-generated method stub
        
        switch(item.getItemId()){
        
        case 0:
            ContentValues cv = new ContentValues();
            cv.put("name", "楚凌");
            getContentResolver().insert(Config.CONTENT_URI, cv);
            
            break;
        case 1:
            
            break;
        case 2:
            dbHelper.getWritableDatabase().execSQL("update "+Config.TABLE_NAME+" set name = 'chuling' where _id = 2");
            adapter.notifyDataSetChanged();
            break;
        case 3:
            dbHelper.getWritableDatabase().delete(Config.TABLE_NAME, null, null);
            break;
        
        }
        
        return super.onOptionsItemSelected(item);
    }

如果按以上代码,执行插入操作listview是不能及时先出来的,重启程序之后就可以看到新加的项。那要怎样才能插入一个就显示一个也就是更新数据及时刷新UI界面,因为ListView绑定的是CursorAdapter而这个cursor是充数据库查询出来的,所以要做的事情有两个。一个是数据源更新然后就是适配器去通知ui数据已改变,cursor就要重新查询一遍,或者adapter更换游标,然后再调用adapter.notifyDataSetChanged();但是cursor.requery()执行会影响效率,而光靠adapter.notifyDataSetChanged()界面是不会更新的,本来想用ContentObserver来实现,最后还是感觉不适合,有没有数据库变动及时更新Listview的良策???

DBHelper dbHelper  = null;
    SimpleCursorAdapter adapter;
    Cursor cursor;
    Handler handler;
    MyObserver observer;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        dbHelper = new DBHelper(this);
        handler = new Handler(){

            @Override
            public void handleMessage(Message msg) {
                // TODO Auto-generated method stub
                super.handleMessage(msg);
                if(msg.what==2013){
                    if(cursor!=null&&!cursor.isClosed()&&adapter!=null){
                        adapter.notifyDataSetChanged();
                    }
                }
            }
            
        };
       
        
        cursor = dbHelper.getReadableDatabase().rawQuery("select * from "+Config.TABLE_NAME, null);
        adapter = new SimpleCursorAdapter(this,android.R.layout.simple_list_item_1,cursor, new String[]{"name"}, new int[]{android.R.id.text1});
        setListAdapter(adapter);
        
        observer = new MyObserver(this,handler,cursor);
        getContentResolver().registerContentObserver(Config.CONTENT_URI, false, observer);
        
    }

其实这里看到Config.CONTENT_URI,其实这个就是指向的数据库的路径,就是牵着风筝的一条线,牵着线的手是ContentResolver,风筝当然就是ContetnProvider咯!
这里没看cursor.requery()哈,其实这个我已经放在ContentObserver里了。上面还有个handler,这个handler是用来注册ContentObserver的,这里将会处理观察者发出的消息。

(四) ContentObserver

这个类的构造方法必须有个Handler的参数来进行通信,还有个必须的就是必须覆盖onChanged()方法,用handler去发送message跟Activity去通信。

package com.bvin.study.observer;

import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

public class MyObserver extends ContentObserver{

    private Handler handler;
    private Cursor cursor;
    private Context context;
    public MyObserver(Context context,Handler handler,Cursor cursor){
        super(handler);
        this.context = context;
        this.handler = handler;
        this.cursor = cursor;
        
    }
    
    @Override
    public void onChange(boolean selfChange) {
        // TODO Auto-generated method stub
        super.onChange(selfChange);
        
        cursor.requery();
        handler.sendEmptyMessage(2013);
    }

    
    
}

我这里把cursor传进去,目的就是注册的URI指向的数据库更新时,就在onChanged()方法里再查询一边,然后Activity里的Handler处理消息的方法里用Adapter通知一下,界面就会及时更新了。
但是用这个去更新Listview也不太现实,handler里处理一下其他的ui元素还是可以的,比如插入数据,就蹦出个对话框“数据插入完毕”。。。

下面还是发下全部的代码,貌似还不可以传附件。。

主界面:MainActivity.java

package com.bvin.study.observer;

import android.app.ListActivity;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.SimpleCursorAdapter;

public class MainActivity extends ListActivity {
    /** Called when the activity is first created. */
    
    DBHelper dbHelper  = null;
    SimpleCursorAdapter adapter;
    Cursor cursor;
    Handler handler;
    MyObserver observer;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.main);
        dbHelper = new DBHelper(this);
        handler = new Handler(){

            @Override
            public void handleMessage(Message msg) {
                // TODO Auto-generated method stub
                super.handleMessage(msg);
                if(msg.what==2013){
                    if(cursor!=null&&!cursor.isClosed()&&adapter!=null){
                        adapter.notifyDataSetChanged();
                    }
                }
            }
            
        };
       
        
        cursor = dbHelper.getReadableDatabase().rawQuery("select * from "+Config.TABLE_NAME, null);
        adapter = new SimpleCursorAdapter(this,android.R.layout.simple_list_item_1,cursor, new String[]{"name"}, new int[]{android.R.id.text1});
        setListAdapter(adapter);
        
        observer = new MyObserver(this,handler,cursor);
        getContentResolver().registerContentObserver(Config.CONTENT_URI, false, observer);
        
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // TODO Auto-generated method stub
        
        menu.add(0, 0, 0, "添加");
        menu.add(0, 1, 1, "查询");
        menu.add(0, 2, 2, "更改");
        menu.add(0, 3, 3, "删除");
        
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // TODO Auto-generated method stub
        
        switch(item.getItemId()){
        
        case 0:
            ContentValues cv = new ContentValues();
            cv.put("name", "楚凌");
            getContentResolver().insert(Config.CONTENT_URI, cv);
            
            break;
        case 1:
            
            break;
        case 2:
            dbHelper.getWritableDatabase().execSQL("update "+Config.TABLE_NAME+" set name = 'chuling' where _id = 2");
            adapter.notifyDataSetChanged();
            break;
        case 3:
            dbHelper.getWritableDatabase().delete(Config.TABLE_NAME, null, null);
            break;
        
        }
        
        return super.onOptionsItemSelected(item);
    }

    @Override
    protected void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        if(dbHelper!=null){
            dbHelper.close();
        }
         getContentResolver().unregisterContentObserver(observer);
    }
    
}

MyProvider.java

package com.bvin.study.observer;

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;

public class MyProvide extends ContentProvider{
    
    DBHelper dbHelper;

    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    @Override
    public String getType(Uri uri) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public Uri insert(Uri uri, ContentValues values) {
        // TODO Auto-generated method stub
        
        long rowId = -1;
        rowId =dbHelper.getWritableDatabase().insert(Config.TABLE_NAME, null, values);
        Uri cururi = Uri.withAppendedPath(Config.CONTENT_URI, rowId+"");
        this.getContext().getContentResolver().notifyChange(uri,null);
        return cururi;
    }

    @Override
    public boolean onCreate() {
        // TODO Auto-generated method stub
        dbHelper = new DBHelper(getContext());
        return true;
    }

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        // TODO Auto-generated method stub
        return 0;
    }

    
}

DBHelper.java

package com.bvin.study.observer;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DBHelper extends SQLiteOpenHelper{
    
    public DBHelper(Context context){
        super(context,"cache",null,Config.DATA_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        // TODO Auto-generated method stub
        db.execSQL("create table person(_id integer primary key autoincrement,name string,sex string,age integer)");
        
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // TODO Auto-generated method stub
        
    }

    
    
}

MyObserver.java

package com.bvin.study.observer;

import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;

public class MyObserver extends ContentObserver{

    private Handler handler;
    private Cursor cursor;
    private Context context;
    public MyObserver(Context context,Handler handler,Cursor cursor){
        super(handler);
        this.context = context;
        this.handler = handler;
        this.cursor = cursor;
        
    }
    
    @Override
    public void onChange(boolean selfChange) {
        // TODO Auto-generated method stub
        super.onChange(selfChange);
        
        cursor.requery();
        handler.sendEmptyMessage(2013);
    }

    
    
}

Config.java

package com.bvin.study.observer;

import android.net.Uri;

public class Config {

    public static final String AOTHORITY = "com.bvin.study.observer.MyProvide";
    public static final String TABLE_NAME = "person";
    public static final Uri CONTENT_URI = Uri.parse("content://"+AOTHORITY+"/"+TABLE_NAME);
    public static final int DATA_VERSION =1;
}

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.bvin.study.observer"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <provider android:name=".MyProvide" android:authorities="com.bvin.study.observer.MyProvide"/>
    </application>

</manifest>



相关文章
|
22天前
|
消息中间件 安全 数据处理
Android为什么不能在子线程更新UI
Android为什么不能在子线程更新UI
25 0
|
1月前
|
网络协议 关系型数据库 MySQL
如何实现无公网ip远程访问本地安卓Termux部署的MySQL数据库【内网穿透】
如何实现无公网ip远程访问本地安卓Termux部署的MySQL数据库【内网穿透】
|
3月前
|
存储 数据库连接 数据库
Android数据存储:解释SQLite数据库在Android中的使用。
Android数据存储:解释SQLite数据库在Android中的使用。
41 0
|
11天前
|
编解码 Android开发 UED
安卓UI/UX设计原则:打造引人入胜的用户体验
【4月更文挑战第13天】本文探讨了安卓UI/UX设计的关键原则,包括一致性、简洁性、反馈、清晰性、效率和适应性。一致性要求视觉和行为保持一致,利用系统UI;简洁性减少用户行动,简化导航;反馈需即时且明确;清晰性强调表达清晰,布局有序;效率关注性能优化和任务简化;适应性涉及多设备适配和用户多样性。遵循这些原则,可创建出色应用,提供无缝用户体验。设计应持续迭代,适应技术发展和用户需求。
|
15天前
|
XML 移动开发 Android开发
构建高效安卓应用:采用Jetpack Compose实现动态UI
【4月更文挑战第10天】 在现代移动开发中,用户界面的流畅性和响应性对于应用的成功至关重要。随着技术的不断进步,安卓开发者寻求更加高效和简洁的方式来构建动态且吸引人的UI。本文将深入探讨Jetpack Compose这一革新性技术,它通过声明式编程模型简化了UI构建过程,并提升了性能与跨平台开发的可行性。我们将从基本概念出发,逐步解析如何利用Jetpack Compose来创建具有数据动态绑定能力的安卓应用,同时确保应用的高性能和良好用户体验。
15 0
|
16天前
|
XML Java Android开发
Android之UI基础控件
Android之UI基础控件
|
17天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。
|
1月前
|
XML API Android开发
【Android 从入门到出门】第三章:使用Hilt处理Jetpack Compose UI状态
【Android 从入门到出门】第三章:使用Hilt处理Jetpack Compose UI状态
26 4
|
1月前
|
存储 XML 编译器
【Android 从入门到出门】第二章:使用声明式UI创建屏幕并探索组合原则
【Android 从入门到出门】第二章:使用声明式UI创建屏幕并探索组合原则
48 3
|
1月前
|
存储 SQL 数据库
【Android 从入门到出门】第六章:使用Room数据库并测试
【Android 从入门到出门】第六章:使用Room数据库并测试
29 4