安卓基礎知識

Android:


五種布局: FrameLayout 、 LinearLayout 、 AbsoluteLayout 、 RelativeLayout 、 TableLayout 全都繼承自ViewGroup,各自特點及繪制效率對比。

  • FrameLayout(框架布局)

    此布局是五中布局中最簡單的布局,Android中并沒有對child view的擺布進行控制,這個布局中所有的控件都會默認出現在視圖的左上角,我們可以使用android:layout_marginandroid:layout_gravity等屬性去控制子控件相對布局的位置。

  • LinearLayout(線性布局)

    一行只控制一個控件的線性布局,所以當有很多控件需要在一個界面中列出時,可以用LinearLayout布局。
    此布局有一個需要格外注意的屬性:android:orientation=“horizontal|vertical

    • android:orientation="horizontal時,說明你希望將水平方向的布局交給LinearLayout* ,其子元素的android:layout_gravity="right|left" 等控制水平方向的gravity值都是被忽略的,此時LinearLayout中的子元素都是默認的按照水平從左向右來排*,我們可以用android:layout_gravity="top|bottom"等gravity值來控制垂直展示。
    • 反之,可以知道 當android:orientation="vertical時,LinearLayout對其子元素展示上的的處理方式。
  • AbsoluteLayout(絕對布局)

    可以放置多個控件,并且可以自己定義控件的x,y位置

  • RelativeLayout(相對布局)

    這個布局也是相對自由的布局,Android 對該布局的child view的 水平layout& 垂直layout做了解析,由此我們可以FrameLayout的基礎上使用標簽或者Java代碼對垂直方向 以及 水平方向 布局中的views任意的控制.

    • 相關屬性:
    
      android:layout_centerInParent="true|false"
      android:layout_centerHorizontal="true|false"
      android:layout_alignParentRight="true|false"
      
    
  • TableLayout(表格布局)

    將子元素的位置分配到行或列中,一個TableLayout由許多的TableRow組成


Activity生命周期。

  • 啟動Activity:
    onCreate()—>onStart()—>onResume(),Activity進入運行狀態。

  • Activity退居后臺:
    當前Activity轉到新的Activity界面或按Home鍵回到主屏:
    onPause()—>onStop(),進入停滯狀態。

  • Activity返回前臺:
    onRestart()—>onStart()—>onResume(),再次回到運行狀態。

  • Activity退居后臺,且系統內存不足,
    系統會殺死這個后臺狀態的Activity(此時這個Activity引用仍然處在任務棧中,只是這個時候引用指向的對象已經為null),若再次回到這個Activity,則會走onCreate()–>onStart()—>onResume()(將重新走一次Activity的初始化生命周期)

  • 鎖屏:onPause()->onStop()

  • 解鎖:onStart()->onResume()

  • 更多流程分支,請參照以下生命周期流程圖


    image

通過Acitivty的xml標簽來改變任務棧的默認行為

  • 使用android:launchMode="standard|singleInstance|singleTask|singleTop"來控制Acivity任務棧。

    任務棧是一種后進先出的結構。位于棧頂的Activity處于焦點狀態,當按下back按鈕的時候,棧內的Activity會一個一個的出棧,并且調用其onDestory()方法。如果棧內沒有Activity,那么系統就會回收這個棧,每個APP默認只有一個棧,以APP的包名來命名.

    • standard : 標準模式,每次啟動Activity都會創建一個新的Activity實例,并且將其壓入任務棧棧頂,而不管這個Activity是否已經存在。Activity的啟動三回調(onCreate()->onStart()->onResume())都會執行。
    • singleTop : 棧頂復用模式.這種模式下,如果新Activity已經位于任務棧的棧頂,那么此Activity不會被重新創建,所以它的啟動三回調就不會執行,同時Activity的onNewIntent()方法會被回調.如果Activity已經存在但是不在棧頂,那么作用于standard模式一樣.
    • singleTask: 棧內復用模式.創建這樣的Activity的時候,系統會先確認它所需任務棧已經創建,否則先創建任務棧.然后放入Activity,如果棧中已經有一個Activity實例,那么這個Activity就會被調到棧頂,onNewIntent(),并且singleTask會清理在當前Activity上面的所有Activity.(clear top)
    • singleInstance : 加強版的singleTask模式,這種模式的Activity只能單獨位于一個任務棧內,由于棧內復用的特性,后續請求均不會創建新的Activity,除非這個獨特的任務棧被系統銷毀了

Activity的堆棧管理以ActivityRecord為單位,所有的ActivityRecord都放在一個List里面.可以認為一個ActivityRecord就是一個Activity棧


Activity緩存方法。

有a、b兩個Activity,當從a進入b之后一段時間,可能系統會把a回收,這時候按back,執行的不是a的onRestart而是onCreate方法,a被重新創建一次,這是a中的臨時數據和狀態可能就丟失了。

可以用Activity中的onSaveInstanceState()回調方法保存臨時數據和狀態,這個方法一定會在活動被回收之前調用。方法中有一個Bundle參數,putString()、putInt()等方法需要傳入兩個參數,一個鍵一個值。數據保存之后會在onCreate中恢復,onCreate也有一個Bundle類型的參數。

示例代碼:

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

        //這里,當Acivity第一次被創建的時候為空
        //所以我們需要判斷一下
        if( savedInstanceState != null ){
            savedInstanceState.getString("anAnt");
        }
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        outState.putString("anAnt","Android");

    }

一、onSaveInstanceState (Bundle outState)

當某個activity變得“容易”被系統銷毀時,該activity的onSaveInstanceState就會被執行,除非該activity是被用戶主動銷毀的,例如當用戶按BACK鍵的時候。

注意上面的雙引號,何為“容易”?言下之意就是該activity還沒有被銷毀,而僅僅是一種可能性。這種可能性有哪些?通過重寫一個activity的所有生命周期的onXXX方法,包括onSaveInstanceState和onRestoreInstanceState方法,我們可以清楚地知道當某個activity(假定為activity A)顯示在當前task的最上層時,其onSaveInstanceState方法會在什么時候被執行,有這么幾種情況:

1、當用戶按下HOME鍵時。

這是顯而易見的,系統不知道你按下HOME后要運行多少其他的程序,自然也不知道activity A是否會被銷毀,故系統會調用onSaveInstanceState,讓用戶有機會保存某些非永久性的數據。以下幾種情況的分析都遵循該原則

2、長按HOME鍵,選擇運行其他的程序時。

3、按下電源按鍵(關閉屏幕顯示)時。

4、從activity A中啟動一個新的activity時。

5、屏幕方向切換時,例如從豎屏切換到橫屏時。(如果不指定configchange屬性)
在屏幕切換之前,系統會銷毀activity A,在屏幕切換之后系統又會自動地創建activity A,所以onSaveInstanceState一定會被執行

總而言之,onSaveInstanceState的調用遵循一個重要原則,即當系統“未經你許可”時銷毀了你的activity,則onSaveInstanceState會被系統調用,這是系統的責任,因為它必須要提供一個機會讓你保存你的數據(當然你不保存那就隨便你了)。另外,需要注意的幾點:

1.布局中的每一個View默認實現了onSaveInstanceState()方法,這樣的話,這個UI的任何改變都會自動的存儲和在activity重新創建的時候自動的恢復。但是這種情況只有在你為這個UI提供了唯一的ID之后才起作用,如果沒有提供ID,將不會存儲它的狀態。

2.由于默認的onSaveInstanceState()方法的實現幫助UI存儲它的狀態,所以如果你需要覆蓋這個方法去存儲額外的狀態信息時,你應該在執行任何代碼之前都調用父類的onSaveInstanceState()方法(super.onSaveInstanceState())。
既然有現成的可用,那么我們到底還要不要自己實現onSaveInstanceState()?這得看情況了,如果你自己的派生類中有變量影響到UI,或你程序的行為,當然就要把這個變量也保存了,那么就需要自己實現,否則就不需要。

3.由于onSaveInstanceState()方法調用的不確定性,你應該只使用這個方法去記錄activity的瞬間狀態(UI的狀態)。不應該用這個方法去存儲持久化數據。當用戶離開這個activity的時候應該在onPause()方法中存儲持久化數據(例如應該被存儲到數據庫中的數據)。

4.onSaveInstanceState()如果被調用,這個方法會在onStop()前被觸發,但系統并不保證是否在onPause()之前或者之后觸發。

二、onRestoreInstanceState (Bundle outState)

至于onRestoreInstanceState方法,需要注意的是,onSaveInstanceState方法和onRestoreInstanceState方法“不一定”是成對的被調用的,(本人注:我昨晚調試時就發現原來不一定成對被調用的!)

onRestoreInstanceState被調用的前提是,activity A“確實”被系統銷毀了,而如果僅僅是停留在有這種可能性的情況下,則該方法不會被調用,例如,當正在顯示activity A的時候,用戶按下HOME鍵回到主界面,然后用戶緊接著又返回到activity A,這種情況下activity A一般不會因為內存的原因被系統銷毀,故activity A的onRestoreInstanceState方法不會被執行

另外,onRestoreInstanceState的bundle參數也會傳遞到onCreate方法中,你也可以選擇在onCreate方法中做數據還原。
還有onRestoreInstanceState在onstart之后執行。
至于這兩個函數的使用,給出示范代碼(留意自定義代碼在調用super的前或后):

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
        savedInstanceState.putBoolean("MyBoolean", true);
        savedInstanceState.putDouble("myDouble", 1.9);
        savedInstanceState.putInt("MyInt", 1);
        savedInstanceState.putString("MyString", "Welcome back to Android");
        // etc.
        super.onSaveInstanceState(savedInstanceState);
}

@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);

        boolean myBoolean = savedInstanceState.getBoolean("MyBoolean");
        double myDouble = savedInstanceState.getDouble("myDouble");
        int myInt = savedInstanceState.getInt("MyInt");
        String myString = savedInstanceState.getString("MyString");
}


Fragment的生命周期和activity如何的一個關系

這我們引用本知識庫里的一張圖片:
[圖片上傳失敗...(image-c0f06c-1519381687309)]

為什么在Service中創建子線程而不是Activity中

這是因為Activity很難對Thread進行控制,當Activity被銷毀之后,就沒有任何其它的辦法可以再重新獲取到之前創建的子線程的實例。而且在一個Activity中創建的子線程,另一個Activity無法對其進行操作。但是Service就不同了,所有的Activity都可以與Service進行關聯,然后可以很方便地操作其中的方法,即使Activity被銷毀了,之后只要重新與Service建立關聯,就又能夠獲取到原有的Service中Binder的實例。因此,使用Service來處理后臺任務,Activity就可以放心地finish,完全不需要擔心無法對后臺任務進行控制的情況。

Intent的使用方法,可以傳遞哪些數據類型。

通過查詢Intent/Bundle的API文檔,我們可以獲知,Intent/Bundle支持傳遞基本類型的數據和基本類型的數組數據,以及String/CharSequence類型的數據和String/CharSequence類型的數組數據。而對于其它類型的數據貌似無能為力,其實不然,我們可以在Intent/Bundle的API中看到Intent/Bundle還可以傳遞Parcelable(包裹化,郵包)和Serializable(序列化)類型的數據,以及它們的數組/列表數據。

所以要讓非基本類型和非String/CharSequence類型的數據通過Intent/Bundle來進行傳輸,我們就需要在數據類型中實現Parcelable接口或是Serializable接口。

http://blog.csdn.net/kkk0526/article/details/7214247

Fragment生命周期

image

注意和Activity的相比的區別,按照執行順序

  • onAttach(),onDetach()
  • onCreateView(),onDestroyView()

Service的兩種啟動方法,有什么區別
1.在Context中通過public boolean bindService(Intent service,ServiceConnection conn,int flags) 方法來進行Service與Context的關聯并啟動,并且Service的生命周期依附于Context(不求同時同分同秒生!但求同時同分同秒屎!!)。

2.通過public ComponentName startService(Intent service)方法去啟動一個Service,此時Service的生命周期與啟動它的Context無關。

3.要注意的是,whatever,都需要在xml里注冊你的Service,就像這樣:

<service
        android:name=".packnameName.youServiceName"
        android:enabled="true" />

廣播(Boardcast Receiver)的兩種動態注冊和靜態注冊有什么區別。

  • 靜態注冊:在AndroidManifest.xml文件中進行注冊,當App退出后,Receiver仍然可以接收到廣播并且進行相應的處理
  • 動態注冊:在代碼中動態注冊,當App退出后,也就沒辦法再接受廣播了

ContentProvider使用方法

http://blog.csdn.net/juetion/article/details/17481039


目前能否保證service不被殺死

Service設置成START_STICKY

  • kill 后會被重啟(等待5秒左右),重傳Intent,保持與重啟前一樣

提升service優先級

  • 在AndroidManifest.xml文件中對于intent-filter可以通過android:priority = "1000"這個屬性設置最高優先級,1000是最高值,如果數字越小則優先級越低,同時適用于廣播
  • 【結論】目前看來,priority這個屬性貌似只適用于broadcast,對于Service來說可能無效

提升service進程優先級

  • Android中的進程是托管的,當系統進程空間緊張的時候,會依照優先級自動進行進程的回收
  • 當service運行在低內存的環境時,將會kill掉一些存在的進程。因此進程的優先級將會很重要,可以在startForeground()使用startForeground()將service放到前臺狀態。這樣在低內存時被kill的幾率會低一些。
  • 【結論】如果在極度極度低內存的壓力下,該service還是會被kill掉,并且不一定會restart()

onDestroy方法里重啟service

  • service +broadcast 方式,就是當service走ondestory()的時候,發送一個自定義的廣播,當收到廣播的時候,重新啟動service
  • 也可以直接在onDestroy()里startService
  • 【結論】當使用類似口口管家等第三方應用或是在setting里-應用-強制停止時,APP進程可能就直接被干掉了,onDestroy方法都進不來,所以還是無法保證

監聽系統廣播判斷Service狀態

  • 通過系統的一些廣播,比如:手機重啟、界面喚醒、應用狀態改變等等監聽并捕獲到,然后判斷我們的Service是否還存活,別忘記加權限
  • 【結論】這也能算是一種措施,不過感覺監聽多了會導致Service很混亂,帶來諸多不便

在JNI層,用C代碼fork一個進程出來

  • 這樣產生的進程,會被系統認為是兩個不同的進程.但是Android5.0之后可能不行

root之后放到system/app變成系統級應用

大招: 放一個像素在前臺(手機QQ)


動畫有哪兩類,各有什么特點?三種動畫的區別

  • tween 補間動畫。通過指定View的初末狀態和變化時間、方式,對View的內容完成一系列的圖形變換來實現動畫效果。
    Alpha
    Scale
    Translate
    Rotate。

  • frame 幀動畫
    AnimationDrawable 控制
    animation-list xml布局

  • PropertyAnimation 屬性動畫


Android的數據存儲形式。

  • SQLite:SQLite是一個輕量級的數據庫,支持基本的SQL語法,是常被采用的一種數據存儲方式。
    Android為此數據庫提供了一個名為SQLiteDatabase的類,封裝了一些操作數據庫的api

  • SharedPreference: 除SQLite數據庫外,另一種常用的數據存儲方式,其本質就是一個xml文件,常用于存儲較簡單的參數設置。

  • File: 即常說的文件(I/O)存儲方法,常用語存儲大數量的數據,但是缺點是更新數據將是一件困難的事情。

  • ContentProvider: Android系統中能實現所有應用程序共享的一種數據存儲方式,由于數據通常在各應用間的是互相私密的,所以此存儲方式較少使用,但是其又是必不可少的一種存儲方式。例如音頻,視頻,圖片和通訊錄,一般都可以采用此種方式進行存儲。每個Content Provider都會對外提供一個公共的URI(包裝成Uri對象),如果應用程序有數據需要共享時,就需要使用Content Provider為這些數據定義一個URI,然后其他的應用程序就通過Content Provider傳入這個URI來對數據進行操作。


Sqlite的基本操作。

http://blog.csdn.net/zgljl2012/article/details/44769043


如何判斷應用被強殺

在Applicatio中定義一個static常量,賦值為-1,在歡迎界面改為0,如果被強殺,application重新初始化,在父類Activity判斷該常量的值。

應用被強殺如何解決

如果在每一個Activity的onCreate里判斷是否被強殺,冗余了,封裝到Activity的父類中,如果被強殺,跳轉回主界面,如果沒有被強殺,執行Activity的初始化操作,給主界面傳遞intent參數,主界面會調用onNewIntent方法,在onNewIntent跳轉到歡迎頁面,重新來一遍流程。

Json有什么優劣勢。

怎樣退出終止App

Asset目錄與res目錄的區別。

Android怎么加速啟動Activity。

Android內存優化方法:ListView優化,及時關閉資源,圖片緩存等等。

Android中弱引用與軟引用的應用場景。

Bitmap的四種屬性,與每種屬性隊形的大小。

View與View Group分類。自定義View過程:onMeasure()、onLayout()、onDraw()。

如何自定義控件:

  1. 自定義屬性的聲明和獲取

    • 分析需要的自定義屬性
    • 在res/values/attrs.xml定義聲明
    • 在layout文件中進行使用
    • 在View的構造方法中進行獲取
  2. 測量onMeasure

  3. 布局onLayout(ViewGroup)

  4. 繪制onDraw

  5. onTouchEvent

  6. onInterceptTouchEvent(ViewGroup)

  7. 狀態的恢復與保存

Android長連接,怎么處理心跳機制。


View樹繪制流程


下拉刷新實現原理


你用過什么框架,是否看過源碼,是否知道底層原理。

Retrofit

EventBus

glide


Android5.0、6.0新特性。

Android5.0新特性:

  • MaterialDesign設計風格
  • 支持多種設備
  • 支持64位ART虛擬機

Android6.0新特性

  • 大量漂亮流暢的動畫
  • 支持快速充電的切換
  • 支持文件夾拖拽應用
  • 相機新增專業模式

Android7.0新特性

  • 分屏多任務
  • 增強的Java8語言模式
  • 夜間模式

Context區別

  • Activity和Service以及Application的Context是不一樣的,Activity繼承自ContextThemeWraper.其他的繼承自ContextWrapper
  • 每一個Activity和Service以及Application的Context都是一個新的ContextImpl對象
  • getApplication()用來獲取Application實例的,但是這個方法只有在Activity和Service中才能調用的到。那么也許在絕大多數情況下我們都是在Activity或者Service中使用Application的,但是如果在一些其它的場景,比如BroadcastReceiver中也想獲得Application的實例,這時就可以借助getApplicationContext()方法,getApplicationContext()比getApplication()方法的作用域會更廣一些,任何一個Context的實例,只要調用getApplicationContext()方法都可以拿到我們的Application對象。
  • Activity在創建的時候會new一個ContextImpl對象并在attach方法中關聯它,Application和Service也差不多。ContextWrapper的方法內部都是轉調ContextImpl的方法
  • 創建對話框傳入Application的Context是不可以的
  • 盡管Application、Activity、Service都有自己的ContextImpl,并且每個ContextImpl都有自己的mResources成員,但是由于它們的mResources成員都來自于唯一的ResourcesManager實例,所以它們看似不同的mResources其實都指向的是同一塊內存
  • Context的數量等于Activity的個數 + Service的個數 + 1,這個1為Application

IntentService的使用場景與特點。

IntentService是Service的子類,是一個異步的,會自動停止的服務,很好解決了傳統的Service中處理完耗時操作忘記停止并銷毀Service的問題

優點:

  • 一方面不需要自己去new Thread
  • 另一方面不需要考慮在什么時候關閉該Service

onStartCommand中回調了onStart,onStart中通過mServiceHandler發送消息到該handler的handleMessage中去。最后handleMessage中回調onHandleIntent(intent)。


圖片緩存

查看每個應用程序最高可用內存:

    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
    Log.d("TAG", "Max memory is " + maxMemory + "KB");  

Gradle

構建工具、Groovy語法、Java

Jar包里面只有代碼,aar里面不光有代碼還包括


你是如何自學Android

首先是看書和看視頻敲代碼,然后看大牛的博客,做一些項目,向github提交代碼,覺得自己API掌握的不錯之后,開始看進階的書,以及看源碼,看完源碼學習到一些思想,開始自己造輪子,開始想代碼的提升,比如設計模式,架構,重構等。


?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,818評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,185評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,656評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,647評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,446評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,951評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,041評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,189評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,718評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,602評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,800評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,316評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,045評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,419評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,671評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,420評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,755評論 2 371

推薦閱讀更多精彩內容