【073】Android 数据存储(SQLite)
目录:
I. 使用 SharedPreferences 来存储应用程序的数据
V. Content Provider 简介
I. 使用 SharedPreferences 来存储应用程序的数据:
参考:Beginning Android 4 Application Development - Page 252
- 将数据存储到 SharedPreferences 对象中去,这个时候要用到 SharedPreferences.Editor 中的方法。
- 上面的过程会将数据存储在一个 xml 文件中。
在 DDMS 视图中找到 File Explore, 选择 data/data/<PackageName>/shared_prefs/*.xml
如下图所示:
- 通过 getSharedPreferences() 方法获取 SharedPreferences 对象,并将里面的数据取出。
举例效果:
代码功能:通过"修改文本"按钮可以将文本内容存储, 然后点击"显示文本"按钮就可以显示出刚才修改了的文本.
另外:在其他的 activity 中也可以直接的调用, 调用方法与下面的一样~
1 public class MainActivity extends Activity { 2 3 @Override 4 public void onCreate(Bundle savedInstanceState) { 5 super.onCreate(savedInstanceState); 6 setContentView(R.layout.activity_main); 7 8 Button displayButton = (Button)findViewById(R.id.button1); 9 displayButton.setOnClickListener(new OnClickListener() { 10 11 public void onClick(View v) { 12 // TODO Auto-generated method stub 13 SharedPreferences appPreferences = getSharedPreferences( 14 "com.example.preferenceexer_preferences", MODE_PRIVATE); 15 Toast.makeText(MainActivity.this, appPreferences.getString("editTextPref", ""), Toast.LENGTH_SHORT).show(); 16 } 17 }); 18 19 Button modifyButton = (Button)findViewById(R.id.button2); 20 modifyButton.setOnClickListener(new OnClickListener() { 21 22 public void onClick(View v) { 23 // TODO Auto-generated method stub 24 SharedPreferences appPreferences = getSharedPreferences( 25 "com.example.preferenceexer_preferences", MODE_PRIVATE); 26 SharedPreferences.Editor prefsEditor = appPreferences.edit(); 27 prefsEditor.putString("editTextPref", ((EditText)findViewById(R.id.editText1)).getText().toString()); 28 prefsEditor.commit(); 29 } 30 }); 31 } 32 }
SharedPreferences 接口:
1. Interface for accessing and modifying preference data returned by getSharedPreferences(String, int).
getSharedPreferences(String, int):返回值:SharedPreferences
第一个参数:就是存储文件的名称, 一般以 <PackageName>_preferences 的格式, 其他应该也是可以的
第二个参数:0 代表 MODE_PRIVATE, 只允许此程序使用. 还有其他的常量.
2. SharedPreferences Methods:
- edit():返回值:SharedPreferences.Editor. 进入编辑模式.
- contains(String key):检查是否存在指定的键.
- getAll():返回值:Map<String, ?>. 从 preferences 中取回所有的值.
- getBoolean(String key, boolean defValue):取回布尔值, 第二个为参数默认值.
- getFloat(String key, float defValue):
- getInt(String key, int defValue):
- getLong(String key, long defValue):
- getString(String key, String defValue):取回字符串, 第二个参数为默认值.
- getStringSet(String key, Set<String> defValues):
SharedPreferences.Editor 接口:
1. 用来修改 SharedPreferences 对象的值。
2. SharedPreferences.Editor Methods:
- commit():提交修改的内容.
- apply():同上.
- remove(String key):删除指定的键.
- clear():全部清空.
- putBoolean(String key, boolean value):加入布尔值.
- putFloat(String key, float value):
- putInt(String key, int value):
- putLong(String key, long value):
- putString(String key, String value):加入字符串.
- putStringSet(String key, Set<String> values):
II. 将应用程序的数据存储到内部存储器中:
参考:Beginning Android 4 Application Development - Page 263
效果图:
在 DDMS 视图中找到 File Explore, 选择 data/data/<PackageName>/files/textfile.txt
如下图所示:
public class FilesActivity extends Activity { EditText textBox; static final int READ_BLOCK_SIZE = 100; //字符数组 @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textBox = (EditText) findViewById(R.id.txtText1); } public void onClickSave(View view) { String str = textBox.getText().toString(); try { FileOutputStream fOut = openFileOutput("textfile.txt", MODE_WORLD_READABLE); //文件输出流 OutputStreamWriter osw = new OutputStreamWriter(fOut); osw.write(str); osw.close(); Toast.makeText(getBaseContext(), "File saved successfully!", Toast.LENGTH_SHORT).show(); textBox.setText(""); } catch (IOException ioe) { ioe.printStackTrace(); } } public void onClickLoad(View view) { try { FileInputStream fIn = openFileInput("textfile.txt"); //文件写入流 InputStreamReader isr = new InputStreamReader(fIn); char[] inputBuffer = new char[READ_BLOCK_SIZE]; String s = ""; int charRead; while ((charRead = isr.read(inputBuffer))>0) { String readString = String.copyValueOf(inputBuffer, 0, charRead); s += readString; inputBuffer = new char[READ_BLOCK_SIZE]; } textBox.setText(s); Toast.makeText(getBaseContext(), "File loaded successfully!", Toast.LENGTH_SHORT).show(); } catch (IOException ioe) { ioe.printStackTrace(); } } }
源文件下载:Files.zip
III. 将应用程序的数据存储到外部存储器中:
参考:Beginning Android 4 Application Development - Page 268
效果图:
在 DDMS 视图中找到 File Explore, 选择 mnt/sdcard/MyFiles/textfile.txt
如下图所示:
public class FilesActivity extends Activity { EditText textBox; static final int READ_BLOCK_SIZE = 100; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); textBox = (EditText) findViewById(R.id.txtText1); } public void onClickSave(View view) { String str = textBox.getText().toString(); try { //---SD Card Storage--- File sdCard = Environment.getExternalStorageDirectory(); File directory = new File (sdCard, "MyFiles"); directory.mkdirs(); File file = new File(directory, "textfile.txt"); FileOutputStream fOut = new FileOutputStream(file); FileWriter fileWriter = new FileWriter(file); BufferedWriter bufferedWriter = new BufferedWriter(fileWriter); bufferedWriter.write(str); bufferedWriter.close(); Toast.makeText(getBaseContext(), "File saved successfully!", Toast.LENGTH_SHORT).show(); textBox.setText(""); } catch (IOException ioe) { ioe.printStackTrace(); } } public void onClickLoad(View view) { try { //---SD Storage--- File sdCard = Environment.getExternalStorageDirectory(); File directory = new File (sdCard.getAbsolutePath() + "/MyFiles"); File file = new File(directory, "textfile.txt"); FileInputStream fIn = new FileInputStream(file); InputStreamReader isr = new InputStreamReader(fIn); FileReader fileReader = new FileReader(file); BufferedReader bufferedReader = new BufferedReader(fileReader); char[] inputBuffer = new char[100]; String strSum = ""; int charRead; while ((charRead = bufferedReader.read(inputBuffer)) > 0) { strSum += new String(inputBuffer); } bufferedReader.close(); textBox.setText(strSum); Toast.makeText(getBaseContext(), "File loaded successfully!", Toast.LENGTH_SHORT).show(); } catch (IOException ioe) { ioe.printStackTrace(); } } }
源文件下载:Files.zip
IV. 通过 SQLite 数据库来实现数据存储:
参考:Beginning Android 4 Application Development - Page 273
※ 参考:Android之SQlite存储
☀-☀ → SQLiteDatabase 类:
1. 显示了一些用来管理 SQLite 数据的方法。
java.lang.Object
↳ android.database.sqlite.SQLiteClosable
↳ android.database.sqlite.SQLiteDatabase
2. 方法:
- execSQL(String sql):执行没有返回结果的 SQL 语句.
- insert(String table, String nullColumnHack, ContentValues values):插入数据. 返回 ID 值(long).
第一个参数:欲插入行的表的名称.
第二个参数:直接 null.
第三个参数:包括了初始化行值的 map. key 对应列名称, value 对应列的值.
ContentValues:通过 ContentValues 的 put() 方法可以键值对添加进去. - delete(String table, String whereClause, String[] whereArgs):删除数据. 返回值表示删除的个数(int).
第二个参数:WHERE 语句内容, null 将删除所有列.
第三个参数:null. - update(String table, ContentValues values, String whereClause, String[] whereArgs):更新数据. 返回值为受影响的行数(int).
- rawQuery(String sql, String[] selectionArgs):返回值:Cursor.
第一个参数:SQL 查询语句, 语句结尾要有分号";"结束.
- query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy):查询, 返回值:Cursor.
第一个参数:欲查询的表名称.
第二个参数:想要得到返回值的列的 list. 传递 null 则返回所有列.
第三个参数:类似一个 SQL WHERE 语句(不包括关键字, 下面一样), null 则返回所有的行.
第四个参数:用来替换文本的, 就给个 null 好了.
第五个参数:SQL GROUP BY 语句, null 则不排序.
第六个参数:SQL HAVING 语句, 不用就 null.
第七个参数:SQL ORDER BY 语句, 不用就 null. - query(String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit):
第八个参数:LIMIT 语句. 限制返回查询的行的个数. - query(boolean distinct, String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy, String limit):
第一个参数:true 则返回的每一行是惟一的, 否则为 false.
☀-☀ →Cursor 接口:
1. 数据库查询指针.
若想通过 Cursor 来遍历所有数据, 首先要调用 moveToFirst() 方法, 因为指针不一定知道最上面的位置, 否则会出现数据遗漏, 同时也可以使用 moveToLast() 方法, 然后往上移动~
当调用 moveToFirst() 方法的时候, 显示此时 getPosition() 返回值为 0, 也就是第一条记录;
当调用 moveToLast() 方法的时候, 显示此时 getPosition() 返回值为 7, 也就是最后一条记录.
android.database.Cursor
2. 方法:
- getColumnCount():获取所有列数.
- getColumnIndex(String columnName):通过列名称查找列索引.
- getColumnName(int columnIndex):
- getColumnNames():返回值:String[]. 返回所有列名的数组.
- getCount():返回所有的行数.
- getPosition():返回当前位于此行集合中的位置.
- getExtras():返回值:Bundle.
- getDouble(int columnIndex):
- getFloat(int columnIndex):
- getInt(int columnIndex):
- getLong(int columnIndex):
- getShort(int columnIndex):
- getString(int columnIndex):
- getType(int columnIndex):
- isBeforeFirst():返回是否现在指针指在第一行之前.
- isAfterLast():返回是否现在指针指在最后一行之后.
- isFirst():返回指针是否指在第一行.
- isLast():返回指针是否指在最后一行.
- isNull(int columnIndex):返回指定列是否为空.
- isClosed():返回此指针是否关闭.
- move(int offset):指针从当前位置向前或向后移动一定的行数.
- moveToFirst():将指针移动到第一行.
- moveToLast():将指针移动到最后一行.
- moveToPrevious():将指针向前移动一行.
- moveToNext():将指针向后移动一行.
- moveToPosition(int position):将指针移动到指定的行位置.
☀-☀ → SQLiteOpenHelper 类:
1. 用于数据库创建和版本管理的帮助类.
java.lang.Object
↳ android.database.sqlite.SQLiteOpenHelper
2. 方法:
- close():关闭任何打开的数据库对象.
- getDatabaseName():返回正在运行的 SQLite 数据库的名称.
- getReadableDatabase():返回值:SQLiteDatabase. 创建 和/或 打开一个数据库.
- getWritableDatabase():返回值:SQLiteDatabase. 创建 和/或 打开一个数据库, 这个数据库将被读和写.
- onOpen(SQLiteDatabase db):当数据库运行的时候执行.
- onCreate(SQLiteDatabase db):当数据库第一次创建的时候执行.
- onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion):当数据库需要升级的时候执行.
数据库被默认存储在 data/data/<Package_Name>/databases 文件夹内部:
MyDB 中显示的内容如下:
第一步:新建一个 DBAdapter 类, 写入操作数据库的各种方法.
public class DBAdapter { static final String KEY_ROWID = "_id"; static final String KEY_NAME = "name"; static final String KEY_EMAIL = "email"; static final String TAG = "DBAdapter"; static final String DATABASE_NAME = "MyDB"; static final String DATABASE_TABLE = "contacts"; static final int DATABASE_VERSION = 2; static final String DATABASE_CREATE = "create table contacts (_id integer primary key autoincrement, " + "name text not null, email text not null);"; final Context context; DatabaseHelper DBHelper; SQLiteDatabase db; public DBAdapter(Context ctx) { this.context = ctx; DBHelper = new DatabaseHelper(context); } private static class DatabaseHelper extends SQLiteOpenHelper { DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); //新建数据库文件 } @Override public void onCreate(SQLiteDatabase db) { try { db.execSQL(DATABASE_CREATE); //创建数据库表 } catch (SQLException e) { e.printStackTrace(); } } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.w(TAG, "Upgrading database from version " + oldVersion + " to " + newVersion + ", which will destroy all old data"); db.execSQL("DROP TABLE IF EXISTS contacts"); onCreate(db); } } //---opens the database--- public DBAdapter open() throws SQLException { db = DBHelper.getWritableDatabase(); //为 SQLiteDatabase 赋值. return this; } //---closes the database--- public void close() { DBHelper.close(); } //---insert a contact into the database--- public long insertContact(String name, String email) { ContentValues initialValues = new ContentValues(); initialValues.put(KEY_NAME, name); initialValues.put(KEY_EMAIL, email); return db.insert(DATABASE_TABLE, null, initialValues); } //---deletes a particular contact--- public boolean deleteContact(long rowId) { return db.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0; } //---retrieves all the contacts--- public Cursor getAllContacts() { return db.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME, KEY_EMAIL}, null, null, null, null, null); } //---retrieves a particular contact--- public Cursor getContact(long rowId) throws SQLException { Cursor mCursor = db.query(true, DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME, KEY_EMAIL}, KEY_ROWID + "=" + rowId, null, null, null, null, null); if (mCursor != null) { mCursor.moveToFirst(); } return mCursor; } //---updates a contact--- public boolean updateContact(long rowId, String name, String email) { ContentValues args = new ContentValues(); args.put(KEY_NAME, name); args.put(KEY_EMAIL, email); return db.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0; } }
第二步:在 activity 中调用.
public class DatabasesActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); DBAdapter db = new DBAdapter(this);
//下面实现文件从 assets 文件夹中赋值到指定的程序目录中去. try { String destPath = "/data/data/" + getPackageName() + "/databases"; File f = new File(destPath); if (!f.exists()) { f.mkdirs(); f.createNewFile(); //---copy the db from the assets folder into // the databases folder--- CopyDB(getBaseContext().getAssets().open("mydb"), new FileOutputStream(destPath + "/MyDB")); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //---get all contacts--- db.open(); Cursor c = db.getAllContacts(); if (c.moveToFirst()) { do { DisplayContact(c); } while (c.moveToNext()); } db.close(); } public void CopyDB(InputStream inputStream, OutputStream outputStream) throws IOException { //---copy 1K bytes at a time--- byte[] buffer = new byte[1024]; int length; while ((length = inputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length); } inputStream.close(); outputStream.close(); } public void DisplayContact(Cursor c) { Toast.makeText(this, "id: " + c.getString(0) + "\n" + "Name: " + c.getString(1) + "\n" + "Email: " + c.getString(2), Toast.LENGTH_LONG).show(); } }
源文件下载: Databases.zip
下面展示一些常用操作的实现代码:
INSERT - 插入数据
public long insertContact(String name, String email) { ContentValues initialValues = new ContentValues(); initialValues.put(KEY_NAME, name); initialValues.put(KEY_EMAIL, email); return db.insert(DATABASE_TABLE, null, initialValues); }
//---add a contact--- db.open(); long id = db.insertContact("Wei-Meng Lee", "weimenglee@learn2develop.net"); id = db.insertContact("Mary Jackson", "mary@jackson.com"); db.close();
SELECT - 选择语句
public Cursor getAllContacts() { return db.query(DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME, KEY_EMAIL}, null, null, null, null, null); //null 为返回全部. }
//--get all contacts--- db.open(); Cursor c = db.getAllContacts(); if (c.moveToFirst()) { do { DisplayContact(c); } while (c.moveToNext()); } db.close();
SELECT - 选择语句 & WHERE - 筛选语句
public Cursor getContact(long rowId) throws SQLException { Cursor mCursor = db.query(true, DATABASE_TABLE, new String[] {KEY_ROWID, KEY_NAME, KEY_EMAIL}, KEY_ROWID + "=" + rowId, null, //此处为 where 语句的内容. null, null, null, null); if (mCursor != null) { mCursor.moveToFirst(); } return mCursor; }
//---get a contact--- db.open(); Cursor c = db.getContact(2); if (c.moveToFirst()) DisplayContact(c); else Toast.makeText(this, "No contact found", Toast.LENGTH_LONG).show(); db.close();
UPDATE - 更新语句
public boolean updateContact(long rowId, String name, String email) { ContentValues args = new ContentValues(); args.put(KEY_NAME, name); args.put(KEY_EMAIL, email); return db.update(DATABASE_TABLE, args, KEY_ROWID + "=" + rowId, null) > 0; }
//---update contact--- db.open(); if (db.updateContact(1, "Wei-Meng Lee", "weimenglee@gmail.com")) Toast.makeText(this, "Update successful.", Toast.LENGTH_LONG).show(); else Toast.makeText(this, "Update failed.", Toast.LENGTH_LONG).show(); db.close();
DELETE - 删除语句
public boolean deleteContact(long rowId) { return db.delete(DATABASE_TABLE, KEY_ROWID + "=" + rowId, null) > 0; //null 为删除全部. }
//---delete a contact--- db.open(); if (db.deleteContact(1)) Toast.makeText(this, "Delete successful.", Toast.LENGTH_LONG).show(); else Toast.makeText(this, "Delete failed.", Toast.LENGTH_LONG).show(); db.close();
查询指定名称的个数.
public int queryStudentBySid(String sid) { Cursor cursor = SQLiteDB.query(DATABASE_TABLE_NAME, null, KEY_SID + "=" + sid, null, null, null, null); return cursor.getCount(); }
ORDER BY - 排序语句 (DESC & ASC)
public Cursor ascendStudents() { Cursor cursor = SQLiteDB.query( DATABASE_TABLE_NAME, new String[]{KEY_ID, KEY_SID, KEY_NAME, KEY_GENDER, KEY_HOMETOWN}, null, null, null, null, KEY_SID); if (cursor != null) { cursor.moveToFirst(); } return cursor; }
public Cursor descendStudents() { Cursor cursor = SQLiteDB.query( DATABASE_TABLE_NAME, new String[]{KEY_ID, KEY_SID, KEY_NAME, KEY_GENDER, KEY_HOMETOWN}, null, null, null, null, KEY_SID + " DESC"); //order by 语句内容. if (cursor != null) { cursor.moveToFirst(); } return cursor; }
V. Content Provider 使用简介:
参考:Beginning Android 4 Application Development - Page 293
1. Content Provider 主要是用来获取其他程序的信息,例如可以取得通讯录中的内容等。
public class McProvider extends ListActivity { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_mc_provider); Uri allContactsUri = Uri.parse("conten://contacts/people"); Cursor cursor; if (android.os.Build.VERSION.SDK_INT < 11) { cursor = managedQuery(allContactsUri, null, null, null, null); } else { CursorLoader cursorLoader = new CursorLoader(this, allContactsUri, null, null, null, null); cursor = cursorLoader.loadInBackground(); } String[] columns = new String[] { ContactsContract.Contacts.DISPLAY_NAME, ContactsContract.Contacts._ID}; int[] views = new int[] {R.id.contactName,R.id.contactID}; SimpleCursorAdapter adapter; if (android.os.Build.VERSION.SDK_INT < 11) { adapter = new SimpleCursorAdapter(this, R.layout.activity_mc_provider, cursor, columns, views); } else { adapter = new SimpleCursorAdapter(this, R.layout.activity_mc_provider, cursor, columns, views, CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER); } this.setListAdapter(adapter); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· .NET10 - 预览版1新功能体验(一)