Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> Android開發 >> 關於android開發 >> (試筆)一、Android四大框架之ContentProvider的學習與運用,實現SQLite的增刪改查。,安卓sqlite增刪改查

(試筆)一、Android四大框架之ContentProvider的學習與運用,實現SQLite的增刪改查。,安卓sqlite增刪改查

編輯:關於android開發

(試筆)一、Android四大框架之ContentProvider的學習與運用,實現SQLite的增刪改查。,安卓sqlite增刪改查


本文系原創博客,文中不妥煩請指出,如需轉載摘要請注明出處!

 

ContentProvider的學習與運用

Alpha Dog

2016-04-13  10:27:06

 

 

  首先,項目的地址:https://github.com/DarkPointK/MyContentProvider.git。

  網上對於Android的Content Provider框架以及SQLite這款輕量級的嵌入式數據庫的介紹有很多,我不再復述,在此我將著重於如何實現對數據庫的操作。

  工作之余開始系統性的自主學習,在閱讀了網絡上很多大牛的各類技術文章教程後,想試著寫一篇屬於自己的博客,以記錄一些心得成果還可給其他正在學習的朋友一點幫助,而對於文中存在的不妥之處還煩請指出。

  這是一個布局很簡單的例子,主要內容在於後台對一個SQLite表的增刪改查一系列操作。

 

 

  • “增”:進入應用時“刪”“改”按鈕不可用,在ET中輸入想要添加的字符串後點擊“增”彈出新增數據的URI(關於URI的介紹可以通過這片不錯的文章了解:http://www.cnblogs.com/linjiqin/archive/2011/05/28/2061396.html);
  • “查”:會根據ET中輸入的文字在表中搜索並將搜索到的行的_id顯示在TV上,此時“刪”“改”按鈕變成可用;
  • “刪”:被點擊後也將使這兩按鈕不可用,並刪除TV中顯示的ID的行,在下次點擊“查”並查得數據時按鈕又被啟用;
  • “改”:當查詢到數據並在TV中顯示ID時可以在ET中輸入新的字符串,點擊按鈕即可改變相應ID行的數據為此字符串並會刷新搜索隊列。

  而數據庫的表在被創建後會存在於/data/data/package_name/databases文件夾中,如有需要在windows環境下查看表可以利用AS的Device Monitor導出:

 

  我們建立一個自己的項目在AS中,在這個項目中我們至多需要3個類,分別為:繼承自SQLiteOpenHelper的類用於直接對數據庫操作,繼承自ContentProvider的類會被用於決定以何種方式操作數據庫,然後就是我們的Activity。

  1.1繼承自SQLiteOpenHelper的StringDataBase類:

  在繼承SQLiteOpenHelper後我們需要重寫它的onCreate()方法和onUpgrade()方法並且實現構造方法。當StringDataBase被初始化時,其構造方法將提交數據庫名及其版本號給父類的方法得到處理。首次打開數據庫onCreate()方法會被調用將會處理SQL_CREATE字串中的SQL語句,創建數據庫得到一個“string”表。當版本號得到提升將調用onUpgrade()方法。

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;

import java.sql.SQLException;

/**
 * Created by Alpha Dog on 2016/4/8.
 */
public class StringDataBase extends SQLiteOpenHelper {

    private static final String DATABASE_NAME = "StringDatabase";
    private static final String TABLE_NAME = "string";
    private static final String KEY_NAME = "str";

    private static final String SQL_CREATE =
            "CREATE TABLE IF NOT EXISTS " + TABLE_NAME + "(_id INTEGER PRIMARY KEY AUTOINCREMENT, " + KEY_NAME + " TEXT)";
    private static final String SQL_DROP = "DROP TABLE IS EXISTS" + TABLE_NAME;

    public StringDataBase(Context context) {
        super(context, DATABASE_NAME, null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        db.execSQL(SQL_CREATE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        db.execSQL(SQL_DROP);
        onCreate(db);
    }

}

 

  重寫完這兩個方法後,開始著手完善將會被StringProvider類用到的所有對數據庫增刪改查操作的方法。

  在這一階段,你可能會發現這裡對數據庫的操作都是通過以getWritableDataBase()方法獲得一個可以操作數據庫的SQLiteDatabase實例,且有讀寫權(此外,有getReadableDatabase()方法可獲得只讀權實例)。在獲得SQLiteDatabase的實例後,調用相應的方法並將返回的結果作為返回值,當在StringProvider類中得到這些返回值時,可以進行下一步的處理。

public long addString(ContentValues values) throws SQLException {

        long id = getWritableDatabase().insert(TABLE_NAME, "", values);
        if (id <= 0) {
            throw new SQLException("Failed to add String");
        }

        return id;
    }

    public int deleteString(String id) {
        if (id == null) {
            return getWritableDatabase().delete(TABLE_NAME, null, null);
        } else {
            return getWritableDatabase().delete(TABLE_NAME, "_id=?", new String[]{id});
        }

    }

    public int updateString( ContentValues values,String id) {
        return getWritableDatabase().update(TABLE_NAME,values,"_id=?", new String[]{id});
    }

    public Cursor getString(String[] projection, String selection, String[] selectionArgs, String sortOrder) {

        SQLiteQueryBuilder sqb = new SQLiteQueryBuilder();
        sqb.setTables(TABLE_NAME);

        if (sortOrder == null || sortOrder == "") {
            sortOrder = "_id";
        }

        return sqb.query(getReadableDatabase(), projection, selection, selectionArgs, null, null, sortOrder);
    }

 

   對於SQLiteDatabase及SQLiteQueryBuilder在這裡調用到的那些增刪改查方法的參數列表有必要在這裡細說:

    • getWritableDatabase().insert (String table, String nullColumnHack, ContentValues values)  第一個參數為要操作的表名;第三個參數類似於需要插入的Map,鍵為列名,值為值;第二個參數不為空時,當values的值為空將會在指定列安全的插入一個null值;插入失敗返回-1。
    • getWritableDatabase().delete (String table, String whereClause, String[] whereArgs);  第二個參數是String的語句用於作為SQL的where語句部分,如果null將刪除所有行;第三個參數是在whereClause中含有?號時將其取代並綁定;刪除成功返回受影響的行數目,否則返回0。
    • getWritableDatabase().update (String table, ContentValues values, String whereClause, String[] whereArgs); 參考以上。update方法將會選擇表,查詢行,更新列,返回受影響的行數目; 
    • SQLiteQueryBuilder.query (SQLiteDatabase db, String[] projectionIn, String selection, String[] selectionArgs, String groupBy, String having, String sortOrder);  有了以上的解釋,根據query的參數名及這個SDK的API文檔鏈接了解它們的作用  http://www.android-doc.com/reference/android/database/sqlite/SQLiteQueryBuilder.html#query%28android.database.sqlite.SQLiteDatabase,%20java.lang.String[],%20java.lang.String,%20java.lang.String[],%20java.lang.String,%20java.lang.String,%20java.lang.String%29

  1.2繼承自ContentProvider的StringProvider類:

  ContentProvider需要重寫的方法,這些方法將在獲取ContentProvider的實例後可以被利用URI調用:

  由於需要URI連接的原因,我們需要在AndroidManifest.xml中注冊這個Provider:

<provider
            android:authorities="com.alphadog.mycontentprovider"
            android:name="com.alphadog.mycontentprovider.StringProvider"/>
    </application>

 

 

  其中authorities中的字段將作為URI中關鍵的一部分,用於請求Provider權限:  content://com.alphadog.mycontentprovider/

  name中的參數指定的是這個Provider具體提供服務的類。

  好了,注冊完Provider之後,現在來看看這個StringProvider裡究竟要寫些什麼:

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.net.Uri;

/**
 * Created by Alpha Dog on 2016/4/8.
 */
public class StringProvider extends ContentProvider {

    private static final String PROVIDER_NAME = "com.alphadog.mycontentprovider";
    private static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/string");
    private static final int STR = 1;
    private static final int STRS = 2;
    private StringDataBase sdb = null;

    private static final UriMatcher um = getUriMatcher();

    private static UriMatcher getUriMatcher() {
        UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        uriMatcher.addURI(PROVIDER_NAME, "string/#", STR);
        uriMatcher.addURI(PROVIDER_NAME, "string", STRS);
        return uriMatcher;
    }

    //初始化StringDatabase
    @Override
    public boolean onCreate() {
        Context context = getContext();
        sdb = new StringDataBase(context);
        return true;
    }

    @Override
    public String getType(Uri uri) {
        switch (um.match(uri)) {
            case STR:
                return "vnd.android.cursor.item/vnd.com.alphadog.mycontentprovider.string";
            case STRS:
                return "vnd.android.cursor.dir/vnd.com.alphadog.mycontentprovider.string";
        }

        return "";
    }

    //增
    @Override
    public Uri insert(Uri uri, ContentValues values) {

        try {
            long id = sdb.addString(values);
            Uri returnUri = ContentUris.withAppendedId(CONTENT_URI, id);
            return returnUri;
        } catch (Exception e) {
            return null;
        }
    }

    //刪
    @Override
    public int delete(Uri uri, String selection, String[] selectionArgs) {


        return sdb.deleteString(selection);
    }

    //改
    @Override
    public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {

        return sdb.updateString( values,selection);
    }

    //查

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {


        return sdb.getString(projection,selection,selectionArgs,sortOrder);
    }
}

  這裡的代碼為便於理解也非常簡潔,onCreate()裡顯示對StringDataBase進行了初始化,這將導致連接或創建數據庫,而其他的增刪改查方法也是調用這個初始化過sdb裡的方法對數據庫進行相應的操作。其中,getType()方法可以被外部調用,它會分析傳入的URI字串並返回一個MIME字串,關於MIME的具體意義可以參考這篇博客:http://blog.csdn.net/harvic880925/article/details/44620851 。

 

  2.1 Activity中的調用

  至此,一個自定義的Provide就算大功告成了,剩下的便是對這個Provider的使用方法了,這是個令人激動的階段,對此可以參考下我寫這個Activity,然後根據你的需求進行優化改動。

import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import butterknife.ButterKnife;
import butterknife.InjectView;
import butterknife.OnClick;

import static android.widget.Toast.LENGTH_SHORT;

public class MainActivity extends AppCompatActivity {

    private static final String PROVIDER_NAME = "com.alphadog.mycontentprovider";
    private static final Uri CONTENT_URI = Uri.parse("content://" + PROVIDER_NAME + "/string");

    private Cursor c;
    private String s;
    private boolean fresh = true;
    private Uri seleUri;
    private int id;

    @InjectView(R.id.tv)
    TextView tv;
    @InjectView(R.id.et)
    EditText et;
    @InjectView(R.id.add)
    Button add;
    @InjectView(R.id.del)
    Button del;
    @InjectView(R.id.update)
    Button update;
    @InjectView(R.id.query)
    Button query;

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


        update.setEnabled(false);
        del.setEnabled(false);

        et.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                fresh = true;
            }

            @Override
            public void afterTextChanged(Editable s) {
            }
        });
    }

    @OnClick({R.id.add, R.id.del, R.id.update, R.id.query})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.add:
                add();
                break;
            case R.id.del:
                del();
                break;
            case R.id.update:
                update();
                break;
            case R.id.query:
                query();
                break;
        }
    }


    private void add() {
        ContentValues cv = new ContentValues();
        cv.put("str", et.getText().toString());
        Uri uri = getContentResolver().insert(CONTENT_URI, cv);
        if (uri != null) {
            Toast.makeText(getBaseContext(), uri.toString(), LENGTH_SHORT).show();
        }
    }

    private void del() {

//        條件只傳id,因為在StringDataBase中有完整處理where語句
        int uri = getContentResolver().delete(CONTENT_URI, "" + id, null);
        Toast.makeText(getBaseContext(), "刪除成功" + id, LENGTH_SHORT).show();

        update.setEnabled(false);
        del.setEnabled(false);
        fresh = true;
    }

    private void update() {
        ContentValues cv = new ContentValues();

        if (!c.getString(1).equals(et.getText().toString())) {
            cv.put("str", et.getText().toString());
            int uri = getContentResolver().update(CONTENT_URI, cv, "" + id, null);
            Log.i("修改id為" + id + "的值為", " " + et.getText().toString());
            Toast.makeText(getBaseContext(), "修改成功" + id+"為"+et.getText().toString(), LENGTH_SHORT).show();
            fresh = true;
        } else {
            Toast.makeText(this, "沒做任何更改!", LENGTH_SHORT).show();
            fresh = false;
        }
    }

    private void query() {
        s = et.getText().toString();
        if (fresh&& !s.equals("") ) {
            s = "str = '" + et.getText().toString() + "'";
            Log.i("正在搜索的s為", s);

            Cursor cursor = getContentResolver().query(CONTENT_URI, null, s, null, null);

            if (cursor == null || cursor.getCount() <= 0) {
                Toast.makeText(this, "然而什麼都沒搜到", LENGTH_SHORT).show();
                return;
            }
            c = cursor;
        }
        display();
    }

    private void display() {
        if (c != null && c.moveToNext()) {
            update.setEnabled(true);
            del.setEnabled(true);
            Log.i("c", c.getString(0) + "   " + c.getString(1));
            tv.setText(c.getString(0));
            id = c.getInt(0);
Toast.makeText(this, "正在查找:"+c.getString(1)+" = "+id, LENGTH_SHORT).show(); fresh = false; } else { tv.setText(""); update.setEnabled(false); del.setEnabled(false); Toast.makeText(this, "搜索結束,隊列將刷新並從頭開始", LENGTH_SHORT).show(); fresh = true; } } }

  這一塊的內容主要是描述“增刪改查”操作時邏輯上的判斷,本文的開頭已經具體描述。每個操作都是先得到一個ContentResolver實例然後以URI及必要參數傳入調用方法。需要注意的是在query()方法查詢到數據後返回的是cursor類型,取值的方法具體參考:http://www.cnblogs.com/TerryBlog/archive/2010/07/05/1771459.html

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved