一.四大組件:
Android四大組件分別為activity
、service
、content provider
、broadcast receiver
。
1、activity
(1)一個Activity
通常就是一個單獨的屏幕(窗口)。
(2)Activity
之間通過Intent
進行通信。
(3)android應用中每一個Activity
都必須要在AndroidManifest.xml
配置文件中聲明,否則系統將不識別也不執行該Activity
。
2、service
(1)service
用于在后臺完成用戶指定的操作。service
分為兩種:
-
started
(啟動):當應用程序組件(如activity
)調用startService()
方法啟動服務時,服務處于started
狀態。 -
bound
(綁定):當應用程序組件調用bindService()
方法綁定到服務時,服務處于bound
狀態。
(2)startService()
與bindService()
區別:
started service
(啟動服務)是由其他組件調用startService()
方法啟動的,這導致服務的onStartCommand()
方法被調用。當服務是started
狀態時,其生命周期與啟動它的組件無關,并且可以在后臺無限期運行,即使啟動服務的組件已經被銷毀。因此,服務需要在完成任務后調用stopSelf()
方法停止,或者由其他組件調用stopService()
方法停止。使用
bindService()
方法啟用服務,調用者與服務綁定在了一起,調用者一旦退出,服務也就終止,大有“不求同時生,必須同時死”的特點。
(3)開發人員需要在應用程序配置文件中聲明全部的service
,使用<service></service>
標簽。
(4)Service
通常位于后臺運行,它一般不需要與用戶交互,因此Service
組件沒有圖形用戶界面。Service
組件需要繼承Service
基類。Service
組件通常用于為其他組件提供后臺服務或監控其他組件的運行狀態。
3、content provider
(1)android
平臺提供了Content Provider
使一個應用程序的指定數據集提供給其他應用程序。其他應用可以通過ContentResolver
類從該內容提供者中獲取或存入數據。
(2)只有需要在多個應用程序間共享數據是才需要內容提供者。例如,通訊錄數據被多個應用程序使用,且必須存儲在一個內容提供者中。它的好處是統一數據訪問方式。
(3)ContentProvider
實現數據共享。ContentProvider
用于保存和獲取數據,并使其對所有應用程序可見。這是不同應用程序間共享數據的唯一方式,因為android
沒有提供所有應用共同訪問的公共存儲區。
(4)開發人員不會直接使用ContentProvider
類的對象,大多數是通過ContentResolver
對象實現對ContentProvider
的操作。
(5)ContentProvider使用URI來唯一標識其數據集,這里的URI以content://作為前綴,表示該數據由ContentProvider來管理。
4、broadcast receiver
(1)你的應用可以使用它對外部事件進行過濾,只對感興趣的外部事件(如當電話呼入時,或者數據網絡可用時)進行接收并做出響應。廣播接收器沒有用戶界面。然而,它們可以啟動一個activity
或serice
來響應它們收到的信息,或者用NotificationManager
來通知用戶。通知可以用很多種方式來吸引用戶的注意力,例如閃動背燈、震動、播放聲音等。一般來說是在狀態欄上放一個持久的圖標,用戶可以打開它并獲取消息。
(2)廣播接收者的注冊有兩種方法,分別是程序動態注冊和AndroidManifest
文件中進行靜態注冊。
(3)動態注冊廣播接收器特點是當用來注冊的Activity
關掉后,廣播也就失效了。靜態注冊無需擔憂廣播接收器是否被關閉,只要設備是開啟狀態,廣播接收器也是打開著的。也就是說哪怕app本身未啟動,該app訂閱的廣播在觸發時也會對它起作用。
二.六大布局:
六大界面布局方式包括:
LinearLayout(線性布局)、
RelativeLayout(相對布局)、
TableLayout(表格布局)、
FrameLayout(幀布局)、
GridLayout(網格布局) 、
AbsoluteLayout(絕對布局)。
1. LinearLayout 線性布局
LinearLayout
容器中的組件一個挨一個排列,通過控制android:orientation
屬性,可控制各組件是橫向排列還是縱向排列。
LinearLayout
的常用XML屬性及相關方法:
XML屬性 | 相關方法 | 說明 |
---|---|---|
android:gravity | setGravity(int) | 設置布局管理器內組件的對齊方式 |
android:orientation | setOrientation(int) | 設置布局管理器內組件的排列方式,可以設置為horizontal、vertical兩個值之一 |
其中,gravity
屬性支持top, left, right, center_vertical, fill_vertical, center_horizontal, fill_horizontal, center, fill, clip_vertical, clip_horizontal
。也可以同時指定多種對齊方式的組合。
LinearLayout子元素支持的常用XML屬性及方法:
XML屬性 | 說明 |
---|---|
android:layout_gravity | 指定該子元素在LinearLayout中的對齊方式 |
android:layout_weight | 指定子元素在LinearLayout中所占的權重 |
2. TableLayout表格布局
TableLayout
繼承自Linearout
,本質上仍然是線性布局管理器。表格布局采用行、列的形式來管理UI組件,并不需要明確地聲明包含多少行、多少列,而是通過添加TableRow
、其他組件來控制表格的行數和列數。
每向TableLayout
中添加一個TableRow就代表一行;
每向TableRow
中添加一個一個子組件就表示一列;
如果直接向TableLayout
添加組件,那么該組件將直接占用一行;
在表格布局中,可以為單元格設置如下三種行為方式:
-
Shrinkable
:該列的所有單元格的寬度可以被收縮,以保證該表格能適應父容器的寬度; -
Strentchable
:該列所有單元格的寬度可以被拉伸,以保證組件能完全填滿表格空余空間; -
Collapsed
:如果該列被設置為Collapsed
,那么該列的所有單元格會被隱藏;
TableLayout
的常用XML屬性及方法
XML屬性 | 相關方法 | 說明 |
---|---|---|
android:collapseColumns | setColumns(int, boolean) | 設置需要被隱藏的列的序號,多個序號間用逗號分隔 |
android:shrinkColumns | setShrinkAllColumns(boolean) | 設置需要被收縮的列的序號 |
android:stretchColumns | setStretchAllColumns(boolean) | 設置允許被拉伸的列的序號 |
3. FrameLayout幀布局
FrameLayout
直接繼承自ViewGroup
組件。幀布局為每個加入其中的組件創建一個空白的區域(稱為一幀),每個子組件占據一幀,這些幀會根據gravity屬性執行自動對齊。
FrameLayout
的常用XM了屬性及方法
XML屬性 | 相關方法 | 說明 |
---|---|---|
android:foreground | setForeground(Drawable) | 設置該幀布局容器的前景圖像 |
android:foregroundGravity | setForeGroundGraity(int) | 定義繪制前景圖像的gravity屬性 |
4. RelativeLayout相對布局
RelativeLayout
的XML屬性及相關方法說明
XML屬性 | 相關方法 | 說明 |
---|---|---|
android:gravity | setGravity(int) | 設置布局管理器內組件的對齊方式 |
android:ignoreGravity | setIgnoreGravity(int) | 設置哪個組件不受gravity屬性的影響 |
為了控制該布局容器的各子組件的布局分布,RelativeLayout
提供了一個內部類:RelativeLayout.LayoutParams
。
RelativeLayout.LayoutParams
里只能設為boolean
的XML屬性:
XML屬性 | 說明 |
---|---|
android:layout_centerHorizontal | 設置該子組件是否位于布局容器的水平居中 |
android:layout_centerVertical | 設置該子組件是否位于布局容器的豎直居中 |
android:layout_centerParent | 設置該子組件居中 |
android:layout_alignParentBottom | 設置該子組件距底部多少 |
android:layout_alignParentLeft | 設置該子組件距左邊多少 |
android:layout_alignParentRight | 設置該子組件距右邊多少 |
android:layout_alignParentTop | 設置該子組件距頂部多少 |
RelativeLayout.LayoutParams
里屬性值為其他UI組件ID的XML屬性
XML屬性 | 說明 |
---|---|
android:layout_toRightOf | 控制該子組件位于給出ID組件的右側 |
android:layout_toLeftOf | 控制該子組件位于給出ID組件的左側 |
android:layout_above | - |
android:layout_below | - |
android:layout_alignTop | - |
android:layout_alignBottom | - |
android:layout_alignRight | - |
android:layout_alignRight | - |
5. Android 4.0新增的網格布局GridLayout
GridLayout
是Android4.0
增加的網格布局控件,與之前的TableLayout
有些相似,它把整個容器劃分為rows
× columns
個網格,每個網格可以放置一個組件。性能及功能都要比tablelayout
好,比如GridLayout
布局中的單元格可以跨越多行,而tablelayout
則不行,此外,其渲染速度也比tablelayout
要快。
GridLayout
提供了setRowCount(int)
和setColumnCount(int)
方法來控制該網格的行和列的數量。
GridLayout
常用的XML屬性和方法說明:
XML屬性 | 相關方法 | 說明 |
---|---|---|
android:alignmentMode | setAlignmentMode(int) | 設置該布局管理器采用的對齊模式 |
android:columnCount | setColumnCount(int) | 設置該網格的列數量 |
android:columnOrderPreserved | setColumnOrderPreserved(boolean) | 設置該網格容器是否保留序列號 |
android:roeCount | setRowCount(int) | 設置該網格的行數量 |
android:rowOrderPreserved | setRowOrderPreserved(boolean) | 設置該網格容器是否保留行序號 |
android:useDefaultMargins | setUseDefaultMargins(boolean) | 設置該布局管理器是否使用默認的頁邊距 |
為了控制GridLayout
布局容器中各子組件的布局分布,GridLayout
提供了一個內部類:GridLayout.LayoutParams
,來控制Gridlayout
布局容器中子組件的布局分布。
XML屬性 | 說明 |
---|---|
android:layout_column | 設置該組件在GridLayout的第幾列 |
android:layout_columnSpan | 設置該子組件在GridLayout橫向上跨幾列 |
android:layout_gravity | 設置該子組件采用何種方式占據該網格的空間 |
android:layout_row | 設置該子組件在GridLayout的第幾行 |
android:layout_rowSpan | 設置該子組件在GridLayout縱向上跨幾行 |
6. AbsoluteLayout絕對布局
即Android
不提供任何布局控制,而是由開發人員自己通過X坐標、Y坐標來控制組件的位置。每個組件都可指定如下兩個XML屬性:
- layour_x;
- layout_y;
絕對布局已經過時,不應使用或少使用。
界面布局類型的選擇和性能優化
首先得明確,界面布局類型的嵌套越多越深越復雜,會使布局實例化變慢,使Activity
的展開時間延長。建議盡量減少布局嵌套,盡量減少創建View
對象的數量。
1 . 減少布局層次,可考慮用RelativeLayout
來代替LinearLayout
。通過Relative
的相對其他元素的位置來布局,可減少塊狀嵌套;
2 . 另一種減少布局層次的技巧是使用 <merge />
標簽來合并布局;
3 . 重用布局。Android支持在XML中使用 <include />
標簽, <include />
通過指定android:layout
屬性來指定要包含的另一個XML布局。
如:
<include android:id="@+id/id1" android:layout="@layout/mylayout">
<include android:id="@+id/id2" android:layout="@layout/mylayout">
<include android:id="@+id/id3" android:layout="@layout/mylayout">
三.五大存儲:
在Android中,可供選擇的存儲方式有SharedPreferences
、文件存儲、SQLite
數據庫方式、內容提供器(Content provider
)和網絡。
1.SharedPreferences方式
Android
提供用來存儲一些簡單的配置信息的一種機制,例如,一些默認歡迎語、登錄的用戶名和密碼等。其以鍵值對的方式存儲,使得我們可以很方便的讀取和存入.
保存:
@Override
protected void onStop(){
super.onStop();
SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); //首先獲取一個SharedPreferences對象
settings.edit()
.putString(NAME, field_name.getText().toString())
.putString(PASSWORD, filed_pass.getText().toString())
.commit();
} //將用戶名和密碼保存進去 ```
獲取:
SharedPreferences settings = getSharedPreferences(SETTING_INFOS, 0); //獲取一個SharedPreferences對象
String name = settings.getString(NAME, ""); //取出保存的NAME
String password = settings.getString(PASSWORD, ""); //取出保存的PASSWORD
**注意**:`Preferences`只能在同一個包內使用,不能在不同的包之間使用。
######2.文件存儲方式
在`Android`中,其提供了`openFileInput` 和 `openFileOuput `方法讀取設備上的文件,下面看個例子代碼,具體如下所示:
`String FILE_NAME = "tempfile.tmp";` //確定要操作文件的文件名
`FileOutputStream fos = openFileOutput(FILE_NAME, Context.MODE_PRIVATE); `//初始化
`FileInputStream fis = openFileInput(FILE_NAME);` //創建寫入流
上述代碼中兩個方法只支持讀取該應用目錄下的文件,讀取非其自身目錄下的文件將會拋出異常。需要提醒的是,如果調用
`FileOutputStream` 時指定的文件不存在,`Android` 會自動創建它。另外,在默認情況下,寫入的時候會覆蓋原文件內容,如果想把
新寫入的內容附加到原文件內容后,則可以指定其模式為`Context.MODE_APPEND`。
######3.**SQLite[數據庫](http://lib.csdn.net/base/14)方式**
` SQLite`是`Android`所帶的一個標準的數據庫,它支持SQL語句,它是一個輕量級的嵌入式數據庫。
/*
- 什么是SQLiteDatabase?
- 一個SQLiteDatabase的實例代表了一個SQLite的數據庫,通過SQLiteDatabase實例的一些方法,我們可以執行SQL語句,
- 對數據庫進行增、刪、查、改的操作。需要注意的是,數據庫對于一個應用來說是私有的,并且在一個應用當中,數據庫的名字也是惟一的。
*/
/*
- 什么是SQLiteOpenHelper ?
- 這個類主要生成一個數據庫,并對數據庫的版本進行管理。
- 當在程序當中調用這個類的方法getWritableDatabase()或者getReadableDatabase()方法的時候,如果當時沒有數據,那么Android系統就會自動生成一個數據庫。
- SQLiteOpenHelper 是一個抽象類,我們通常需要繼承它,并且實現里邊的3個函數,
onCreate(SQLiteDatabase):在數據庫第一次生成的時候會調用這個方法,一般我們在這個方法里邊生成數據庫表。
onUpgrade(SQLiteDatabase, int, int):當數據庫需要升級的時候,Android系統會主動的調用這個方法。一般我們在這個方法里邊刪除數據表,并建立新的數據表,當然是否還需要做其他的操作,完全取決于應用的需求。
onOpen(SQLiteDatabase):這是當打開數據庫時的回調函數,一般也不會用到。
*/
(1)建立一個內部類,主要生成一個數據庫
private static class DatabaseHelper extends SQLiteOpenHelper {
DatabaseHelper(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
//在數據庫第一次生成的時候會調用這個方法,一般我們在這個方法里邊生成數據庫表。
@Override
public void onCreate(SQLiteDatabase db) {
String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE
+ " text not null, " + BODY + " text not null " + ");";
Log.i("haiyang:createDB=", sql);
db.execSQL(sql);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
} ```
(2)重新建立數據表
private void CreateTable() {
//mOpenHelper.getWritableDatabase()語句負責得到一個可寫的SQLite數據庫,如果這個數據庫還沒有建立,
//那么mOpenHelper輔助類負責建立這個數據庫。如果數據庫已經建立,那么直接返回一個可寫的數據庫。
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String sql = "CREATE TABLE " + TABLE_NAME + " (" + TITLE
+ " text not null, " + BODY + " text not null " + ");";
Log.i("haiyang:createDB=", sql);
try {
db.execSQL("DROP TABLE IF EXISTS diary");
db.execSQL(sql);
setTitle("數據表成功重建");
} catch (SQLException e) {
setTitle("數據表重建錯誤");
}
} ```
(3)刪除數據表
private void dropTable() {
//mOpenHelper.getWritableDatabase()語句負責得到一個可寫的SQLite數據庫,如果這個數據庫還沒有建立,
//那么mOpenHelper輔助類負責建立這個數據庫。如果數據庫已經建立,那么直接返回一個可寫的數據庫。
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String sql = "drop table " + TABLE_NAME;
try {
db.execSQL(sql);
setTitle("數據表成功刪除:" + sql);
} catch (SQLException e) {
setTitle("數據表刪除錯誤");
}
} ```
(4)插入兩條數據
private void insertItem() {
//mOpenHelper.getWritableDatabase()語句負責得到一個可寫的SQLite數據庫,如果這個數據庫還沒有建立,
//那么mOpenHelper輔助類負責建立這個數據庫。如果數據庫已經建立,那么直接返回一個可寫的數據庫。
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
String sql1 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY
+ ") values('haiyang', 'android的發展真是迅速啊');";
String sql2 = "insert into " + TABLE_NAME + " (" + TITLE + ", " + BODY
+ ") values('icesky', 'android的發展真是迅速啊');";
try {
// Log.i()會將參數內容打印到日志當中,并且打印級別是Info級別
// Android支持5種打印級別,分別是Verbose、Debug、Info、Warning、Error,當然我們在程序當中一般用到的是Info級別
Log.i("haiyang:sql1=", sql1);
Log.i("haiyang:sql2=", sql2);
db.execSQL(sql1);
db.execSQL(sql2);
setTitle("插入兩條數據成功");
} catch (SQLException e) {
setTitle("插入兩條數據失敗");
}
} ```
(5)刪除其中的一條數據
private void deleteItem() {
try {
//mOpenHelper.getWritableDatabase()語句負責得到一個可寫的SQLite數據庫,如果這個數據庫還沒有建立,
//那么mOpenHelper輔助類負責建立這個數據庫。如果數據庫已經建立,那么直接返回一個可寫的數據庫。
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
//第一個參數是數據庫表名,在這里是TABLE_NAME,也就是diary。
//第二個參數,相當于SQL語句當中的where部分,也就是描述了刪除的條件。
//如果在第二個參數當中有“?”符號,那么第三個參數中的字符串會依次替換在第二個參數當中出現的“?”符號。
db.delete(TABLE_NAME, " title = 'haiyang'", null);
setTitle("刪除title為haiyang的一條記錄");
} catch (SQLException e) {
}
} ```
(6)查詢數據
/*
* Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null)語句將查詢到的數據放到一個Cursor 當中。
這個Cursor里邊封裝了這個數據表TABLE_NAME當中的所有條列。
query()方法相當的有用,在這里我們簡單地講一下。
第一個參數是數據庫里邊表的名字,比如在我們這個例子,表的名字就是TABLE_NAME,也就是"diary"。
第二個字段是我們想要返回數據包含的列的信息。在這個例子當中我們想要得到的列有title、body。我們把這兩個列的名字放到字符串數組里邊來。
第三個參數為selection,相當于SQL語句的where部分,如果想返回所有的數據,那么就直接置為null。
第四個參數為selectionArgs。在selection部分,你有可能用到“?”,那么在selectionArgs定義的字符串會代替selection中的“?”。
第五個參數為groupBy。定義查詢出來的數據是否分組,如果為null則說明不用分組。
第六個參數為having ,相當于SQL語句當中的having部分。
第七個參數為orderBy,來描述我們期望的返回值是否需要排序,如果設置為null則說明不需要排序。
*/
private void showItems() {
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
String col[] = { TITLE, BODY };
//查詢數據
Cursor cur = db.query(TABLE_NAME, col, null, null, null, null, null);
Integer num = cur.getCount();
setTitle(Integer.toString(num) + " 條記錄");
} ```
######4.內容提供器(Content provider)方式
在`Android`的設計“哲學”里是鼓勵開發者使用內部類的,這樣不但使用方便,而且執行效率也高。
(1).什么是`ContentProvider `
數據在`Android`當中是私有的,當然這些數據包括文件數據和數據庫數據以及一些其他類型的數據。難道兩個程序之間就沒有辦法對于數據進行交換?解決這個問題主要靠`ContentProvider`。
一個`Content Provider`類實現了一組標準的方法接口,從而能夠讓其他的應用保存或讀取此`Content Provider`的各種數據類型。也就是說,一個程序可以通過實現一個`Content Provider`的抽象接口將自己的數據暴露出去。外界根本看不到,也不用看到這個應用暴露的數據在應用當中是如何存儲的,或者是用數據庫存儲還是用文件存儲,還是通過網上獲得,這些一切都不重要,重要的是外界可以通過這一套標準及統一的接口和程序里的數據打交道,可以讀取程序的數據,也可以刪除程序的數據,當然,中間也會涉及一些權限的問題。
下邊列舉一些較常見的接口,這些接口如下所示。
` query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder)`:通過Uri進行查詢,返回一個`Cursor`。
` insert(Uri url, ContentValues values)`:將一組數據插入到Uri 指定的地方。
` update(Uri uri, ContentValues values, String where, String[] selectionArgs)`:更新Uri指定位置的數據。
`delete(Uri url, String where, String[] selectionArgs)`:刪除指定Uri并且符合一定條件的數據。
(2).什么是`ContentResolver`
外界的程序通過`ContentResolver`接口可以訪問`ContentProvider`提供的數據,在`Activity`當中通過`getContentResolver()`可以得到當前應用的`ContentResolver`實例。
`ContentResolver`提供的接口和`ContentProvider`中需要實現的接口對應,主要有以下幾個。
`query(Uri uri, String[] projection, String selection, String[] selectionArgs,String sortOrder`):通過Uri進行查詢,返回一個`Cursor`。
`insert(Uri url, ContentValues values)`:將一組數據插入到Uri 指定的地方。
`update(Uri uri, ContentValues values, String where, String[] selectionArgs)`:更新Uri指定位置的數據。
`delete(Uri url, String where, String[] selectionArgs)`:刪除指定Uri并且符合一定條件的數據。
(3).`ContentProvider`和`ContentResolver`中用到的Uri
在`ContentProvider`和`ContentResolver`當中用到了Uri的形式通常有兩種,一種是指定全部數據,另一種是指定某個ID的數據。
我們看下面的例子。
`content://contacts/people/ ` 這個Uri指定的就是全部的聯系人數據。
` content://contacts/people/1 `這個Uri指定的是ID為1的聯系人的數據。
在上邊兩個類中用到的Uri一般由3部分組成。
第一部分是:`"content://" `。
第二部分是要獲得數據的一個字符串片段。
最后就是ID(如果沒有指定ID,那么表示返回全部)。
由于URI通常比較長,而且有時候容易出錯,且難以理解。所以,在`Android`當中定義了一些輔助類,并且定義了一些常量來代替這些長字符串的使用,例如下邊的代碼:
` Contacts.People.CONTENT_URI `(聯系人的URI)。
(4)實現的功能
在這個例子里邊,首先在系統的聯系人應用當中插入一些聯系人信息,然后把這些聯系人的名字和電話再顯示出來
(5)實現方法
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//getContentResolver()方法得到應用的ContentResolver實例。
// query(Phones.CONTENT_URI, null, null, null, null)。它是ContentResolver里的方法,負責查詢所有聯系人,并返回一個Cursor。這個方法參數比較多,每個參數的具體含義如下。
//· 第一個參數為Uri,在這個例子里邊這個Uri是聯系人的Uri。
//· 第二個參數是一個字符串的數組,數組里邊的每一個字符串都是數據表中某一列的名字,它指定返回數據表中那些列的值。
//· 第三個參數相當于SQL語句的where部分,描述哪些值是我們需要的。
//· 第四個參數是一個字符串數組,它里邊的值依次代替在第三個參數中出現的“?”符號。
//· 第五個參數指定了排序的方式。
Cursor c = getContentResolver().query(Phones.CONTENT_URI, null, null, null, null);
startManagingCursor(c); //讓系統來管理生成的Cursor。
ListAdapter adapter = new SimpleCursorAdapter(
this,
android.R.layout.simple_list_item_2,
c,
new String[] { Phones.NAME, Phones.NUMBER },
new int[] { android.R.id.text1, android.R.id.text2 });
setListAdapter(adapter); //將ListView和SimpleCursorAdapter進行綁定。
} ```
######5. 網絡存儲方式
簡單講就是把一段數據通過POST發送的方式保存到服務器,要用到的時候在去服務器中請求拿到數據。