主要讲述了Android数据持久化的三种方式:文件存储、SharedPreference存储、SQLite数据库存储。
(一)文件存储
其实Android中文件存储方式和Java的文件操作类似,就是用IO流进行操作。文件存储只能保存简单的字符串或二进制数据,不适合保存结构较为复杂的数据。
1、示例程序(代码中有详细注释):
(1)xml文件:
其中有一个EditText,可以在里面输入字符,还有两个Button,一个用于保存输入的内容到一个文件中,另一个用于载入相应的文件内容到EditText中。
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical" > 5 6 <EditText 7 android:id="@+id/input_et" 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" /> 10 11 <Button 12 android:id="@+id/save_btn" 13 android:layout_width="match_parent" 14 android:layout_height="wrap_content" 15 android:text="保存" /> 16 17 18 <Button 19 android:id="@+id/load_btn" 20 android:layout_width="match_parent" 21 android:layout_height="wrap_content" 22 android:text="载入文件内容" /> 23 24 </LinearLayout>
(2)MainActivity:
1 import java.io.BufferedReader; 2 import java.io.BufferedWriter; 3 import java.io.FileInputStream; 4 import java.io.FileOutputStream; 5 import java.io.InputStreamReader; 6 import java.io.OutputStreamWriter; 7 8 import android.app.Activity; 9 import android.content.Context; 10 import android.os.Bundle; 11 import android.text.TextUtils; 12 import android.view.Menu; 13 import android.view.MenuItem; 14 import android.view.View; 15 import android.view.View.OnClickListener; 16 import android.widget.Button; 17 import android.widget.EditText; 18 import android.widget.Toast; 19 20 public class MainActivity extends Activity implements OnClickListener { 21 22 private String dataFileName = "MyDataFile"; 23 24 private EditText inputEt; 25 private Button saveBtn; 26 private Button loadBtn; 27 28 @Override 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.activity_main); 32 33 inputEt = (EditText) findViewById(R.id.input_et); 34 saveBtn = (Button) findViewById(R.id.save_btn); 35 loadBtn = (Button) findViewById(R.id.load_btn); 36 37 saveBtn.setOnClickListener(this); 38 loadBtn.setOnClickListener(this); 39 } 40 41 @Override 42 public void onClick(View v) { 43 switch (v.getId()) { 44 case R.id.save_btn: 45 String inputText = inputEt.getText().toString(); 46 if (inputText.length() == 0) { 47 Toast.makeText(MainActivity.this, "未输入任何内容!", 48 Toast.LENGTH_SHORT).show(); 49 } else { 50 save(dataFileName, inputText); 51 Toast.makeText(MainActivity.this, "保存成功!", Toast.LENGTH_SHORT) 52 .show(); 53 } 54 break; 55 56 case R.id.load_btn: 57 String fileContent = load(dataFileName); 58 59 // 用于判断一个字符串是否是null或者空内容的工具方法 60 if (!TextUtils.isEmpty(fileContent)) { 61 inputEt.setText(fileContent); 62 // 让光标移到文字最后 63 inputEt.setSelection(fileContent.length()); 64 Toast.makeText(this, "载入文件内容成功!", Toast.LENGTH_SHORT).show(); 65 } 66 break; 67 default: 68 break; 69 } 70 } 71 72 // 保存输入内容到文件的方法 73 public void save(String fileName, String inputText) { 74 FileOutputStream out = null; 75 BufferedWriter writer = null; 76 77 try { 78 // 1.用Context类的openFileOutput方法创建FileOutputStream实例 79 // MODE_PRIVATE模式是默认操作模式,表示当指定同样文件名时,所写入的内容将会覆盖原来的内容 80 // MODE_APPEND表示如果该文件已经存在就往文件里面追加内容,不存在就创建新文件 81 // 注:这里的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data/包名/files目录的 82 out = openFileOutput(fileName, Context.MODE_PRIVATE); 83 84 // 2.用FileOutputStream实例创建OutputStreamWriter实例,再用OutputStreamWriter实例创建BufferedWriter实例 85 writer = new BufferedWriter(new OutputStreamWriter(out)); 86 87 // 3.写入内容到文件 88 writer.write(inputText); 89 90 /** 91 * 注:查看该文件的方法: DDMS—>File 92 * Explorer—>/data/data/com.example.filepersistencetest(包名)/files/ 93 * 该目录下即可看到刚刚保存的文件,DDMS按钮下方有一个导出文件,即可把文件导出到电脑上用记事本查看 94 */ 95 96 } catch (Exception e) { 97 e.printStackTrace(); 98 } finally { 99 try { 100 if (writer != null) { 101 writer.close(); 102 } 103 } catch (Exception e) { 104 e.printStackTrace(); 105 } 106 } 107 } 108 109 // 载入并读取文件内容的方法 110 public String load(String fileName) { 111 FileInputStream in = null; 112 BufferedReader reader = null; 113 StringBuilder content = new StringBuilder(); 114 115 try { 116 // 1.用Context类的openFileInput方法创建FileInputStream实例 117 in = openFileInput(fileName); 118 119 if (in == null) { 120 Toast.makeText(MainActivity.this, "数据文件不存在!", 121 Toast.LENGTH_SHORT).show(); 122 return ""; 123 } 124 125 // 2.创建BufferedReader实例 126 reader = new BufferedReader(new InputStreamReader(in)); 127 128 String line = ""; 129 130 // 3.读取每一行 131 while ((line = reader.readLine()) != null) { 132 content.append(line); 133 } 134 } catch (Exception e) { 135 e.printStackTrace(); 136 } finally { 137 if (reader != null) { 138 try { 139 reader.close(); 140 } catch (Exception e) { 141 e.printStackTrace(); 142 } 143 } 144 } 145 146 return content.toString(); 147 } 148 149 @Override 150 protected void onDestroy() { 151 super.onDestroy(); 152 String inputText = inputEt.getText().toString(); 153 if (inputText.length() != 0) { 154 save(dataFileName, inputText); 155 } 156 } 157 158 }
2、用openFileOutput和openFileInput创建文件时,参数中的文件名不可以包含路径,因为所有的文件都是默认存储到/data/data/包名/files目录的。
3、查看该文件的方法: DDMS—>File Explorer—>/data/data/com.example.filepersistencetest(包名)/files/,该目录下即可看到刚刚保存的文件,DDMS按钮下方有一个导出文件按钮,即可把文件导出到电脑上用记事本查看。
(二)SharedPreferences
SharedPreferences用键值对形式存储数据,适合保存程序的一些偏好设置等。SharedPreferences文件会自动存放在/data/data/包名/shared_prefs目录下,是xml格式的文件。
1、获取SharedPreferences对象的三种方法:
(1)Activity类的getPreferences方法,只接收一个模式参数,这个方法会自动把当前活动类名作为SharedPreferences文件名。
(2)PreferencesManager类中的getDefaultSharedPreferences方法,这是一个静态方法,接收一个Context参数,并自动使用当前 应用程序的包名作为前缀来命名SharedPreferences文件。
(3)Context类的getSharedPreferences方法,接受两个参数,第一个为文件名字符串(不要带路径!),第二个是文件操作模式。
2、向SharedPreferences文件写入数据的步骤:
(1)用SharedPreferences对象的edit方法获取SharedPreferences.Editor实例:
1 SharedPreferences.Editor editor = getSharedPreferences("dataFile", 2 MODE_PRIVATE).edit();
(2)写入数据到SharedPreferences中:
1 editor.putString("name", "贾永基"); 2 editor.putInt("age", 23); 3 editor.putBoolean("married", false);
(3)用commit方法提交:
1 editor.commit();
3、从SharedPreferences文件读数据的步骤:
(1)创建SharedPreferences实例:
1 SharedPreferences pref = getSharedPreferences("dataFile", MODE_PRIVATE);
(2)用getXXX方法读取数据:
1 // 第一个参数表示键名,第二个参数表示如果找不到数据时候返回的默认值 2 String name = pref.getString("name", ""); 3 int age = pref.getInt("age", 0); 4 boolean married = pref.getBoolean("married", false); 5 6 Log.d("MainActivity", "name is " + name); 7 Log.d("MainActivity", "age is " + age); 8 Log.d("MainActivity", "married is " + married);
4、示例程序:
(1)XML文件:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical" > 5 6 <Button 7 android:id="@+id/save_data_btn" 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" 10 android:text="保存数据" /> 11 12 <Button 13 android:id="@+id/load_data_btn" 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" 16 android:text="载入数据" /> 17 18 </LinearLayout>
(2)MainActivity:
1 package com.example.sharedpreferencestest; 2 3 import android.app.Activity; 4 import android.content.SharedPreferences; 5 import android.os.Bundle; 6 import android.util.Log; 7 import android.view.Menu; 8 import android.view.MenuItem; 9 import android.view.View; 10 import android.view.View.OnClickListener; 11 import android.widget.Button; 12 import android.widget.Toast; 13 14 public class MainActivity extends Activity implements OnClickListener { 15 16 private Button saveDataBtn; 17 private Button loadDataBtn; 18 19 @Override 20 protected void onCreate(Bundle savedInstanceState) { 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.activity_main); 23 24 saveDataBtn = (Button) findViewById(R.id.save_data_btn); 25 loadDataBtn = (Button) findViewById(R.id.load_data_btn); 26 27 saveDataBtn.setOnClickListener(this); 28 loadDataBtn.setOnClickListener(this); 29 } 30 31 @Override 32 public void onClick(View v) { 33 switch (v.getId()) { 34 case R.id.save_data_btn: 35 saveData(); 36 Toast.makeText(MainActivity.this, "保存数据成功", Toast.LENGTH_SHORT) 37 .show(); 38 break; 39 case R.id.load_data_btn: 40 loadData(); 41 break; 42 default: 43 break; 44 } 45 } 46 47 // 保存数据到SharedPreferences 48 public void saveData() { 49 // 1.用SharedPreferences对象的edit方法获取SharedPreferences.Editor实例 50 SharedPreferences.Editor editor = getSharedPreferences("dataFile", 51 MODE_PRIVATE).edit(); 52 53 // 注:获取SharedPreferences对象还有两种方法: 54 // (1)Activity类的getPreferences方法,只接收一个模式参数,这个方法会自动把当前活动类名作为SharedPreferences文件名 55 // (2)PreferencesManager类中的getDefaultSharedPreferences方法,这是一个静态方法,接收一个Context参数,并自动使用当前 56 // 应用程序的包名作为前缀来命名SharedPreferences文件 57 58 // 2.写入数据到SharedPreferences中 59 editor.putString("name", "贾永基"); 60 editor.putInt("age", 23); 61 editor.putBoolean("married", false); 62 63 // 3.用commit方法提交 64 // 注:SharedPreferences文件会自动存放在/data/data/包名/shared_prefs目录下,是xml格式的文件 65 editor.commit(); 66 } 67 68 // 从SharedPreferences读数据 69 public void loadData() { 70 SharedPreferences pref = getSharedPreferences("dataFile", MODE_PRIVATE); 71 72 // 第一个参数表示键名,第二个参数表示如果找不到数据时候返回的默认值 73 String name = pref.getString("name", ""); 74 int age = pref.getInt("age", 0); 75 boolean married = pref.getBoolean("married", false); 76 77 Log.d("MainActivity", "name is " + name); 78 Log.d("MainActivity", "age is " + age); 79 Log.d("MainActivity", "married is " + married); 80 } 81 82 }
(三)SQLite
SQLite是Android内嵌的轻量级关系型数据库,速度很快,支持标准的SQL语法,还支持ACID事务。
1、创建数据库:
继承SQLiteOpenHelper类创建自己的类MyDatabaseHelper,并实现onCreate和onUpgrade两个抽象方法,在onCreate方法中建表,在onUpgrade中升级数据库。
1 import android.content.Context; 2 import android.database.sqlite.SQLiteDatabase; 3 import android.database.sqlite.SQLiteDatabase.CursorFactory; 4 import android.database.sqlite.SQLiteOpenHelper; 5 import android.widget.Toast; 6 7 public class MyDatabaseHelper extends SQLiteOpenHelper { 8 9 // 1.将建表语句定义成字符串常量 10 public static final String CREATE_BOOK = "create table book(" // 注:表名和字段名称不区分大小写 11 + "id integer primary key autoincrement," 12 + "author text," 13 + "price real," + "pages integer," + "name text,"+ "category_id integer)";; 14 15 public static final String CREATE_CATEGORY = "create table category(" 16 + "id integer primary key autoincrement," + "category_name text," 17 + "category_code integer)"; 18 19 private Context mContext; 20 21 public MyDatabaseHelper(Context context, String name, 22 CursorFactory factory, int version) { 23 super(context, name, factory, version); 24 mContext = context; 25 } 26 27 @Override 28 public void onCreate(SQLiteDatabase db) { 29 // 2.用SQLiteDatabase的execSQL方法执行建表语句 30 db.execSQL(CREATE_BOOK); 31 db.execSQL(CREATE_CATEGORY); 32 33 Toast.makeText(mContext, "创建数据库成功!", Toast.LENGTH_SHORT).show(); 34 } 35 36 @Override 37 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 38 switch (oldVersion) { 39 case 1: 40 db.execSQL(CREATE_CATEGORY); 41 case 2: // 注意这里case的最后是不写break的,以应对跨版本升级的情况 42 db.execSQL("alter table book add column category_id integer"); 43 default: 44 break; 45 } 46 } 47 48 }
2、创建数据库后,在adb shell中可以用命令行方式查看数据库的具体数据,方法如下:
(1)打开控制台窗口,输入adb shell,然后cd到路径:/data/data/当前包名/databases/,使用ls查看当前目录里的文件。
(2)假设提前创建的数据库名叫BookStore.db,那么接着就用sqlite3 BookStore.db命令打开数据库,然后可以输入各种SQL语句进行操作。
(3)输入.table命令可以查看当前数据库的表,.schema命令可以查看所有表的建表语句。.exit或.quit命令可以退出数据库编辑,exit命令可以退出adb shell。
(4)在数据库中查询结果出现乱码的情况的解决(Win7环境):
在控制台里输入命令:chcp 65001 确定—>在命令行标题栏上点击右键,选择【属性】 -【字体】,将字体修改为【Lucida Console】 确定
完成后再通过 adb shell 进入sqlite3,乱码解决.
注:恢复cmd的默认设置:Win+R -> 输入regedit -> 找到HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe -> 右键删除文件夹%SystemRoot%_system32_cmd.exe -> 重启cmd即可。
3、示例程序:
(1)xml文件:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical" > 5 6 <Button 7 android:id="@+id/create_database_btn" 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" 10 android:text="创建数据库" /> 11 12 <Button 13 android:id="@+id/add_data_btn" 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" 16 android:text="添加数据" /> 17 18 <Button 19 android:id="@+id/update_data_btn" 20 android:layout_width="match_parent" 21 android:layout_height="wrap_content" 22 android:text="更新数据" /> 23 24 <Button 25 android:id="@+id/delete_data_btn" 26 android:layout_width="match_parent" 27 android:layout_height="wrap_content" 28 android:text="删除数据" /> 29 30 <Button 31 android:id="@+id/query_data_btn" 32 android:layout_width="match_parent" 33 android:layout_height="wrap_content" 34 android:text="查询数据" /> 35 36 <TextView 37 android:layout_width="match_parent" 38 android:layout_height="wrap_content" 39 android:text="查询结果:" /> 40 41 <TextView 42 android:id="@+id/query_result_tv" 43 android:layout_width="match_parent" 44 android:layout_height="wrap_content" /> 45 46 </LinearLayout>
(2)MainActivity:
1 package com.example.databasetest2; 2 3 import java.util.ArrayList; 4 5 import android.app.Activity; 6 import android.database.Cursor; 7 import android.database.sqlite.SQLiteDatabase; 8 import android.os.Bundle; 9 import android.view.Menu; 10 import android.view.MenuItem; 11 import android.view.View; 12 import android.view.View.OnClickListener; 13 import android.widget.Button; 14 import android.widget.EditText; 15 import android.widget.TextView; 16 17 public class MainActivity extends Activity implements OnClickListener { 18 19 private Button createDatabaseBtn; 20 private Button addDataBtn; 21 private Button updateDataBtn; 22 private Button deleteDataBtn; 23 private Button queryDataBtn; 24 private TextView queryResultTv; 25 26 private MyDatabaseHelper dbHelper; 27 28 @Override 29 protected void onCreate(Bundle savedInstanceState) { 30 super.onCreate(savedInstanceState); 31 setContentView(R.layout.activity_main); 32 33 // 1.创建MyDatabaseHelper实例 34 dbHelper = new MyDatabaseHelper(this, "BookStore.db", null, 3); // 这里的3为数据库版本号 35 36 createDatabaseBtn = (Button) findViewById(R.id.create_database_btn); 37 addDataBtn = (Button) findViewById(R.id.add_data_btn); 38 updateDataBtn = (Button) findViewById(R.id.update_data_btn); 39 deleteDataBtn = (Button) findViewById(R.id.delete_data_btn); 40 queryDataBtn = (Button) findViewById(R.id.query_data_btn); 41 queryResultTv = (TextView) findViewById(R.id.query_result_tv); 42 43 createDatabaseBtn.setOnClickListener(this); 44 addDataBtn.setOnClickListener(this); 45 updateDataBtn.setOnClickListener(this); 46 deleteDataBtn.setOnClickListener(this); 47 queryDataBtn.setOnClickListener(this); 48 } 49 50 @Override 51 public void onClick(View v) { 52 SQLiteDatabase db; 53 switch (v.getId()) { 54 case R.id.create_database_btn: 55 // 2.调用MyDatabaseHelper的getWritableDatabase方法打开数据库 56 dbHelper.getWritableDatabase(); 57 break; 58 case R.id.add_data_btn: 59 // 先获取SQLiteDatabase实例,然后直接用execSQL方法执行SQL语句的方式往表中插入数据 60 db = dbHelper.getWritableDatabase(); 61 db.execSQL( 62 "insert into book (name,author,pages,price,category_id) values (?,?,?,?,?)", 63 new String[] { "三体", "刘慈欣", "567", "49.9", "1" }); // 注:所有类型的占位符数据都要是字符串,若语句中无占位符参数,则第二个函数参数可为null 64 db.execSQL( 65 "insert into book (name,author,pages,price,category_id) values (?,?,?,?,?)", 66 new String[] { "第一行代码——Android", "郭霖", "401", "25.9", "3" }); 67 break; 68 case R.id.update_data_btn: 69 db = dbHelper.getWritableDatabase(); 70 // 更新数据 71 db.execSQL("update book set price = ? where name = ?", 72 new String[] { "10.99", "三体" }); 73 break; 74 case R.id.delete_data_btn: 75 db = dbHelper.getWritableDatabase(); 76 // 删除数据 77 db.execSQL("delete from book where pages > ?", new String[] { "1" }); 78 break; 79 case R.id.query_data_btn: 80 db = dbHelper.getWritableDatabase(); 81 Cursor cursor = db.rawQuery("select * from book where id < ?", 82 new String[] { "100" }); 83 int idIndex = 0; 84 int authorIndex = 0; 85 int priceIndex = 0; 86 int pagesIndex = 0; 87 int nameIndex = 0; 88 int categoryIdIndex = 0; 89 90 ArrayList<Book> bookQueryList = new ArrayList<Book>(); 91 92 // 获取每个字段的ColumnIndex 93 if (cursor.getCount() >= 0) { 94 idIndex = cursor.getColumnIndex("id"); 95 authorIndex = cursor.getColumnIndex("author"); 96 priceIndex = cursor.getColumnIndex("price"); 97 pagesIndex = cursor.getColumnIndex("pages"); 98 nameIndex = cursor.getColumnIndex("name"); 99 categoryIdIndex = cursor.getColumnIndex("category_id"); 100 } 101 while (cursor.moveToNext()) { 102 Book book = new Book(); 103 book.setId(cursor.getInt(idIndex)); 104 book.setAuthor(cursor.getString(authorIndex)); 105 book.setPrice(cursor.getDouble(priceIndex)); 106 book.setPages(cursor.getInt(pagesIndex)); 107 book.setName(cursor.getString(nameIndex)); 108 book.setCategoryId(cursor.getInt(categoryIdIndex)); 109 110 bookQueryList.add(book); 111 } 112 113 queryResultTv.setText(bookQueryList.toString()); 114 break; 115 default: 116 break; 117 } 118 } 119 } 120 121 // Book类 122 class Book { 123 private int id; 124 private String author; 125 private double price; 126 private int pages; 127 private String name; 128 private int categoryId; 129 130 public int getCategoryId() { 131 return categoryId; 132 } 133 134 public void setCategoryId(int categoryId) { 135 this.categoryId = categoryId; 136 } 137 138 public int getId() { 139 return id; 140 } 141 142 public void setId(int id) { 143 this.id = id; 144 } 145 146 public String getAuthor() { 147 return author; 148 } 149 150 public void setAuthor(String author) { 151 this.author = author; 152 } 153 154 public double getPrice() { 155 return price; 156 } 157 158 public void setPrice(double price) { 159 this.price = price; 160 } 161 162 public int getPages() { 163 return pages; 164 } 165 166 public void setPages(int pages) { 167 this.pages = pages; 168 } 169 170 public String getName() { 171 return name; 172 } 173 174 public void setName(String name) { 175 this.name = name; 176 } 177 178 @Override 179 public String toString() { 180 return "Book [ id = " + id + ", author = " + author + ", price = " 181 + price + ", pages = " + pages + ", name = " + name 182 + ", category_id = " + categoryId + " ]"; 183 } 184 }
程序运行效果:
4、使用事务
1 // 删除旧数据并添加新数据,两个操作为一个原子操作 2 SQLiteDatabase db = dbHelper.getWritableDatabase(); 3 db.beginTransaction(); // 开启事务 4 try { 5 db.delete("book", null, null); 6 // if (true) { 7 // // 在这里手动抛出一个异常,让事务失败 8 // throw new NullPointerException(); 9 // } 10 ContentValues values = new ContentValues(); 11 values.put("name", "这是本新书"); 12 values.put("author", "贾永基"); 13 values.put("pages", 123); 14 values.put("price", 13.4); 15 db.insert("book", null, values); 16 db.setTransactionSuccessful();// 事务已经执行成功 17 18 } catch (Exception e) { 19 e.printStackTrace(); 20 } finally { 21 db.endTransaction(); // 结束事务 22 }
5、补充:使用SQLiteDataBase类自带的方法进行数据库的增、删、改操作(因为使用自带的方法进行查询的操作过于复杂不做介绍,直接使用SQL语句即可):
(1)插入数据:
1 ... 2 SQLiteDatabase db = dbHelper.getWritableDatabase(); 3 4 // 使用ContentValues对象组装数据 5 ContentValues values = new ContentValues(); 6 values.put("name","三体"); 7 values.put("pages",565); 8 values.put("price",23.99); 9 10 // 插入数据到数据库中 11 // 第一个参数:表名;第二个参数:用于在未指定添加数据的情况下为某些可为空的字段自动赋值为NULL,一般用不到这个功能,把这个参数传入null即可;第三个参数:携带数据的ContentValues对象 12 db.insert("Book",null,values); 13 ...