1. 程式人生 > >Android 五大存儲方式具體解釋

Android 五大存儲方式具體解釋

宋體 初始 -s 刪除數據 pop item 數據庫版本 主鍵 help

SharedPreferences與Editor

SharedPreferences保存的數據僅僅要是類似於配置信息格式的數據。因此它保存的數據主要是簡單的key-value對形式。以下關系圖

技術分享

上圖全然能夠看出,存的時候用SharedPreferences的內部類Editor,取的時候用SharedPreferences。

SharedPreference是接口無法創建實例,Context提供以下方法創建實例 該實例僅僅能有一個,也就是單例模式。

getSharedPreferences(String name,int mode);


存取樣例

 public class MainActivity extends Activity {
	 SharedPreferences sp;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//獲取SharedPreferences實例  Test是文件名稱。不要加後綴,默覺得XML格式
		sp = getSharedPreferences("Test", Context.MODE_WORLD_WRITEABLE);
		//得到內部類寫數據
		SharedPreferences.Editor editor = sp.edit();
		editor.putString("name", "value");
		//不提交則無法獲得數據
		editor.commit();
		
	}
	public void btn(View view){
		String string = sp.getString("name", "null");
		Toast.makeText(this, string, 1).show();
	}
}
存儲路徑為:data/data/包名/shared_prefs/Test.xml

打開該文件看到

<?xml version="1.0" encoding="utf-8" standalone ="yes" ?>
<map>
<string name="name">value</string>
</map>

存儲的放值的時候String相應XML文件中就是string節點。Int的時候相應int節點。

存儲時長:卸載軟件時包名所在的目錄會消失,所以該文件無法相同會消失。


讀寫其它應用的SharedPreferences

上述是在本身的APP中玩耍,這次看怎樣讀取其它應用。關鍵是獲得其它應用的程序的Context(代表Android應用的全局信息的接口)又由於包名是Andoird應用的唯一標示,所以調用本APP的Context的方法

createPackageContext("android.example.homework", CONTEXT_IGNORE_SECURITY);

得到其它應用的Context對象而且其它應用SharedPreferences是可讀狀態(可寫不行,必須可讀)後就能夠“為所欲為”了。

public class MainActivity extends Activity {
	SharedPreferences sp;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		// 獲取SharedPreferences實例
		try {
			//通過包名得到該應用的Context對象
			Context context=createPackageContext("com.example.homework", CONTEXT_IGNORE_SECURITY);
			//用其它應用的Context創建SharedPreferences
			sp = context.getSharedPreferences("Test", Context.MODE_WORLD_READABLE+Context.MODE_WORLD_WRITEABLE);
		} catch (NameNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	public void btn(View view) {
		String string = sp.getString("name", "null");
		Toast.makeText(this, string, 1).show();
	}
}

上述的讀寫並非唯一的,程序猿全然能夠利用IO流來直接操作本應用和其它應用的SharedPreferences數據。僅僅是Android將他封裝好了,非常簡便。

無法存儲自己定義類型。主要存儲的APP配置信息,比如(username,password等)。

Chche存儲

存儲空間不夠系統會刪除Cache存儲,但不要依賴系統刪除

存:

		File dir = getCacheDir();
		File file = new File(dir,"test.txt");
		try {
			FileOutputStream fos = new FileOutputStream(file);
			fos.write("緩沖存儲".getBytes());
			
		} catch (Exception e) {
			// TODO: handle exception
		}

取:

FileInputStream fis = new FileInputStream(file)

以上兩種方式均為內部存儲,卸載應用時數據都會丟失。


File存儲

FIle存儲就是Android繼續使用Java中的IO流繼續讀寫文件的一種方式

兩個核心方法:

openFileInput(String name);

openFIleOutput(String name);

以下是簡單讀寫的樣例

public class MainActivity extends Activity {
	SharedPreferences sp;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

	}

	public void readed(View view) {
		FileInputStream fis = null;
		StringBuffer sb=null;
		try {
			//打開文件輸入流     files文件名稱。這裏系統不給加入後綴,不寫無後綴
			fis = openFileInput("files");
			byte[] buff = new byte[1024];
			int len = 0;
			sb = new StringBuffer();
			while ((len = fis.read(buff)) != -1) {
				sb.append(new String(buff, 0, len));
			}
			//關流
			fis.close();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		Toast.makeText(this, sb, 0).show();
	}

	public void writer(View view) {
		FileOutputStream fos = null;
		try {
			// 以追加模式打開文件輸出流
			fos = openFileOutput("files", MODE_APPEND);
			// 將流包裝成PrintStream
			PrintStream printStream = new PrintStream(fos);
			printStream.print("內容啦啦啦啦");
			Toast.makeText(this, "寫入成功", 0).show();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if (fos != null)
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
		}
	}
}

該文件無後綴,系統默認不加入後綴。存放路徑為data/data/包名/file/目錄下

該文件依舊存放在包名下,所以卸載時依舊會丟失數據。


SD卡存儲

使用File存儲和SharedPreferences存儲的缺點是容量小,由於在應用程序的數據目錄下。所以才有了SD卡存儲。使用SD卡存儲總共分為三步:

(1)推斷應用是否有讀寫SD卡權限

Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
(2)獲得SD卡的文件夾

File dir = Environment.getExternalStorageDirectory();
(3)使用流讀寫數據


SD卡的權限

	<!-- 加入SD卡的創建與刪除文件權限 -->
	<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/>
	<!-- 加入SD卡寫入權限 -->
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

讀寫SD卡樣例

public class MainActivity extends Activity {
	private final String FILE_NAME="test.txt";
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
	}
	/**讀取數據*/
	public void readed(View view) {
		BufferedReader bis=null;
		//當SD狀態裝備就緒時
		if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
			File dir = Environment.getExternalStorageDirectory();
			try {
				File file = new File(dir, FILE_NAME);
				FileInputStream fis = new FileInputStream(file);
				//將fis流包裝成BufferedReader
				bis= new BufferedReader(new InputStreamReader(fis));
				String len = null;
				StringBuffer sb = new StringBuffer("");
				while((len=bis.readLine())!=null){
					sb.append(len);
				}
				Toast.makeText(this, "讀到的數據是:"+sb, 0).show();
				
			} catch (Exception e) {
				e.printStackTrace();
			} 
			finally{
				if(bis!=null){
					try {
						bis.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
		else Toast.makeText(this, "SD卡不可用", 0).show();
		
		
	}
	/**寫入數據*/
	public void writer(View view) {
		RandomAccessFile raf = null;
		if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
			File dir = Environment.getExternalStorageDirectory();
			File file = new File(dir, FILE_NAME);
			//用指定文件創建RandomAccessFIle
			try {
				raf = new RandomAccessFile(file, "rw");
				//將文件記錄指針移動到最後  防止再次寫入覆蓋之前的
				raf.seek(file.length()); 
				raf.write("來一個字符串玩玩".getBytes());
				Toast.makeText(this, "寫入成功", 0).show();
			} catch (Exception e) {
				e.printStackTrace();
			}
			finally{
				if(raf!=null){
					try {
						raf.close();
					} catch (IOException e) {
						e.printStackTrace();
					}
				}
			}
		}
	}
}

SQLite存儲

SQLite是輕量級的數據庫(底層就是一個數據庫文件)一旦應用獲得了代表指定數據庫的SQLiteDatabase對象。接下來就能夠通過該對象來管理,操作數據庫了。

(1)獲取SQLiteDatabase對象。它代表了與數據庫的鏈接。

(2).調用SQLiteDatabase的方法來運行SQL語句。

(3).操作SQL語句的運行結果,比方用SimpleCursorAdapter封裝Cursor

(4).關閉SQLiDatabase。回收資源。

經常用法

//創建(假設不存在)或打開數據庫
static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory)
//打卡一個已經存在的數據庫
static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags)

MainActivity.java

public class MainActivity extends Activity {
	private ListView listview;
	private SQLiteDatabase db;
	private EditText editText;
	private String TABLE_NAME="student";
	//創建表語句
	private String CREATE_TABLE = "create table "+TABLE_NAME+" (_id integer primary key autoincrement,name varchar(20),age integer)";
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//初始化
		editText = (EditText) findViewById(R.id.et);
		listview=(ListView) findViewById(R.id.listivew);
		//打開或創建數據庫(這裏須要絕對路徑)
		db=SQLiteDatabase.openOrCreateDatabase("/mnt/sdcard/my.db3", null);
		if(!exits(TABLE_NAME)){
			db.execSQL(CREATE_TABLE);
		}
	}
	@Override
	protected void onDestroy() {
		super.onDestroy();
		//關閉數據庫
		if(db!=null&&db.isOpen())
			db.close();
	}
	public void btn(View view){
		switch (view.getId()) {
		case R.id.btn1://插入
			String str = editText.getText().toString();
			String sql = "insert into "+TABLE_NAME+" (name) values (‘"+str+"‘) ";
			System.out.println(sql);
			db.execSQL(sql);
			break;
		case R.id.btn2://讀取
			String sql2 = "select * from "+TABLE_NAME+"";
			Cursor cursor = db.rawQuery(sql2, null);
			inflateListView(cursor);
			break;
		}
	}
	public boolean exits(String table){
		String sql= "select * from sqlite_master where name="+"‘"+table+"‘";
		System.out.println(sql);
		Cursor cursor = db.rawQuery(sql, null);
		if(cursor.getCount()!=0){
			return true;
		}
		return false;
	}
	private void inflateListView(Cursor cursor){
		//構建Cursor適配器的同一時候就是對Cursor封裝成為Adapter
		SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, android.R.layout.simple_expandable_list_item_1, cursor, new String[]{"name"}, new int[]{android.R.id.text1},CursorAdapter.FLAG_REGISTER_CONTENT_OBSERVER);
		listview.setAdapter(adapter);
	}
}


activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <EditText
        android:id="@+id/et"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:id="@+id/btn1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="btn"
        android:text="插入 字符串數據" />

    <Button
        android:id="@+id/btn2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:onClick="btn"
        android:text="取出數據" />


    <ListView
        android:id="@+id/listivew"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" >
    </ListView>

</LinearLayout>

效果圖:

技術分享

因為是自己定義的路徑,所以卸載軟件依舊存在。這樣的直接使用SQLiteDatabase的方式使用的極少的。

註意:這裏主鍵的列名必須為_id,由於SimpleCursorAdapter僅僅能識別列名為_id的主鍵。

android還為我們提供操作數據庫的一些便捷方法

insert方法

不管第三個參數是否有數據,運行該方法都會加入一條數據。

        	//table插入的表名
        	//nullColumnHack強行插入為null列的列名 當values參數為null或不包括隨意key-value對時該參數有效
        	//values代表一行記錄的數據
        	insert(String table, String nullColumnHack, ContentValues values)
實際insert方法底層依舊通過SQL語句來插入的。由於它生成的SQL語句總是形如以下的語句

//ContentValue裏的key-value對的數量決定了以下的key-value對。
insert into <表名>(key1,key2...)values(value1,value2....)
假設此時第三個參數為空

insert into <表名>()values()


updata方法

該方法返回的數據庫影響的行數

//要更改數據庫表名
//更新完畢後的值
//滿足whereClaues字句的記錄將會被更新
//用於為whereClause字句傳入參數
update(String table, ContentValues values, String whereClause, String[] whereArgs)
比如我們想更新person表中的全部主鍵大於20的人的人名。可調用例如以下方法:

ContentValues values = new ContentValues();
//存放更新之後的人名
values.put("name", "新人名");
 db.update("person", values, "_id>?

", new Integer[]{20});

相應SQL語句

updata person set key1=value1,key2=value2...where _id>20


delete方法

返回值更改的行數

//table表名。whereClause刪除的條件 。whereArgs給whereClause傳入刪除的參數
delete(String table, String whereClause, String[] whereArgs)
比如:想刪除person表中全部人名以王字開頭的記錄

db.delete("person","name like?",new String[]{"王%"});
相應SQl語句

delete person where name like ‘王%‘


query方法

//distinct指定是否去除反復記錄
//table 運行查詢數據的表名
//columns要查詢出來的列名
//selection查詢條件的字句 相當於select語句wherekeyword後面的部分。
//selectionArgs查詢語句匹配的參數
//groupBy用於控制分組,
//having 對分組進行過濾
//orderBy 排序,personid desc降序,age asc升序;
//limit 進行分頁  比如:5,10
db.query(distinct, table, columns, selection, selectionArgs, groupBy, having, orderBy, limit, cancellationSignal);


db.query("person", new String[]{"_id,name,age"}, "name like ?", new String[]{"張%"}, null, null, "desc", null);

SQLiteOpenHelper

這樣的繼承SQLiteOpenHelper方式用的還是比較多的。

它是Android提供的一個管理數據庫的工具類,可用於管理數據庫的創建的版本號和更新。

經常用法:

synchronized SQLiteDatabase getReadableDatabase();以僅僅讀方式打開相應SQLiteDatabase 對象

synchronized SQLiteDatabase getWritableDatabase();以寫方式打開相應SQLiteDatabase 對象

abstract void Create(SQLiteDatabase db);第一次創建數據庫時回調該方法。


abstract void Upgrade(SQLiteDatabase db,int oldVersion,int newVersion);當數據庫版本號更新時回調該方法。

在獲得數據庫實例時建議使用getReadableDatabase();假設用getWritableDatabase()一旦磁盤滿了,打開數據庫就會出錯,而getReadableDatabase方法先僅僅讀方式打開,就算磁盤空間滿了,也不會出錯,而是繼續使用僅僅讀方式。

(getReadableDatabase調用時會包括getWritableDatabase)當調用getReadableDatabase()假設數據庫不存在調用onCreate(SQLiteDatabase db);存在之後返回該實例

MyDB.java

public class MyDB extends SQLiteOpenHelper {
	//參數1:上下文
	//參數2:數據庫名字
	//參數3:遊標工廠(自己定義的時候才用,不須要自己定義null)
	//數據庫版本
	public MyDB(Context context, String name,  int version) {
		super(context, name, null, version);
		// TODO Auto-generated constructor stub
	}
	public SQLiteDatabase db;
	
	final String CREATE_TABLE="create table student(_id integer primary key autoincrement,word varchar(20),detail varchar(20))";
	//第一次創建數據庫的時候調用。當執行第二次時db==null
	@Override
	public void onCreate(SQLiteDatabase db) {
		this.db=db;
		db.execSQL(CREATE_TABLE);
	}
	//軟件升級的時候更新表結構調用,當newVersion>oldVersion時,系統自己主動觸發該方法。
	//當打開數據庫時傳入的版本與當前的版本不同一時候會調用該方法
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

	}

}





MainActivity.java

public class MainActivity extends Activity {
	String a;
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		MyDB db = new MyDB(this, "mydb.db3", 1);
		SQLiteDatabase database = db.getReadableDatabase();

	}
}

存儲路徑data/data/包名/databases/mydb.db3

卸載時文件依舊丟失。


查詢的時候會用到Cursor:

經常用法:

isAfterLast()遊標的指針是否指向了最後一條數據的後面

moveToNext() 讓遊標的指針指向下一條數據

moveToFirst() 讓遊標的指針指向第一條數據

getString(int columnIndex) 獲取當前行中指定列的String值。參數列索引

getColumnIndex(StringcolumnName) 依據列名字獲取列索引

插入會用到:ContentValus

ContentValus的方法非常easy,鍵值對而已

改動數據:

//改動 參數1:被改動的表的名字 參數2:改動的成為的值 參數3:更新條件 參數4:更新條件中占位符的值

db.update(DBHelper.TABLE_NAME, values,DBHelper.ENSCORE_NAME+" = ?", new String[]{"hanhan"});

刪除數據:

//數據刪除 參數1:要刪除數據的表的名字 參數2:刪除條件 參數3:刪除條件中的占位符的值

//返回值---》刪除數據的行數

db.delete(DBHelper.TABLE_NAME,DBHelper.ENSCORE_NAME+" = ?", new String[]{"zhangsan"})。






Android 五大存儲方式具體解釋