Android Sqite数据库 <7>

转换成xml可以看看网上的大神的杰作.

下面要解决一个问题,就是自己APP里面创建的数据和数据如何提供给其他APP使用,需要靠一个什么工具盒介质呢?

Android里面有四大组件,其中一种ContentProvider组件就能够做到,要ContentProvider组件提供的数据,那么就需要将ContentProvider在配置文件(AndroidManifest.xml)显示暴露出来.当然,Android的四大组件都需要在AndroidManifest.xml中注册,但是不一定暴露,要提供给其他APP使用,四大组件都需要设置暴露出来.

这里要开发两个APP,一个APP提供ContentProvider,另外一个使用共享的数据库,开发步骤如下:

<1> :根据前面的数据库基本使用,这里仍然需要使用SqliteOpenHelper来进行数据库操作,ContentProvider虽然重载了update(...).delete(...)等方法,但是在具体实现这些方法时,还是需要借助SqliteOpenHelper来操作数据库.,所以综述上面的,首先还是需要新建一个继承SqliteOpenHelper的类,另外为了方便管理数据库字段,一般先定义一个单独类专门管理数据库名,table名,字段名,Uri,等等:

package hx.android.test;

import android.net.Uri;
import android.provider.BaseColumns;

public class Mytable{
    public static class MyColumns implements BaseColumns {/*
         * BaseColumns 是一个接口,里边有两个变量,一个是_ID=“_id”,一个是_COUNT="_ count" 。
         * 在Android当中,每一个数据库表至少有一个字段,而且这个字段是_id。
         * 所以当我们构造列名的辅助类时,直接实现BaseColumns ,这样我们便默认地拥有了_id字段。
         * 如果没有继承BaseColum类,就要自己定义变量_id
         */
        private MyColumns() {}
        public static final String AUTHORITY = "hx.android.test.mycontentprovider";
        public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/mytable");
        
        
        public static final String CONTENT_TYPE = "vnd.Android.cursor.dir/vnd.hx.mytable";
        public static final String CONTENT_ITEM_TYPE = "vnd.Android.cursor.item/vnd.hx.mytable";
        public static final String DEFAULT_SORT_ORDER = "created DESC";
        public static final String TABLE_NAME = "mytable";
        public static final int VERSION = 1;
        
        public static final String TITLE = "title";
        public static final String BODY = "body";
        public static final String NAME = "name";
}
    

}
View Code

然后新建一个继承SqliteOpenHelper的类:

package hx.android.test;

import hx.android.test.Mytable.MyColumns;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseHelper extends SQLiteOpenHelper {
    public static String DATABASE_NAME = "mydatabase2.db";
    public static int version = 1;
    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, version);
        // TODO Auto-generated constructor stub
    }
    
    @Override  
    public void onOpen(SQLiteDatabase db) {  
        super.onOpen(db);  
        //这是当打开数据库时的回调函数,一般也不会用到。
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        // 在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表
        // SQL语句
//        String sql = "CREATE TABLE " + MyColumns.TABLE_NAME + " (" +MyColumns._ID +"int not null,"+ MyColumns.TITLE
//        + " text not null, " + MyColumns.BODY + " text not null," +MyColumns.NAME+"text not null"+ ");";
        String s = "CREATE TABLE \"mytable\"( [_id] int PRIMARY KEY ,[title] varchar(100) ,[body] varchar(10) ,[name] varchar(100) ) ";
        //执行这条SQL语句
        db.execSQL(s);

        
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 当数据库需要升级的时候,Android系统会主动的调用这个方法。一般我们在这个方法里边删除数据表,并建立新的数据表,
        db.execSQL("DROP TABLE IF EXISTS notes");
        onCreate(db);

    }  

}
View Code

<2> 新建一个继承ContentProvider的类,然后在这个类中的各个重载方法中操作前面继承SqliteOpenHelper来操作数据库:

package hx.android.test;

import java.util.HashMap;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

/**
 * ContentProvider是什么时候创建的,是谁创建的?访问某个应用程序共享的数据,是否需要启动这个应用程序?
 * 这个问题在 Android SDK中没有明确说明,但是从数据共享的角度出发,ContentProvider应该是Android在系统启动时就创建了,否则就谈不上数据共享了。 
 * 这就要求在AndroidManifest.XML中使用元素明确定义。
 *  可能会有多个程序同时通过ContentResolver访问一个ContentProvider,会不会导致像数据库那样的“脏数 据”?
 *  这个问题一方面需要数据库访问的同步,尤其是数据写入的同步,在AndroidManifest.XML中定义ContentProvider的时 候,需要考虑是元素multiprocess属性的值;
 *  另外一方面Android在ContentResolver中提供了notifyChange() 接口,在数据改变时会通知其他ContentObserver,这个地方应该使用了观察者模式,
 *  在ContentResolver中应该有一些类似 register,unregister的接口。 
 *  至此,已经对ContentProvider提供了比较全面的分析,至于如何创建ContentProvider,
 *  可通过2种方法:创建一个属于你自己的 ContentProvider或者将你的数据添加到一个已经存在的ContentProvider中,当然前提是有相同数据类型并且有写入 Content provider的权限。
 * 
 * */
public class MyContentProvider extends ContentProvider {
    
    private static final String TABLE_NAME = "mytable";
    private static final int TABLES = 1;
    private static final int TABLE_ID = 2;
    private static final UriMatcher sUriMatcher ;
    static{//注册需要匹配的Uri
        /**
         *     什么是URI?
            将其分为A,B,C,D 4个部分:
            A:标准前缀,用来说明一个Content Provider控制这些数据,无法改变的;"content://"
            B:URI的标识,它定义了是哪个Content Provider提供这些数据。对于第三方应用程序,为了保证URI标识的唯一性,它必须是一个完整的、小写的 类名。这个标识在 元素的 authorities属性中说明:一般是定义该ContentProvider的包.类的名称 ;"content://hx.android.text.myprovider"
            C:路径,不知道是不是路径,通俗的讲就是你要操作的数据库中表的名字,或者你也可以自己定义,记得在使用的时候保持一致就ok了;"content://hx.android.text.myprovider/tablename"
            D:如果URI中包含表示需要获取的记录的ID;则就返回该id对应的数据,如果没有ID,就表示返回全部; "content://hx.android.text.myprovider/tablename/#" #表示数据id
         * 
         * 
         * 
         *  UriMatcher:用于匹配Uri,它的用法如下:
            1.首先把你需要匹配Uri路径全部给注册上,如下: 
            //常量UriMatcher.NO_MATCH表示不匹配任何路径的返回码(-1)。
             *  UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH); //如果match()方法匹配
             *  content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回匹配码为1 
             *  uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact”, 1); //添加需要匹配uri,如果匹配就会返回匹配码 //如果match()方法匹配 
             *  content://com.changcheng.sqlite.provider.contactprovider/contact/230路径,返回匹配码为2 
             *  uriMatcher.addURI(“com.changcheng.sqlite.provider.contactprovider”, “contact/#”, 2);//#号为通配符
            2.注册完需要匹配的Uri后,就可以使用uriMatcher.match(uri)方法对输入的Uri进行匹配,如果匹配就返回匹配码,匹配码是调用addURI()方法传入的第三个参数,假设匹配content://com.changcheng.sqlite.provider.contactprovider/contact路径,返回的匹配码为1。
         * 
         * 
         * */
        sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        sUriMatcher.addURI(Mytable.MyColumns.AUTHORITY, TABLE_NAME, TABLES);
        sUriMatcher.addURI(Mytable.MyColumns.AUTHORITY, TABLE_NAME+"/#", TABLE_ID);
    
    };


    @Override
    public int delete(Uri uri, String arg1, String[] arg2) {
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
         
        return db.delete(TABLE_NAME, arg1, arg2);

    }
     
    @Override
    public String getType(Uri uri) {
        switch (sUriMatcher.match(uri)) {
            case TABLES:
            return Mytable.MyColumns.CONTENT_TYPE;
            case TABLE_ID:
            return Mytable.MyColumns.CONTENT_ITEM_TYPE;
            default:
            throw new IllegalArgumentException("Unknown URI " + uri);
        }

    }

    int i = 1;
    @Override
    public Uri insert(Uri uri, ContentValues initialValues) {
        // TODO Auto-generated method stub
        if (sUriMatcher.match(uri) != TABLES) {
            throw new IllegalArgumentException("Unknown URI " + uri);
        }
        ContentValues values;
        if (initialValues != null) {
            Log.i("##not null", "initialValues");
            values = new ContentValues(initialValues);
        } 
        else {
            Log.i("##null", "initialValues");
            values = new ContentValues();
        }
        
        if (values.containsKey(Mytable.MyColumns._ID) == false) {
            values.put(Mytable.MyColumns._ID, i);
            i++;
        }
        if (values.containsKey(Mytable.MyColumns.TITLE) == false) {
            values.put(Mytable.MyColumns.TITLE, "title"+i);
        }
        if (values.containsKey(Mytable.MyColumns.BODY) == false) {
            values.put(Mytable.MyColumns.BODY,"body"+i);
        }
        if (values.containsKey(Mytable.MyColumns.NAME) == false) {
        values.put(Mytable.MyColumns.NAME, "name"+i);
        }
        
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        long rowId = db.insert(TABLE_NAME, Mytable.MyColumns.BODY, values);
        if (rowId > 0) {
            /**
             * 下面会用到辅助类ContentUris 的两个实用方法
             * withAppendedId(uri, id)用于为路径加上ID部分
             * parseId(uri)方法用于从路径中获取ID部分
             */
            Uri myUri= ContentUris.withAppendedId(Mytable.MyColumns.CONTENT_URI, rowId);
            return myUri;//返回值为一个uri
        }
        throw new SQLException("Failed to insert row into " + uri);

    }

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

    @Override
    public Cursor query(Uri uri, String[] projection, String selection,
            String[] selectionArgs, String sortOrder) {
        SQLiteQueryBuilder qb = new SQLiteQueryBuilder();//是一个构造SQL查询语句的辅助类
        switch (sUriMatcher.match(uri)) {
            case TABLES:
                qb.setTables(TABLE_NAME);
                break;
            case TABLE_ID:
                qb.setTables(TABLE_NAME);
                qb.appendWhere(Mytable.MyColumns._ID + "="
                        + uri.getPathSegments().get(1));
                break;
            default:
                throw new IllegalArgumentException("Unknown URI " + uri);
        }
 
        SQLiteDatabase db = mOpenHelper.getReadableDatabase();
        Cursor c = qb.query(db, projection, selection, selectionArgs, null,
                null, sortOrder);
        return c;

    }

    @Override
    public int update(Uri uri, ContentValues values, String selection,
            String[] selectionArgs) {
        SQLiteDatabase db = mOpenHelper.getWritableDatabase();
        String rowId = uri.getPathSegments().get(1);
        return db.update(TABLE_NAME, values, Mytable.MyColumns._ID + "="
        + rowId, null);

    }

}
View Code

<3> 自己APP使用ContentProvider提供的共享数据:
所有ContentProvider提供的数据,在被自己APP还是其他APP使用时,都要用到ContentResolver,相当于进行数据库连接,然后使用它的update(...)等方法进行操作.

package hx.android.test;

import hx.android.test.Mytable.MyColumns;
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class DatabaseHelper extends SQLiteOpenHelper {
    public static String DATABASE_NAME = "mydatabase2.db";
    public static int version = 1;
    public DatabaseHelper(Context context) {
        super(context, DATABASE_NAME, null, version);
        // TODO Auto-generated constructor stub
    }
    
    @Override  
    public void onOpen(SQLiteDatabase db) {  
        super.onOpen(db);  
        //这是当打开数据库时的回调函数,一般也不会用到。
    }
    @Override
    public void onCreate(SQLiteDatabase db) {
        // 在数据库第一次生成的时候会调用这个方法,一般我们在这个方法里边生成数据库表
        // SQL语句
//        String sql = "CREATE TABLE " + MyColumns.TABLE_NAME + " (" +MyColumns._ID +"int not null,"+ MyColumns.TITLE
//        + " text not null, " + MyColumns.BODY + " text not null," +MyColumns.NAME+"text not null"+ ");";
        String s = "CREATE TABLE \"mytable\"( [_id] int PRIMARY KEY ,[title] varchar(100) ,[body] varchar(10) ,[name] varchar(100) ) ";
        //执行这条SQL语句
        db.execSQL(s);

        
    }
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        // 当数据库需要升级的时候,Android系统会主动的调用这个方法。一般我们在这个方法里边删除数据表,并建立新的数据表,
        db.execSQL("DROP TABLE IF EXISTS notes");
        onCreate(db);

    }  

}
View Code

<4> 在使用数据之前,需要现在AndroidManifest.xml先注册这个组件并且暴露:

<provider android:name="MyContentProvider" android:authorities="hx.android.test.mycontentprovider"></provider>

其中 : android:authorities="hx.android.test.mycontentprovider"是对外暴露的.
<5> 现在自己的APP可以使用了,那么在新建一个Android工程,再做另外一个APP来使用这个共享数据:

package com.example.providerutest;

import java.net.URI;

import android.net.Uri;
import android.os.Bundle;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.UriMatcher;
import android.database.Cursor;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";
    private Uri mUri;

    private Button mQueryBtn;
    private Button mQueryIdBtn;
    private Button mQuerySelBtn;

    private TextView mDataView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mQueryBtn = (Button) findViewById(R.id.query);
        mQueryBtn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                // content://+packagename+database tablename
                mUri = Uri
                        .parse("content://hx.android.test.mycontentprovider/mytable");
                String datalist = "";
                // 读取数据是用ContentResover
                ContentResolver cr = MainActivity.this.getContentResolver();
                Cursor cor = cr.query(mUri, null, null, null, null);
                cor.moveToFirst();
                while (!cor.isAfterLast()) {
                    Log.i(TAG,
                            "_id:" + cor.getInt(0) + " //title:"
                                    + cor.getString(1));
                    datalist = datalist + '\n' + "_id:" + cor.getInt(0)
                            + " //title:" + cor.getString(1);
                    cor.moveToNext();
                }

                mDataView.setText(datalist);
            }

        });

        mQueryIdBtn = (Button) findViewById(R.id.queryid);
        mQueryIdBtn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                // content://+packagename+database tablename
                // get the second data lists(only one list will be return)
                String datalist = "";

                mUri = Uri
                        .parse("content://hx.android.test.mycontentprovider/mytable/2");
                ContentResolver cr = MainActivity.this.getContentResolver();
                Cursor cor = cr.query(mUri, null, null, null, null);
                cor.moveToFirst();
                Log.i(TAG,
                        "_id:" + cor.getInt(0) + " //title:" + cor.getString(1));
                datalist = datalist + '\n' + "_id:" + cor.getInt(0)
                        + " //title:" + cor.getString(1);

                mDataView.setText(datalist);
            }

        });

        mQuerySelBtn = (Button) findViewById(R.id.querysel);
        mQuerySelBtn.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub

                String datalist = "";
                String selections = "_id=?";
                String[] conditions = new String[] { "" + 3 };
                mUri = Uri
                        .parse("content://hx.android.test.mycontentprovider/mytable");
                ContentResolver cr = MainActivity.this.getContentResolver();
                Cursor cor = cr.query(mUri, null, selections, conditions, null);
                cor.moveToFirst();
                // only one data list will be return

                Log.i(TAG,
                        "_id : " + cor.getInt(0) + " //title : "
                                + cor.getString(1));
                Toast.makeText(
                        MainActivity.this,
                        "_id : " + cor.getInt(0) + " //title : "
                                + cor.getString(1), Toast.LENGTH_SHORT).show();

                datalist = datalist + '\n' + "_id:" + cor.getInt(0)
                        + " //title:" + cor.getString(1);

                mDataView.setText(datalist);
            }

        });

        mDataView = (TextView) findViewById(R.id.dataview);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}
View Code

操作方法和上面第一个APP的操作方式差不多,只要知道Uri("content://packagename/tablename/#"),在利用ContentResolver就可以使用了.

另外在查询时经常需要到根据ID查询的条件,这个uri是如何产生呢,有两种方法:

<1> : 根据下面的uri去查询或者删除记录,将会操作数据库中id=ID的那条记录.

Uri uri=Uri.parse("content://packagename/tablename"+"/"+ID)

<2> : 下面可以再提供一个一般在程序中实时操作的,用的应该比上面的要多:
记住tablename后面没有"/",这个和第一种有区别.

Uri uri= ContentUris.withAppendedId("content://packagename/tablename", rowId);

好了,根据上面两个uri可以随便操作数据库任何一条记录了.

 

 

 

 

 

 

 

 

 

 

 

posted @ 2014-03-27 17:54  MMLoveMeMM  阅读(246)  评论(0)    收藏  举报