A. android手機上sqllite插入數據的性能是多少
SQLite 因其小巧輕便被安卓系統廣泛採用,當然在操作小數據量時,差異並不明顯;但當 SQLite 在操作略大一點的數據時就顯得力不存心了,這時的 CRUD 操作對移動存儲設備的性能有著極大的要求,另外用戶體驗的良好性也對 SQLite 的性能優化提出了要求。那麼,當我們在操作大數據量時如何對 SQLite 進行優化呢?正確的操作是:開啟事務。下面我們通過採用不同的方式向資料庫中插入 10000 條數據來進行比較以體現開啟事務對 SQLite 性能提升方面所做出的貢獻。首先看一張截圖來進行一個感性的認識:
源碼及安裝文件下載方式一:SQLiteDataBase.zip
從上圖中我們會很清晰的看到通過普通方式插入 10000 條數據和開啟事務插入 10000 條數據之間的差異,整整差了 83 秒。下面我們來看測試代碼:
package cn.sunzn.sqlitedatabase;
import android.app.Activity;
import android.content.ContentValues;
import android.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.View;
import android.widget.EditText;
public class MainActivity extends Activity {
protected static final int SUCCESS_INSERT_TO_DB_ONE = 1;
protected static final int SUCCESS_INSERT_TO_DB_TWO = 2;
private EditText et_usedtime1;
private EditText et_usedtime2;
Handler handler = new Handler() {
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what) {
case SUCCESS_INSERT_TO_DB_ONE:
Integer usetime_one = (Integer) msg.obj;
et_usedtime1.setText("插入10000條數據耗時:" + usetime_one / 1000 + "秒");
break;
case SUCCESS_INSERT_TO_DB_TWO:
Integer usetime_two = (Integer) msg.obj;
et_usedtime2.setText("插入10000條數據耗時:" + usetime_two / 1000 + "秒");
break;
default:
break;
}
}
};
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
et_usedtime1 = (EditText) findViewById(R.id.et_usedtime1);
et_usedtime2 = (EditText) findViewById(R.id.et_usedtime2);
}
/**
* 1. 普通方式插入資料庫 10000 條數據
*/
public void insert1(View view) {
MySQLiteOpenHelper openHelper = new MySQLiteOpenHelper(getApplicationContext());
final SQLiteDatabase database = openHelper.getWritableDatabase();
if (database.isOpen()) {
new Thread() {
public void run() {
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
ContentValues values = new ContentValues();
values.put("name", "tom:" + i);
database.insert("person", "_id", values);
}
database.close();
long end = System.currentTimeMillis();
int usetime_one = (int) (end - start);
Message message = new Message();
message.what = SUCCESS_INSERT_TO_DB_ONE;
message.obj = usetime_one;
handler.sendMessage(message);
};
}.start();
}
}
/**
* 2. 開啟事務插入資料庫 10000 條數據
*/
public void insert2(View view) {
MySQLiteOpenHelper openHelper = new MySQLiteOpenHelper(getApplicationContext());
final SQLiteDatabase database = openHelper.getWritableDatabase();
if (database.isOpen()) {
new Thread() {
public void run() {
long start = System.currentTimeMillis();
database.beginTransaction();
for (int i = 0; i < 10000; i++) {
ContentValues values = new ContentValues();
values.put("name", "tom:" + i);
database.insert("person", "_id", values);
}
database.setTransactionSuccessful();
database.endTransaction();
database.close();
long end = System.currentTimeMillis();
int usetime_two = (int) (end - start);
Message message = new Message();
message.what = SUCCESS_INSERT_TO_DB_TWO;
message.obj = usetime_two;
handler.sendMessage(message);
};
}.start();
}
}
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
為什麼只是開啟了一個事務就會有這么大的差距呢?很簡單,SQLite 預設為每個操作開啟了一個事務,那麼測試代碼循環插入 10000 次開啟了 10000 個事務,"事務開啟 + SQL 執行 + 事務關閉" 自然耗費了大量的時間,這也是後面顯式開啟事務後為什麼如此快的原因。
B. 怎麼提高android contentresolver的查詢效率優化
兩種辦法:
1.創建自己的ContentProvider,需要繼承ContentProvider類
2.如果你的數據和已存在的ContentProvider數據結構一致,可以將數據寫到已存在的ContentProvider中
當然前提是獲取寫該ContentProvider的許可權.比如把OA中的成員通訊信息加入到系統的聯系人ContentProvider中
ContentProvider基礎
所有ContentProvider都需要實現相同的介面,用於查詢ContentProvider並返回數據.也包括增加、修改和刪除數據.
步驟:
1.獲得一個ContentResolver的實例,可通過Activity的成員方法getContentResovler()方法:
ContentResolver cr = this.getContentResolver();
ContentResolver實例帶的方法可實現找到指定的ContentProvider並獲取到ContentProvider的數據
ContentResolver的查詢過程開始,Android系統將確定查詢所需的具體ContentProvider,確認它是否啟動並運行它.
android系統負責初始化所有的ContentProvider,不需要用戶自己去創建.實際上,ContentProvider的用戶都不可能直接訪問到ContentProvider實例,只能通過ContentResolver在中間代理.
2.數據模型
ContentProvider展示數據類似一個單個資料庫表.
其中:
每行有個帶唯一值的數字欄位,名為_ID,可用於對表中指定記錄的定位.
ContentProvider返回的數據結構,是類似JDBC的ResultSet,在android中,是Cursor對象.
URI,每個ContentProvider定義一個唯一的公開的URI,用於指定到它的數據集.
一個ContentProvider可以包含多個數據集(可以看作多張表),這樣,就需要有多個URI與每個數據集對應.
這些URI要以這樣的格式開頭:
content://
表示這個URI指定一個ContentProvider.
如果你想創建自己的ContentProvider,最好把自定義的URI設置為類的常量,這樣簡化別人的調用,並且以後如果更新URI也很容易.
android定義了CONTENT_URI常量用於URI,如:android.provider.Contacts.Phones.CONTENT_URI
2.查詢ContentProvider
要想使用一個ContentProvider,需要以下信息:
定義這個ContentProvider的URI,返回結果的欄位名稱,這些欄位的數據類型
如果需要查詢ContentProvider數據集的特定記錄(行),還需要知道該記錄的ID的值.
構建查詢
查詢就是輸入URI等參數,其中URI是必須的,其他是可選的,如果系統能找到URI對應的ContentProvider將返回一個Cursor對象.
可以通過ContentResolver.query()或者Activity.managedQuery()方法.
兩者的方法參數完全一樣,查詢過程和返回值也是相同的.
區別是,通過Activity.managedQuery()方法,不但獲取到Cursor對象,而且能夠管理Cursor對象的生命周期.
比如當Activity暫停(pause)的時候,卸載該Cursor對象,當Activity Restart的時候重新查詢.另外,也可以對一個沒有處於Activity管理的Cursor對象做成被Activity管理的,通過調用Activity.startManaginCursor()方法.
類似這樣:
Cursor cur = managedQuery(myPerson,null,null,null,null);
其中第一個參數myPerson是Uri類型實例.
如果需要查詢的是指定行的記錄,需要用_ID值,比如ID值為23,URI將是類似:
content://....../23
android提供了方便的方法,讓開發者不需要自己拼接上面這樣的URI,比如類似:
Uri myPerson = ContentUris.withAppendedId(People.CONTENT_URI,23);
或者:
Uri myPerson = Uri.withAppendedPath(People.CONTENT_URI,"23");
二者的區別是一個接收整數類型的ID值,一個接收字元串類型.
其他幾個參數:
names,可以為null,表示取數據集的全部列,或者聲明一個String數組,數組中存放列名稱,比如:People._ID.一般列名都在該ContentProvider中有常量對應;
針對返回結果的過濾器,格式類似於SQL中的WHERE子句,區別是不帶WHERE關鍵字,如果返回null表示不過濾,比如name=?;
前面過濾器的參數,是String數組,是針對前面條件中?佔位符的值;
排序參數,類似SQL的ORDER BY字句,不過不需要寫ORDER BY部分,比如name desc,如果不排序,可輸入null.
返回值是Cursor對象,游標位置在第一條記錄之前.
下面實例適用於android 2.0及以上版本,從android通訊錄中得到姓名欄位:
java代碼:
Cursor cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,null,null,null);
讀取返回的數據
如果在查詢的時候使用到ID,那麼返回的數據只有一條記錄.在其他情況下,一般會有多條記錄.和JDBC的ResultSet類似,需要操作游標遍歷結果集,在每行,再通過列名獲取到列的值,可以通過getString()、getInt()、getFloat()等方法獲取值.
比如類似下面:
java代碼:
while(cursor.moveToNext()) {
builder.append(cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME))).append("-");
}
和JDBC中不同,沒有直接通過列名獲取列值的方法,只能先列名獲取到列的整型索引值,然後再通過該索引值定位獲取列的值.
編輯數據
可以通過ContentProvider實現以下編輯功能:
增加新的記錄:
在已經存在的記錄中增加新的值、批量更新已經存在的多個記錄、刪除記錄.
所有的編輯功能都是通過ContentResolver的方法實現.一些ContentProvider對許可權要求更嚴格一些,需要寫的許可權,如果沒有會報錯.
增加記錄
要想增加記錄到ContentProvider,首先,要在ContentValues對象中設置類似map的鍵值對,在這里,鍵的值對應ContentProvider中的列的名字,鍵值對的值,是對應列希望的類型.
然後,調用ContentResolver.insert()方法,傳入這個ContentValues對象,和對應ContentProvider的URI即可.返回值是這個新記錄的URI對象.這樣你可以通過這個URI獲得包含這條記錄的Cursor對象.
比如:
java代碼:
ContentValues values = new ContentValues();
values.put(People.NAME,"Abraham Lincoln");
Uri uri = getContentResolver().insert(People.CONTENT_URI, values);
在原有記錄上增加值
如果記錄已經存在,可在記錄上增加新的值,或者編輯已經存在的值.
首先要找到原來的值對象,然後要清除原有的值,然後像上面增加記錄一樣即可:
java代碼:
Uri uri = Uri.withAppendedPath(People.CONTENT_URI, "23");
Uri phoneUri = Uri.withAppendedPath(uri, People.Phones.CONTENT_DIRECTORY);
values.clear();
values.put(People.Phones.TYPE, People.Phones.TYPE_MOBILE);
values.put(People.Phones.NUMBER, "1233214567");
getContentResolver().insert(phoneUri, values);
批量更新值
批量更新一組記錄的值,比如NY改名為Eew York.可調用ContentResolver.update()方法.
刪除記錄
如果是刪除單個記錄,調用ContentResolver.delete()方法,URI參數,指定到具體行即可.
如果是刪除多個記錄,調用ContentResolver.delete()方法,URI參數指定Contentprovider即可,並帶一個類似SQL的WHERE子句條件.這里和上面類似,不帶WHERE關鍵字.
創建自己的ContentProvider
創建contentprovider,需要設置存儲系統.大多數ContentProvider使用文件或者SQLite資料庫,不過你可以用任何方式存儲數據.android提供SQLiteOpenHelper幫助開發者創建和管理SQLiteDatabase.
繼承ContentProvider,提供對數據的訪問.在manifest文件中聲明ContentProvider.繼承ContentProvider類
必須定義ContentProvider類的子類,需要實現如下方法:
java代碼:
query()
insert()
update()
delete()
getType()
onCreate()
在實現子類的時候,還有一些步驟可以簡化ContentProvider客戶端的使用:
定義public static final Uri常量,名稱為CONTENT_URI:
java代碼:
public static final Uri CONTENT_URI = Uri.parse("content://com.example.codelab.transportationprovider");
如果有多個表,它們也是使用相同的CONTENT_URI,只是它們的路徑部分不同.
聲明ContentProvider
創建ContentProvider後,需要在manifest文件中聲明,android系統才能知道它,當其他應用需要調用該ContentProvider時才能創建或者調用它.
語法類似:
<provider android:name="com.easymorse.cp.MyContentProvider"
android:authorities="com.easymorse.cp.mycp">
</provider>
android:name要寫ContentProvider繼承類的全名.
android:authorities要寫和CONTENT_URI常量的B部分