文档章节

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

Freewheel
 Freewheel
发布于 2015/05/26 01:42
字数 2066
阅读 33
收藏 0

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数据。

© 著作权归作者所有

Freewheel
粉丝 10
博文 83
码字总数 48265
作品 0
普陀
程序员
私信 提问
Android成长之路——数据持久化处理

新媒体管家 点击上方“程序员大咖”,选择“置顶公众号” 关键时刻,第一时间送达! 保存key-value对——SharedPreferences 如果有比较小的数据,这些数据需要储存,那么就可以用到ShardPref...

px01ih8
2017/12/04
0
0
给Kubernetes添加持久存储 你会遇到这些坑

Kubernetes的采用正在爆炸式增长,但是撇开炒作,Kubernetes仍然是个新东西,在成为绝大多数IT基础设施的组成部分之前还有很长的路要走。 与此同时,大多数企业和IT公司还只是在试水。在开发...

K8S技术社区
2018/06/06
0
0
Kubernetes中的存储资源使用为何如此之难?

以Kubernetes为代表的容器编排工具在应用开发部署领域起正发挥着颠覆性的变革作用。随着微服务架构的发展,从开发人员的角度来看,应用逻辑架构与基础设施架构之间开始解耦,这意味着开发者能...

Docker
03/12
0
0
Android数据存储(Data Storage)(一)

Android提供几种保存持久化应用程序数据的选择。依赖具体的需求来选择解决适合的方案,如数据应该是应用程序私有的还是共享的,以及数据所需要的存储空间等。 以下是可选择的数据存储方案: ...

长平狐
2012/10/16
327
0
iOS应用数据存取之数据库存储-----Core Data

iOS应用数据存取的常用方式有如下几种XML属性列表 —— PListNSKeyedArchiver 归档Preference(偏好设置)SQLite3Core Data 现在我想记录以下我对core Data的认识存在理由:苹果公司为了关照...

哥特复心
2014/01/08
0
2

没有更多内容

加载失败,请刷新页面

加载更多

mysql免安装版,服务无法启动没有抱任何错误

1.解压 2.新建my.ini [mysqld]# Remove leading # and set to the amount of RAM for the most important data# cache in MySQL. Start at 70% of total RAM for dedicated server, e......

榴莲黑芝麻糊
23分钟前
0
0
Qt编写安防视频监控系统3-通道交换

一、前言 最开始写通道交换的功能的时候,走了很多弯路,比如最开始用最初级的办法,触发交换的时候,先关闭视频,然后设置新的url重新打开视频,这样处理非常低级而且耗内存还卡还很慢,毕竟...

飞扬青云
24分钟前
0
0
如何远程调试部署在CloudFoundry平台上的nodejs应用

网络上关于如何本地调试nodejs应用的教程已经很多了,工具有Chrome开发者工具,Visual Studio Code,和nodejs周边的一些小工具等等。 在实际情况中,我们可能遇到本地运行良好,但是部署到C...

JerryWang_SAP
45分钟前
5
0
微信扫码访问网站调用默认浏览器打开如何实现?

我们在微信内分享链接或二维码的时候,我们会发现我们的网站是可以在浏览器里正常打开的,但就是不能在微信里打开,提示 “ 已停止访问该网页 ”,无论是聊天框也一样。说是系统检测到您的网...

明尼苏达哈士奇
53分钟前
3
0
一份Java程序员进阶架构师的秘籍,你离架构师还差多远

一、如何定义架构师 Java架构师,首先要是一个Java程序员,熟练使用各种框架,并知道它们实现的原理。jvm虚拟机原理、调优,懂得jvm能让你写出性能更好的代码;池技术,什么对象池,怎么解决并...

我最喜欢三大框架
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部