




版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1、Android數據存儲五種方式總結本文介紹Android平臺進行數據存儲的五大方式,分別如下: 1 使用SharedPreferences存儲數據 2 文件存儲數據 3 SQLite數據庫存儲數據 4 使用ContentProvider存儲數據 5 網絡存儲數據下面詳細講解這五種方式的特點第一種: 使用SharedPreferences存儲數據 適用范圍:保存少量的數據,且這些數據的格式非常簡單:字符串型、基本類型的值。比如應用程序的各種配置信息(如是否打開音效、是否使用震動效果、小游戲的玩家積分等),解鎖口 令密碼等 核心原理:保存基于XML文件存儲的key-value鍵值對數據,通常用來存
2、儲一些簡單的配置信息。通過DDMS的File Explorer面板,展開文件瀏覽樹,很明顯SharedPreferences數據總是存儲在/data/data/<package name>/shared_prefs目錄下。SharedPreferences對象本身只能獲取數據而不支持存儲和修改,存儲修改是通過SharedPreferences.edit()獲取的內部接口Editor對象實現。 SharedPreferences本身是一 個接口,程序無法直接創(chuàng)建SharedPreferences實例,只能通過Context提供的getSharedPreferences(String
3、name, int mode)方法來獲取SharedPreferences實例,該方法中name表示要操作的xml文件名,第二個參數具體如下: Context.MODE_PRIVATE: 指定該SharedPreferences數據只能被本應用程序讀、寫。 Context.MODE_WORLD_READABLE: 指定該SharedPreferences數據能被其他應用程序讀,但不能寫。 Context.MODE_WORLD_WRITEABLE: 指定該SharedPreferences數據能被其他應用程序讀,寫Editor有如下主要重要方法: SharedPreferences.Editor
4、 clear():清空SharedPreferences里所有數據 SharedPreferences.Editor putXxx(String key , xxx value):向SharedPreferences存入指定key對應的數據,其中xxx 可以是boolean,float,int等各種基本類型據 SharedPreferences.Editor remove():刪除SharedPreferences中指定key對應的數據項 boolean commit():當Editor編輯完成后,使用該方法提交修改 實際案例:運行界面如下 這里只提供了兩個按鈕和一個輸入文本框,布局簡單,故在
5、此不給出界面布局文件了,程序核心代碼如下: class ViewOcl implements View.OnClickListener Override public void onClick(View v) switch(v.getId() case R.id.btnSet: /步驟1:獲取輸入值 String code = txtCode.getText().toString().trim(); /步驟2-1:創(chuàng)建一個SharedPreferences.Editor接口對象,lock表示要寫入的XML文件名,MODE_WORLD_WRITEABLE寫操作 SharedPreferences
6、.Editor editor = getSharedPreferences("lock", MODE_WORLD_WRITEABLE).edit(); /步驟2-2:將獲取過來的值放入文件 editor.putString("code", code); /步驟3:提交 mit(); Toast.makeText(getApplicationContext(),"口令設置成功", Toast.LENGTH_LONG).show(); break; case R.id.btnGet: /步驟1:創(chuàng)建一個SharedPreferences接
7、口對象 SharedPreferences read = getSharedPreferences("lock", MODE_WORLD_READABLE); /步驟2:獲取文件中的值 String value = read.getString("code", ""); Toast.makeText(getApplicationContext(),"口令為:"+value, Toast.LENGTH_LONG).show(); break; 讀寫其他應用的SharedPreferences: 步驟如下 1、在創(chuàng)建S
8、haredPreferences時,指定MODE_WORLD_READABLE模式,表明該SharedPreferences數據可以被其他程序讀取 2、創(chuàng)建其他應用程序對應的Context: Context pvCount = createPackageContext("com.tony.app", Context.CONTEXT_IGNORE_SECURITY);這里的com.tony.app就是其他程序的包名 3、使用其他程序的Context獲取對應的SharedPreferences SharedPreferences read = pvCount.getShared
9、Preferences("lock", Context.MODE_WORLD_READABLE); 4、如果是寫入數據,使用Editor接口即可,所有其他操作均和前面一致。SharedPreferences對象與SQLite數據庫相比,免去了創(chuàng)建數據庫,創(chuàng)建表,寫SQL語句等諸多操作,相對而言更加方便,簡潔。但是SharedPreferences也有其自身缺陷,比如其職能存儲boolean,int,float,long和String五種簡單的數據類型,比如其無法進行條件查詢等。所以不論SharedPreferences的數據存儲操作是如何簡單,它也只能是存儲方式的一種補充,
10、而無法完全替代如SQLite數據庫這樣的其他數據存儲方式。 第二種: 文件存儲數據 核心原理: Context提供了兩個方法來打開數據文件里的文件IO流 FileInputStream openFileInput(String name); FileOutputStream(String name , int mode),這兩個方法第一個參數 用于指定文件名,第二個參數指定打開文件的模式。具體有以下值可選: MODE_PRIVATE:為默認操作模式,代表該文件是私有數據,只能被應用本身訪問,在該模式下,寫入的內容會覆蓋原文件的內容,如果想把新寫入的內容追加到原文件中???以使用Context.
11、MODE_APPEND MODE_APPEND:模式會檢查文件是否存在,存在就往文件追加內容,否則就創(chuàng)建新文件。 MODE_WORLD_READABLE:表示當前文件可以被其他應用讀??; MODE_WORLD_WRITEABLE:表示當前文件可以被其他應用寫入。 除此之外,Context還提供了如下幾個重要的方法: getDir(String name , int mode):在應用程序的數據文件夾下獲取或者創(chuàng)建name對應的子目錄 File getFilesDir():獲取該應用程序的數據文件夾得絕對路徑 String fileList():返回該應用數據文件夾的全部文件 實際案例:界面沿用
12、上圖 核心代碼如下:public String read() try FileInputStream inStream= this.openFileInput("message.txt"); byte buffer = new byte1024; int hasRead = 0; StringBuilder sb= new StringBuilder(); while (hasRead = inStream.read(buffer) != -1) sb.append(new String(buffer, 0, hasRead); inStream.close(); retu
13、rn sb.toString(); catch (Exception e) e.printStackTrace(); return null; public void write(String msg) / 步驟1:獲取輸入值 if(msg = null) return; try / 步驟2:創(chuàng)建一個FileOutputStream對象,MODE_APPEND追加模式 FileOutputStream fos = openFileOutput("message.txt", MODE_APPEND); / 步驟3:將獲取過來的值放入文件 fos.write(msg.getBy
14、tes(); / 步驟4:關閉數據流 fos.close(); catch (Exception e) e.printStackTrace(); openFileOutput()方法的第一參數用于指定文件名稱,不能包含路徑分隔符“/” ,如果文件不存在,Android 會自動創(chuàng)建它。創(chuàng)建的文件保存在/data/data/<package name>/files目錄,如: /data/data/cn.tony.app/files/message.txt, 下面講解某些特殊文件讀寫需要注意的地方:讀寫sdcard上的文件其中讀寫步驟按如下進行:1、調用Environment的getEx
15、ternalStorageState()方法判斷手機上是否插了sd卡,且應用程序具有讀寫SD卡的權限,如下代碼將返回trueEnvironment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)2、調用Environment.getExternalStorageDirectory()方法來獲取外部存儲器,也就是SD卡的目錄,或者使用"/mnt/sdcard/"目錄3、使用IO流操作SD卡上的文件 注意點:手機應該已插入SD卡,對于模擬器而言,可通過mksdcard命令來創(chuàng)建虛擬存儲卡 必須在Androi
16、dManifest.xml上配置讀寫SD卡的權限<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>案例代碼:/ 文件寫操作函數 private void write(String content) if (Environment.getExternalStorageState(
17、).equals( Environment.MEDIA_MOUNTED) / 如果sdcard存在 File file = new File(Environment.getExternalStorageDirectory() .toString() + File.separator + DIR + File.separator + FILENAME); / 定義File類對象 if (!file.getParentFile().exists() / 父文件夾不存在 file.getParentFile().mkdirs(); / 創(chuàng)建文件夾 PrintStream out= null; / 打
18、印流對象用于輸出 try out= new PrintStream(new FileOutputStream(file, true); / 追加文件 out.println(content); catch (Exception e) e.printStackTrace(); finally if (out != null) out.close();/ 關閉打印流 else / SDCard不存在,使用Toast提示用戶 Toast.makeText(this, "保存失敗,SD卡不存在!", Toast.LENGTH_LONG).show(); / 文件讀操作函數 priv
19、ate String read() if (Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED) / 如果sdcard存在 File file = new File(Environment.getExternalStorageDirectory() .toString() + File.separator + DIR + File.separator + FILENAME); / 定義File類對象 if (!file.getParentFile().exists() / 父文件夾不存在 file.ge
20、tParentFile().mkdirs(); / 創(chuàng)建文件夾 Scanner scan= null; / 掃描輸入 StringBuilder sb = new StringBuilder(); try scan= new Scanner(new FileInputStream(file); / 實例化Scanner while (scan.hasNext() / 循環(huán)讀取 sb.append(scan.next() + "n"); / 設置文本 return sb.toString(); catch (Exception e) e.printStackTrace();
21、finally if (scan != null) scan.close();/ 關閉打印流 else / SDCard不存在,使用Toast提示用戶 Toast.makeText(this, "讀取失敗,SD卡不存在!", Toast.LENGTH_LONG).show(); return null; 第三種:SQLite存儲數據SQLite是輕量級嵌入式數據庫引擎,它支持 SQL 語言,并且只利用很少的內存就有很好的性能。現在的主流移動設備像Android、iPhone等都使用SQLite作為復雜數據的存儲引擎,在我們?yōu)橐苿釉O備開發(fā)應用程序時,也許就要使用到SQLite
22、來存儲我們大量的數據,所以我們就需要掌握移動設備上的SQLite開發(fā)技巧SQLiteDatabase類為我們提供了很多種方法,上面的代碼中基本上囊括了大部分的數據庫操作;對于添加、更新和刪除來說,我們都可以使用1 db.executeSQL(String sql); 2 db.executeSQL(String sql, Object bindArgs);/sql語句中使用占位符,然后第二個參數是實際的參數集除了統(tǒng)一的形式之外,他們還有各自的操作方法:1 db.insert(String table, String nullColumnHack, ContentValues values);
23、2 db.update(String table, Contentvalues values, String whereClause, String whereArgs); 3 db.delete(String table, String whereClause, String whereArgs);以上三個方法的第一個參數都是表示要操作的表名;insert中的第二個參數表示如果插入的數據每一列都為空的話,需要指定此行中某一列的名稱,系統(tǒng)將此列設置為NULL,不至于出現錯誤;insert中的第三個參數是ContentValues類型的變量,是鍵值對組成的Map,key代表列名,value代表該
24、列要插入的值;update的第二個參數也很類似,只不過它是更新該字段key為最新的value值,第三個參數whereClause表示WHERE表達式,比如“age > ? and age < ?”等,最后的whereArgs參數是占位符的實際參數值;delete方法的參數也是一樣下面給出demo數據的添加1.使用insert方法1 ContentValues cv = new ContentValues();/實例化一個ContentValues用來裝載待插入的數據2 cv.put("title","you are beautiful");/
25、添加title3 cv.put("weather","sun"); /添加weather4 cv.put("context","xxxx"); /添加context5 String publish = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")6 .format(new Date();7 cv.put("publish ",publish); /添加publish8 db.insert("diary",null,
26、cv);/執(zhí)行插入操作2.使用execSQL方式來實現String sql = "insert into user(username,password) values ('Jack Johnson','iLovePopMuisc');/插入操作的SQL語句db.execSQL(sql);/執(zhí)行SQL語句數據的刪除同樣有2種方式可以實現String whereClause = "username=?"/刪除的條件String whereArgs = "Jack Johnson"/刪除的條件參數db.delete(&
27、quot;user",whereClause,whereArgs);/執(zhí)行刪除使用execSQL方式的實現String sql = "delete from user where username='Jack Johnson'"/刪除操作的SQL語句db.execSQL(sql);/執(zhí)行刪除操作數據修改同上,仍是2種方式ContentValues cv = new ContentValues();/實例化ContentValuescv.put("password","iHatePopMusic");/添加要更
28、改的字段及內容String whereClause = "username=?"/修改條件String whereArgs = "Jack Johnson"/修改條件的參數db.update("user",cv,whereClause,whereArgs);/執(zhí)行修改使用execSQL方式的實現String sql = "update user set password = 'iHatePopMusic' where username='Jack Johnson'"/修改的SQL語句
29、db.execSQL(sql);/執(zhí)行修改數據查詢下面來說說查詢操作。查詢操作相對于上面的幾種操作要復雜些,因為我們經常要面對著各種各樣的查詢條件,所以系統(tǒng)也考慮到這種復雜性,為我們提供了較為豐富的查詢形式:1 db.rawQuery(String sql, String selectionArgs); 2 db.query(String table, String columns, String selection, String selectionArgs, String groupBy, String having, String orderBy); 3 db.query(String
30、table, String columns, String selection, String selectionArgs, String groupBy, String having, String orderBy, String limit); 4 db.query(String distinct, String table, String columns, String selection, String selectionArgs, String groupBy, String having, String orderBy, String limit); 上面幾種都是常用的查詢方法,第
31、一種最為簡單,將所有的SQL語句都組織到一個字符串中,使用占位符代替實際參數,selectionArgs就是占位符實際參數集;各參數說明:· table:表名稱· colums:表示要查詢的列所有名稱集· selection:表示WHERE之后的條件語句,可以使用占位符· selectionArgs:條件語句的參數數組· groupBy:指定分組的列名· having:指定分組條件,配合groupBy使用· orderBy:y指定排序的列名· limit:指定分頁參數· distinct:指定“true”
32、或“false”表示要不要過濾重復值· Cursor:返回值,相當于結果集ResultSet最后,他們同時返回一個Cursor對象,代表數據集的游標,有點類似于JavaSE中的ResultSet。下面是Cursor對象的常用方法:1 c.move(int offset); /以當前位置為參考,移動到指定行 2 c.moveToFirst(); /移動到第一行 3 c.moveToLast(); /移動到最后一行 4 c.moveToPosition(int position); /移動到指定行 5 c.moveToPrevious(); /移動到前一行 6 c.moveToNext(
33、); /移動到下一行 7 c.isFirst(); /是否指向第一條 8 c.isLast(); /是否指向最后一條 9 c.isBeforeFirst(); /是否指向第一條之前 10 c.isAfterLast(); /是否指向最后一條之后 11 c.isNull(int columnIndex); /指定列是否為空(列基數為0) 12 c.isClosed(); /游標是否已關閉 13 c.getCount(); /總數據項數 14 c.getPosition(); /返回當前游標所指向的行數 15 c.getColumnIndex(String columnName);/返回某列名對應
34、的列索引值 16 c.getString(int columnIndex); /返回當前行指定列的值實現代碼String params = 12345,123456;Cursor cursor = db.query("user",columns,"ID=?",params,null,null,null);/查詢并獲得游標if(cursor.moveToFirst()/判斷游標是否為空 for(int i=0;i<cursor.getCount();i+) cursor.move(i);/移動到指定記錄 String username = curso
35、r.getString(cursor.getColumnIndex("username"); String password= cursor.getString(cursor.getColumnIndex("password"); 通過rawQuery實現的帶參數查詢Cursor result=db.rawQuery("SELECT ID, name, inventory FROM mytable");/Cursor c = db.rawQuery("s name, inventory FROM mytable where
36、ID=?",new Stirng"123456"); result.moveToFirst(); while (!result.isAfterLast() int id=result.getInt(0); String name=result.getString(1); int inventory=result.getInt(2); / do something useful with these result.moveToNext(); result.close(); 在上面的代碼示例中,已經用到了這幾個常用方法中的一些,關于更多的信息,大家可以參考官方文檔中的
37、說明。最后當我們完成了對數據庫的操作后,記得調用SQLiteDatabase的close()方法釋放數據庫連接,否則容易出現SQLiteException。上面就是SQLite的基本應用,但在實際開發(fā)中,為了能夠更好的管理和維護數據庫,我們會封裝一個繼承自SQLiteOpenHelper類的數據庫操作類,然后以這個類為基礎,再封裝我們的業(yè)務邏輯方法。這里直接使用案例講解:下面是案例demo的界面SQLiteOpenHelper類介紹SQLiteOpenHelper是SQLiteDatabase的一個幫助類,用來管理數據庫的創(chuàng)建和版本的更新。一般是建立一個類繼承它,并實現它的onCreate和o
38、nUpgrade方法。方法名方法描述SQLiteOpenHelper(Context context,String name,SQLiteDatabase.CursorFactory factory,int version)構造方法,其中context 程序上下文環(huán)境 即:XXXActivity.this;name :數據庫名字;factory:游標工廠,默認為null,即為使用默認工廠;version 數據庫版本號onCreate(SQLiteDatabase db)創(chuàng)建數據庫時調用onUpgrade(SQLiteDatabase db,int oldVersion , int newVer
39、sion)版本更新時調用getReadableDatabase()創(chuàng)建或打開一個只讀數據庫getWritableDatabase()創(chuàng)建或打開一個讀寫數據庫首先創(chuàng)建數據庫類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 6 public class
40、SqliteDBHelper extends SQLiteOpenHelper 7 8 / 步驟1:設置常數參量 9 private static final String DATABASE_NAME = "diary_db"10 private static final int VERSION = 1;11 private static final String TABLE_NAME = "diary"12 13 / 步驟2:重載構造方法14 public SqliteDBHelper(Context context) 15 super(context
41、, DATABASE_NAME, null, VERSION);16 17 18 /*19 * 參數介紹:context 程序上下文環(huán)境 即:XXXActivity.this 20 * name 數據庫名字 21 * factory 接收數據,一般情況為null22 * version 數據庫版本號23 */24 public SqliteDBHelper(Context context, String name, CursorFactory factory,25 int version) 26 super(context, name, factory, version);27 28 /數據庫
42、第一次被創(chuàng)建時,onCreate()會被調用29 Override30 public void onCreate(SQLiteDatabase db) 31 / 步驟3:數據庫表的創(chuàng)建32 String strSQL = "create table "33 + TABLE_NAME34 + "(tid integer primary key autoincrement,title varchar(20),weather varchar(10),context text,publish date)"35 /步驟4:使用參數db,創(chuàng)建對象36 db.exec
43、SQL(strSQL);37 38 /數據庫版本變化時,會調用onUpgrade()39 Override40 public void onUpgrade(SQLiteDatabase arg0, int arg1, int arg2) 41 42 43 正如上面所述,數據庫第一次創(chuàng)建時onCreate方法會被調用,我們可以執(zhí)行創(chuàng)建表的語句,當系統(tǒng)發(fā)現版本變化之后,會調用onUpgrade方法,我們可以執(zhí)行修改表結構等語句。 我們需要一個Dao,來封裝我們所有的業(yè)務方法,代碼如下:1 import android.content.Context; 2 import android.databa
44、se.Cursor; 3 import android.database.sqlite.SQLiteDatabase; 4 5 import com.chinasoft.dbhelper.SqliteDBHelper; 6 7 public class DiaryDao 8 9 private SqliteDBHelper sqliteDBHelper;10 private SQLiteDatabase db;11 12 / 重寫構造方法13 public DiaryDao(Context context) 14 this.sqliteDBHelper = new SqliteDBHelper
45、(context);15 db = sqliteDBHelper.getWritableDatabase();16 17 18 / 讀操作19 public String execQuery(final String strSQL) 20 try 21 System.out.println("strSQL>" + strSQL);22 / Cursor相當于JDBC中的ResultSet23 Cursor cursor = db.rawQuery(strSQL, null);24 / 始終讓cursor指向數據庫表的第1行記錄25 cursor.moveToFirst
46、();26 / 定義一個StringBuffer的對象,用于動態(tài)拼接字符串27 StringBuffer sb = new StringBuffer();28 / 循環(huán)游標,如果不是最后一項記錄29 while (!cursor.isAfterLast() 30 sb.append(cursor.getInt(0) + "/" + cursor.getString(1) + "/"31 + cursor.getString(2) + "/" + cursor.getString(3) + "/"32 + curso
47、r.getString(4)+"#");33 /cursor游標移動34 cursor.moveToNext();35 36 db.close();37 return sb.deleteCharAt(sb.length()-1).toString();38 catch (RuntimeException e) 39 e.printStackTrace();40 return null;41 42 43 44 45 / 寫操作46 public boolean execOther(final String strSQL) 47 db.beginTransaction(); /
48、開始事務48 try 49 System.out.println("strSQL" + strSQL);50 db.execSQL(strSQL);51 db.setTransactionSuccessful(); /設置事務成功完成52 db.close();53 return true;54 catch (RuntimeException e) 55 e.printStackTrace();56 return false;57 finally 58 db.endTransaction(); /結束事務 59 60 61 62 我們在Dao構造方法中實例化sqliteDB
49、Helper并獲取一個SQLiteDatabase對象,作為整個應用的數據庫實例;在增刪改信息時,我們采用了事務處理,確保數據完整性;最后要注意釋放數據庫資源db.close(),這一個步驟在我們整個應用關閉時執(zhí)行,這個環(huán)節(jié)容易被忘記,所以朋友們要注意。我們獲取數據庫實例時使用了getWritableDatabase()方法,也許朋友們會有疑問,在getWritableDatabase()和getReadableDatabase()中,你為什么選擇前者作為整個應用的數據庫實例呢?在這里我想和大家著重分析一下這一點。我們來看一下SQLiteOpenHelper中的getReadableDatabase()方法:1 public synchronized SQLiteDatabase getReadableDatabase() 2 if (mDatabase != null && mDatabase.isOpen() 3 / 如果發(fā)現mDatabase不為空并且已經打開則直接返回 4 return mDatabase; 5 6 7 if (mIsInitializing) 8 / 如果正在初始化則拋出異常 9 throw new IllegalStat
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 倉庫大門轉讓合同標準文本
- 加工沙石協議合同標準文本
- 會議培訓合同標準文本
- 食品安全知識帶回家
- 2025四川雅安市寶興縣興綠林業(yè)投資有限公司招聘6人筆試參考題庫附帶答案詳解
- 2025中國鐵路鄭州局集團有限公司招聘普通高校畢業(yè)生614人(河南)筆試參考題庫附帶答案詳解
- 個性化學習的優(yōu)勢與實施方法探討
- 2025上海獸鳥智能科技有限公司招聘2人筆試參考題庫附帶答案詳解
- 2024重慶長安專用汽車有限公司招聘筆試參考題庫附帶答案詳解
- 2024浙江寧波智邦市政工程有限公司招聘筆試及人員筆試參考題庫附帶答案詳解
- 用戶需求調研結果分析報告
- 無期限的六合公式匯編
- 公司通用管理制度匯編適合中小企業(yè)初創(chuàng)公司匯編新版
- 《嬰幼兒睡眠》課件
- 《護士服務禮儀》課件
- 《鍋爐事故處理》課件
- Creo-7.0基礎教程-配套課件
- 方格網土方計算表
- 專題五 商品的價值是如何確定的
- 激發(fā)小學生數學學習興趣的策略探究
- 兩個公司關系證明實用(十三篇)
評論
0/150
提交評論