編輯:關於android開發
本文系原創博客,文中不妥煩請指出,如需轉載摘要請注明出處!
Alpha Dog
2016-04-13 10:27:06
首先,項目的地址:https://github.com/DarkPointK/MyContentProvider.git。
網上對於Android的Content Provider框架以及SQLite這款輕量級的嵌入式數據庫的介紹有很多,我不再復述,在此我將著重於如何實現對數據庫的操作。
工作之余開始系統性的自主學習,在閱讀了網絡上很多大牛的各類技術文章教程後,想試著寫一篇屬於自己的博客,以記錄一些心得成果還可給其他正在學習的朋友一點幫助,而對於文中存在的不妥之處還煩請指出。
這是一個布局很簡單的例子,主要內容在於後台對一個SQLite表的增刪改查一系列操作。





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


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

在繼承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在這裡調用到的那些增刪改查方法的參數列表有必要在這裡細說:
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 。
至此,一個自定義的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
Android應用開發教程之二十六:列表項添加進度框進階
今天有時間就學習了下在ListView、GridView列表項中清加ProgressBar,本文用最簡單的代碼實現可以通用的功能,人人都能看懂,哈哈
Fragment配合RadioGroup實現點擊切換布局,fragmentradiogroup
Fragment配合RadioGroup實現點擊切換布局,fragmentradiogroup 這裡用了 compile com.jakewharton:butter
Android項目:手機安全衛士(16)—— 復雜 ListView淺析
Android項目:手機安全衛士(16)—— 復雜 ListView淺析 Android項目:手機安全衛士(16)—— 復雜 ListView 1
Android自定義控件來襲(Scroller)
Android自定義控件來襲(Scroller) 先看看效果圖 實現方法繼承自ViewGroup需要我們自己來測量,布局,實現滑動的效果,處理滑動沖突, 自定義Vi