数据持久化的几种方法——首选项+内部存储+外部存储+数据库

原创
2015/05/26 01:42
阅读数 50

1. 使用Preference来保存首选项数据

在res/xml下建preference的xml文件

<? xml version = "1.0"  encoding = "utf-8" ?> 
<PreferenceScreen   xmlns:android = "http://schemas.android.com/apk/res/android" > 
  <PreferenceCategory   android:title = "Category One" > 
  <CheckBoxPreference 
  android:defaultValue = "false" 
  android:key = "checkboxpref" 
  android:summary = "True or False" 
  android:title = "CheckBox"   /> 
  </PreferenceCategory> 
  <PreferenceCategory   android:title = "Category Two" > 
  <EditTextPreference 
  android:defaultValue = "[Enter a string here]" 
  android:key = "editTextPref" 
  android:summary = "Enter a string" 
  android:title = "Edit Text"   /> 
  <RingtonePreference 
  android:key = "ringtonepref" 
  android:summary = "select a ringtone" 
  android:title = "Ringtones"   /> 
  <PreferenceScreen 
  android:key = "SecondPrefScreen" 
  android:summary = "Click here to go to the second preference screen" 
  android:title = "Second prefernce Screen" > 
  <EditTextPreference 
  android:key = "secondedittext" 
  android:summary = "Enter a key" 
  android:title = "Edit Text(Second Screen)"   /> 
 
  </PreferenceScreen> 
  </PreferenceCategory> 
</PreferenceScreen>



用到的xml的tag:  PreferenceScreen   PreferenceCategory CheckBoxPreference EditTextPreference RingtonePreference
其中  PreferenceScreen 中的内容显示在同一个界面里,  PreferenceScreen 嵌入 PreferenceScreen  可以放置一个入口进入另一个界面;
  PreferenceCategory 对首选项内容进行分类(视觉上的,实际调用感觉没区别);
每个首选项都有一个键值 android:key 跟View的id类似的识别作用;

public   class   MyPrefAty   extends   PreferenceActivity   { 
 
  @Override 
  protected   void  onCreate ( Bundle  savedInstanceState )   { 
  super . onCreate ( savedInstanceState ); 
 
  PreferenceManager  preferenceManager  =  getPreferenceManager (); 
 preferenceManager . setSharedPreferencesName ( "MySelfdefinePrefName" ); 
 
 addPreferencesFromResource ( R . xml . myapppreferences ); 
  } 
}



继承自   PreferenceActivity ,从xml文件加载首选项界面,并且重命名该首选项的名称
用到的类: PreferenceManager
用到的方法:  getPreferenceManager () setSharedPreferencesName ( "MySelfdefinePrefName" ) addPreferencesFromResource ( R . xml . myapppreferences )

public   void  onClickDisplay ( View  view )   { 
  // SharedPreferences sharedPreferences = getSharedPreferences("com.example.administrator.mypreference_preferences", MODE_PRIVATE); 
  SharedPreferences  sharedPreferences  =  getSharedPreferences ( "MySelfdefinePrefName" ,  MODE_PRIVATE ); 
  Toast . makeText ( getBaseContext (),  sharedPreferences . getString ( "editTextPref" ,   "" ),   Toast . LENGTH_SHORT ). show (); 
  } 
 
  public   void  onClickModify ( View  view )   { 
  //SharedPreferences sharedPreferences = getSharedPreferences("com.example.administrator.mypreference_preferences", MODE_PRIVATE); 
  SharedPreferences  sharedPreferences  =  getSharedPreferences ( "MySelfdefinePrefName" ,  MODE_PRIVATE ); 
  SharedPreferences . Editor  editor  =  sharedPreferences . edit (); 
 editor . putString ( "editTextPref" ,   (( EditText )  findViewById ( R . id . txtString )). getText (). toString ()); 
 editor . commit (); 
  }



用到的类:  SharedPreferences SharedPreferences . Editor
用到的方法: getSharedPreferences ( "MySelfdefinePrefName" , MODE_PRIVATE ) edit () putString() commit ()
关键: getSharedPreferences() 的第一个参数是对应首选项的xml的名称,默认是 “包名+_preferencens”,由此获得相应的首选项。修改首选项中的值 需要用到  SharedPreferences . Editor


2. 数据保存到内部存储


public class FileSave extends Activity { 
     EditText editText; 
     static final int READ_BLOCK_SIZE = 100; 
     @Override 
     protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.myfilesavelayout); 
     editText = (EditText) findViewById(R.id.edittextFile); 
     } 
     public void onClickLoad(View v) { 
     try { 
     FileInputStream fileInputStream = openFileInput("textfile.txt"); 
     InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); 
     char[] inputBuffer = new char[READ_BLOCK_SIZE]; 
     String s = ""; 
     int charRead; 
     while ((charRead = inputStreamReader.read(inputBuffer)) > 0) { 
     String readString = String.copyValueOf(inputBuffer, 0, charRead); 
     s += readString; 
     } 
     editText.setText(s); 
     Toast.makeText(getBaseContext(), "File Load Successfully.", Toast.LENGTH_SHORT).show(); 
     } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
     } catch (IOException e) { 
     e.printStackTrace(); 
     } 
     } 
     public void onClickSave(View view) { 
     String string = editText.getText().toString(); 
     try { 
     FileOutputStream fileOutputStream = openFileOutput("textfile.txt", MODE_WORLD_READABLE); 
     OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream); 
     outputStreamWriter.write(string); 
     outputStreamWriter.flush(); 
     outputStreamWriter.close(); 
     Toast.makeText(getBaseContext(), "File Save Successfully", Toast.LENGTH_SHORT).show(); 
     editText.setText(""); 
     } catch (IOException e) { 
     e.printStackTrace(); 
     } 
     } 
    }



用到的类: FileInputStream InputStreamReader FileOutputStream OutputStreamWriter
用到的方法: openFileInput ( "textfile.txt" ) read ( inputBuffer ) copyValueOf ( inputBuffer , 0 , charRead ) openFileOutput ( "textfile.txt" , MODE_WORLD_READABLE ) write ( string ) flush () close ()

关键点: openFileOutput ( "textfile.txt" , MODE_WORLD_READABLE )  和  openFileInput ( "textfile.txt" ) 都是使用相对路径,即内部存储的路径,只需要自定义文件名。


3. 保存数据到SD卡(外部存储)


public class SdSave extends Activity { 
     EditText editText; 
     @Override 
     protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     setContentView(R.layout.mysdlayout); 
     editText = (EditText) findViewById(R.id.SDedittextFile); 
     } 
     public void onClickSaveSD(View view) { 
     String string = editText.getText().toString(); 
     try { 
     File sdCard = Environment.getExternalStorageDirectory(); 
     File directory = new File(sdCard.getAbsolutePath() + "/MyFiles"); 
     directory.mkdir(); 
     File file = new File(directory, "textfile.txt"); 
     FileOutputStream fileOutputStream = new FileOutputStream(file); 
     OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream); 
     outputStreamWriter.write(string); 
     outputStreamWriter.flush();  
     outputStreamWriter.close(); 
     } catch (IOException e) { 
     e.printStackTrace(); 
     } 
     Toast.makeText(getBaseContext(), "Save In SD successfully!", Toast.LENGTH_SHORT).show(); 
     editText.setText(""); 
     } 
     public void onClickLoadSD(View view) { 
     try { 
     File sdCard = Environment.getExternalStorageDirectory(); 
     File directory = new File(sdCard.getAbsolutePath() + "/MyFiles"); 
     File file = new File(directory, "textfile.txt"); 
     FileInputStream fileInputStream = new FileInputStream(file); 
     InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream); 
     char[] inputBuffer = new char[100]; 
     String s = ""; 
     int charRead; 
     while ((charRead = inputStreamReader.read(inputBuffer)) > 0) { 
     String readString = String.copyValueOf(inputBuffer, 0, charRead); 
     s += readString; 
     inputBuffer = new char[100]; 
     } 
     editText.setText(s); 
     } catch (FileNotFoundException e) { 
     e.printStackTrace(); 
     } catch (IOException e) { 
     e.printStackTrace(); 
     } 
     } 
    }



用到的类: File Environment FileOutputStream OutputStreamWriter FileInputStream InputStreamReader
用到的方法: getExternalStorageDirectory () getAbsolutePath () mkdir () write ( string ) flush () close () read ( inputBuffer ) copyValueOf ( inputBuffer , 0 , charRead )

关键在于用  getExternalStorageDirectory () 和  getAbsolutePath () 获得SD卡完整的路径(还需加上自定义的文件夹名和文件名)


4.使用数据库

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 = "MyDB3";
 static final String DATABASE_TABLE = "contacts";
 static final int DATABASE_VERSON = 1;
 
 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 context){
 this.context = context;
 DBHelper = new DatabaseHelper(context);
 }
 
 private static class DatabaseHelper extends SQLiteOpenHelper{
 
 DatabaseHelper(Context context){
 super(context, DATABASE_NAME, null, DATABASE_VERSON);
 }
 
 @Override
 public void onCreate(SQLiteDatabase db) {
 db.execSQL(DATABASE_CREATE);
 }
 
 @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);
 }
 
 
 }
 
 
 public DBAdapter open() throws SQLException{
 db = DBHelper.getWritableDatabase();
 //db = DBHelper.getReadableDatabase();
 return this;
 }
 
 public void close(){
 DBHelper.close();
 }
 
 public long inserContacts(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);
 }
 
 public boolean deleteContact(long rowId){
 return db.delete(DATABASE_TABLE,KEY_ROWID+"="+rowId,null)>0;
 }
 
 public Cursor getAllContacts(){
 return db.query(DATABASE_TABLE,new String[]{KEY_ROWID,KEY_NAME,KEY_EMAIL},null,null,null,null,null);
 }
 
 public Cursor getContact(long rowId) throws SQLException{
 Cursor cursor = db.query(true,DATABASE_TABLE,new String[]{KEY_ROWID,KEY_NAME,KEY_EMAIL},KEY_ROWID +"="+rowId,null,null,null,null,null);
 if (cursor!=null)
 cursor.moveToFirst();
 return cursor;
 }
 
 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;
 }
}





概述:创建一个辅助类来封装访问数据的复杂性(即方法)
用到的类: SQLiteDatabase SQLiteOpenHelper ContentValues Cursor
用到的方法: onUpgrade ( SQLiteDatabase db , int oldVersion , int newVersion ) getWritableDatabase () close () put ( KEY_NAME , name ) insert ( DATABASE_TABLE , null , initialValues ) delete ( DATABASE_TABLE , KEY_ROWID + "=" + rowId , null ) query ( DATABASE_TABLE , new String []{ KEY_ROWID , KEY_NAME , KEY_EMAIL }, null , null , null , null , null ) update ( DATABASE_TABLE , args , KEY_ROWID + "=" + rowId , null )

核心:   SQLiteOpenHelper 是个安卓SQL的辅助类,用来处理数据库的创建版本管理 ,需要重写它的onCreate()和onUpgrade()方法。 onCreate()在所需数据库不存在的时候被调用来创建一个新的数据库, onUpgrade()在数据库需要升级的时候被调用,取决于数据库的版本号码变化。(e.g. 将上面代码中的 DATABASE_VERSON从1改到2 ,再次运行程序的时候onUpgrade()就会被调用


编程方式使用数据库——

public class MainActivity extends Activity {
 DBAdapter dbAdapter;
 TextView textView;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 textView = (TextView) findViewById(R.id.showInfo);
 dbAdapter = new DBAdapter(this); 
 
 try {
 dbAdapter.open();
 } catch (SQLException e) {
 e.printStackTrace();
 }
 long id = dbAdapter.inserContacts("Li XiaoMing","997654321@qq.com");
 id = dbAdapter.inserContacts("Bruce","lixiaoming@gmail.com");
 dbAdapter.close();
 
 }
 
 public void ShowAllcontacts(View view) {
 try {
 dbAdapter.open();
 Cursor cursor = dbAdapter.getAllContacts();
 String s = "";
 
 if (cursor.moveToFirst()) {
 do {
 s += "id: " + cursor.getString(0) + " Name: " + cursor.getString(1) + " Email: " + cursor.getString(2) + "\n";
 } while (cursor.moveToNext());
 
 textView.setText(s);
 }
 dbAdapter.close();
 } catch (SQLException e) {
 e.printStackTrace();
 }
 }
 
 public void ShowOneContact(View view) {
 
 EditText editText = (EditText) findViewById(R.id.edittext_id);
 int id = Integer.parseInt(editText.getText().toString());
 //   Toast.makeText(getBaseContext(),Integer.toString(id),Toast.LENGTH_SHORT).show();
 
 try {
 dbAdapter.open();
 Cursor cursor = dbAdapter.getContact(id);
 if (cursor.moveToFirst())
 textView.setText("id: " + cursor.getString(0) + " Name: " + cursor.getString(1) + " Email: " + cursor.getString(2) + "\n");
 else
 Toast.makeText(getBaseContext(), "Contact Not Found", Toast.LENGTH_SHORT).show();
 } catch (SQLException e) {
 e.printStackTrace();
 }
 dbAdapter.close();
 
 }
 
 public void UpdateContact(View view) {
 EditText editText = (EditText) findViewById(R.id.edittext_id);
 int id = Integer.parseInt(editText.getText().toString());
 
 try {
 dbAdapter.open();
 } catch (SQLException e) {
 e.printStackTrace();
 }
 if (dbAdapter.updateContact(id, "Charley", "12332@outook.com"))
 Toast.makeText(getBaseContext(), "update Success", Toast.LENGTH_SHORT).show();
 else
 Toast.makeText(getBaseContext(), "update failure", Toast.LENGTH_SHORT).show();
 dbAdapter.close();
 
 }
 
 public void DeleteContact(View view) {
 EditText editText = (EditText) findViewById(R.id.edittext_id);
 int id = Integer.parseInt(editText.getText().toString());
 
 try {
 dbAdapter.open();
 } catch (SQLException e) {
 e.printStackTrace();
 }
 
 if (dbAdapter.deleteContact(id))
 Toast.makeText(getBaseContext(), "Delete Success", Toast.LENGTH_SHORT).show();
 else
 Toast.makeText(getBaseContext(), "Delete failure", Toast.LENGTH_SHORT).show();
 
 dbAdapter.close();
 }
}




用到的类: Cursor DBAdapter(自定义的类)
用到的方法: moveToFirst () moveToNext () getString ( )

分析:使用时主要使用自定义的 DBAdapter 类进行处理数据,其中 Cursor 作为检索返回的数据的类型(类似一个数组集合)


以上的方法的数据库是在运行的时候被创建的,但有时程序可能需要使用现有的数据库或者导入预创建好的数据库。
那么,如何使用于创建好的数据库——
首先,介绍一个可以用来查看和编辑SQLite数据库的软件——sqlitebrowser—— http://https://github.com/sqlitebrowser/sqlitebrowser
接着,简单介绍一下安卓使用的 SQLite 数据库的特殊之处:

如果在程序中创建一个DB,导出后使用进行sqlitebrowser查看——发现,有2个额外的表 android_metadata和sqlite_squence



其中android_metadata用来定义使用的语言,可以删除(虽然有的博客说不行,但我亲测是可以的)



sqlite_squence似乎是来记录主要表的信息(项的数量),并且无法删除

    

      

易出Bug的地方:
sqlitebrowser新建一个DB,会将版本默认为是0,在上面的代码中 DATABASE_VERSON 设置为1,因此如果直接导入新建的这个DB,会出现错误(程序崩溃...查了好久的bug...)
在以下的地方进行修改版本号


将DB文件放入\app\src\main\assets文件夹


代码:


protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 
 textView = (TextView) findViewById(R.id.showInfo);
 dbAdapter = new DBAdapter(this);
 
 try {
 String destPath = "/data/data/" + getPackageName() + "/databases";
 Toast.makeText(getBaseContext(), destPath, Toast.LENGTH_SHORT).show();
 File file = new File(destPath);
 if (!file.exists()) {
 file.mkdirs();
 file.createNewFile();
 CopyDB(getBaseContext().getAssets().open("test2"), new FileOutputStream(destPath + "/MyDB3"));
 }
 
 } catch (IOException e) {
 e.printStackTrace();
 }
 
 }
 
 
 public void CopyDB(InputStream inputStream, OutputStream outputStream) throws IOException {
 byte[] buffer = new byte[1024];
 int length;
 while ((length = inputStream.read(buffer)) > 0) {
 outputStream.write(buffer, 0, length);
 }
 Toast.makeText(getBaseContext(), "Copy Success", Toast.LENGTH_SHORT).show();
 inputStream.close();
 outputStream.close();
 }



用到的类: File FileOutputStream InputStream
用到的方法:  getPackageName () mkdirs () createNewFile () write ( buffer , 0 , length )

分析: 将assets里的DB文件拷贝到内部存储的databases文件夹里;DB文件至多只会被拷贝一次,因为是在判断databases文件夹是否存在之后才进行拷贝,避免重复覆盖DB数据。

展开阅读全文
打赏
0
0 收藏
分享
加载中
更多评论
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部