藍牙初步認識--Google官網文檔

藍牙

Android 平臺包含藍牙網絡堆棧支持骑脱,憑借此項支持,設備能以無線方式與其他藍牙設備交換數據袭异。應用框架提供了通過 Android Bluetooth API 訪問藍牙功能的途徑焙糟。 這些 API 允許應用以無線方式連接到其他藍牙設備,從而實現(xiàn)點到點和多點無線功能晾捏。

使用 Bluetooth API蒿涎,Android 應用可執(zhí)行以下操作:

  • 掃描其他藍牙設備
  • 查詢本地藍牙適配器的配對藍牙設備
  • 建立 RFCOMM 通道
  • 通過服務發(fā)現(xiàn)連接到其他設備
  • 與其他設備進行雙向數據傳輸
  • 管理多個連接

本文將介紹如何使用傳統(tǒng)藍牙。傳統(tǒng)藍牙適用于電池使用強度較大的操作惦辛,例如 Android 設備之間的流式傳輸和通信等劳秋。 針對具有低功耗要求的藍牙設備,Android 4.3(API 級別 18)中引入了面向低功耗藍牙的 API 支持。 如需了解更多信息玻淑,請參閱低功耗藍牙嗽冒。

基礎知識

本文將介紹如何使用 Android Bluetooth API 來完成使用藍牙進行通信的四項主要任務:設置藍牙、查找局部區(qū)域內的配對設備或可用設備补履、連接設備添坊,以及在設備之間傳輸數據。

本文將介紹如何使用 Android Bluetooth API 來完成使用藍牙進行通信的四項主要任務:設置藍牙箫锤、查找局部區(qū)域內的配對設備或可用設備贬蛙、連接設備,以及在設備之間傳輸數據麻汰。

android.bluetooth包中提供了所有 Bluetooth API速客。 下面概要列出了創(chuàng)建藍牙連接所需的類和接口:

BluetoothAdapter

表示本地藍牙適配器(藍牙無線電)戚篙。這 BluetoothAdapter是所有藍牙互動的入門點五鲫。使用此功能,您可以發(fā)現(xiàn)其他藍牙設備岔擂,查詢已綁定(配對)設備的列表位喂,BluetoothDevice使用已知的MAC地址實例化,并創(chuàng)建一個BluetoothServerSocket監(jiān)聽來自其他設備的通信乱灵。

BluetoothDevice

表示遠程藍牙設備塑崖。使用此方法通過BluetoothSocket關于設備的或查詢信息(如其名稱,地址痛倚,類別和綁定狀態(tài))來請求與遠程設備的連接规婆。

BluetoothSocket

表示藍牙插座的接口(類似于TCP Socket)。這是允許應用程序通過InputStream和OutputStream與另一個藍牙設備交換數據的連接點蝉稳。

BluetoothServerSocket

表示用于偵聽傳入請求(類似于TCP ServerSocket)的打開的服務器套接字抒蚜。為了連接兩個Android設備,一個設備必須打開這個類的服務器套接字耘戚。當遠程藍牙設備向該設備發(fā)出連接請求時嗡髓,當接受BluetoothServerSocket連接BluetoothSocket時, 將返回連接收津。

BluetoothClass

描述藍牙設備的一般特性和功能饿这。這是一組只讀屬性,用于定義設備的主要和次要設備類及其服務撞秋。但是长捧,這不能可靠地描述設備支持的所有藍牙配置文件和服務,但對設備類型的提示很有用吻贿。

BluetoothProfile

表示藍牙配置文件的界面唆姐。甲藍牙配置文件是用于在設備之間基于藍牙的通信的無線接口規(guī)范。一個例子是免提配置文件。有關配置文件的更多討論奉芦,請參閱使用配置文件

BluetoothHeadset

支持藍牙耳機與手機配合使用赵抢。這包括藍牙耳機和免提(v1.5)配置文件。

BluetoothA2dp

定義通過藍牙連接將高質量的音頻流從一個設備傳輸到另一個設備声功》橙矗“A2DP”表示高級音頻分配配置文件。

BluetoothHealth

表示控制藍牙服務的運行狀況設備配置文件代理先巴。

BluetoothHealthCallback

用于實現(xiàn)BluetoothHealth回調的抽象類其爵。您必須擴展此類并實現(xiàn)回調方法以接收有關應用程序注冊狀態(tài)和藍牙通道狀態(tài)更改的更新。

BluetoothHealthAppConfiguration

表示藍牙健康第三方應用程序注冊以與遠程藍牙健康設備進行通信的應用程序配置伸蚯。

BluetoothProfile.ServiceListener

BluetoothProfile當IPC客戶端連接到服務器或與服務斷開連接(即運行特定配置文件的內部服務器)時摩渺,可以通知IPC客戶端。

藍牙權限

要在應用中使用藍牙功能剂邮,必須聲明藍牙權限BLUETOOTH摇幻,您需要此權限才能執(zhí)行任何藍牙通信,例如請求連接挥萌、接受連接和傳輸數據等绰姻。

如果您希望您的應用啟動設備發(fā)現(xiàn)或操作藍牙設置,則還必須聲明 BLUETOOTH_ADMIN 權限引瀑。 大多數應用需要此權限僅僅為了能夠發(fā)現(xiàn)本地藍牙設備狂芋。 除非該應用是將要應用戶請求修改藍牙設置的“超級管理員”,否則不應使用此權限所授予的其他能力憨栽。

Android6.0及以上需要位置權限才能掃描到設備帜矾;位置權限是危險權限,需要

    <uses-permission android:name="android.permission.BLUETOOTH_ADMIN"></uses-permission>
    <uses-permission android:name="android.permission.BLUETOOTH"></uses-permission>

    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>

設置藍牙

  1. 獲取 BluetoothAdapter
    將BluetoothAdapter所需的任何和所有的藍牙活動屑柔。要獲取BluetoothAdapter屡萤,請調用靜態(tài)getDefaultAdapter()方法。這將返回一個 BluetoothAdapter表示設備自己的藍牙適配器(藍牙無線電)的锯蛀。整個系統(tǒng)有一個藍牙適配器灭衷,您的應用程序可以使用此對象與其進行交互。如果 getDefaultAdapter()返回null旁涤,則設備不支持藍牙翔曲,您的操作將在此結束。例如:
BluetoothAdapter mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
    // Device does not support Bluetooth
}

2.啟用藍牙
接下來劈愚,您需要確保啟用藍牙瞳遍。調用isEnabled()檢查藍牙是否當前啟用。如果此方法返回false菌羽,則藍牙被禁用掠械。要請求啟用藍牙,請startActivityForResult() 使用ACTION_REQUEST_ENABLEIntent操作調用。這將發(fā)出通過系統(tǒng)設置啟用藍牙的請求(不停止您的應用程序)猾蒂。例如:

if (!mBluetoothAdapter.isEnabled()) {
    Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
    startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}

查找設備

使用該功能BluetoothAdapter均唉,您可以通過設備發(fā)現(xiàn)或通過查詢配對(綁定)設備列表來查找遠程藍牙設備。

設備發(fā)現(xiàn)是一種掃描過程肚菠,可以在本地搜索藍牙設備舔箭,然后請求一些關于每個設備的信息(這有時被稱為“發(fā)現(xiàn)”,“查詢”或“掃描”)蚊逢。但是层扶,本地區(qū)內的藍牙設備只有在當前啟用才能發(fā)現(xiàn)的情況下才能響應發(fā)現(xiàn)請求。如果設備是可發(fā)現(xiàn)的烙荷,它將通過共享一些信息來響應發(fā)現(xiàn)請求镜会,例如設備名稱,類別及其唯一的MAC地址终抽。使用此信息戳表,執(zhí)行發(fā)現(xiàn)的設備隨后可以選擇啟動與發(fā)現(xiàn)的設備的連接。

一旦與第一次使用遠程設備進行連接拿诸,配對請求將自動呈現(xiàn)給用戶扒袖。當設備配對時塞茅,將保存有關該設備的基本信息(如設備名稱亩码,類和MAC地址),并使用藍牙API進行讀取野瘦。使用已知的MAC地址進行遠程設備描沟,可以在任何時間啟動連接,而無需執(zhí)行發(fā)現(xiàn)(假定設備在范圍內)鞭光。

記住配對和連接之間有區(qū)別吏廉。要配對意味著兩個設備都知道彼此的存在,具有可以用于認證的共享鏈路密鑰惰许,并且能夠建立彼此的加密連接席覆。要連接意味著設備當前共享RFCOMM信道,并且能夠彼此傳輸數據汹买。在建立RFCOMM連接之前佩伤,目前的Android藍牙API需要配對設備。(當您使用藍牙API啟動加密連接時晦毙,會自動執(zhí)行配對生巡。)

以下部分介紹如何查找已配對的設備,或使用設備發(fā)現(xiàn)來發(fā)現(xiàn)新設備见妒。

查找配對的設備

在執(zhí)行設備發(fā)現(xiàn)之前孤荣,它值得查詢一組配對的設備,以查看所需設備是否已知。要這樣做盐股,打電話getBondedDevices()钱豁。這將返回一組BluetoothDevice代表配對的設備。例如疯汁,您可以查詢所有配對的設備寥院,然后使用ArrayAdapter向用戶顯示每個設備的名稱:

Set<BluetoothDevice> pairedDevices = mBluetoothAdapter.getBondedDevices();
// If there are paired devices
if (pairedDevices.size() > 0) {
    // Loop through paired devices
    for (BluetoothDevice device : pairedDevices) {
        // Add the name and address to an array adapter to show in a ListView
        mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
    }
}

發(fā)現(xiàn)設備

要開始發(fā)現(xiàn)設備,只需調用startDiscovery()涛目。該進程是異步的秸谢,該方法將立即返回一個布爾值,指示發(fā)現(xiàn)是否已成功啟動霹肝。發(fā)現(xiàn)過程通常涉及大約12秒的查詢掃描估蹄,隨后是每個找到的設備的頁面掃描以檢索其藍牙名稱。

你的應用程序必須登記為action_found意圖接收有關每個設備發(fā)現(xiàn)BroadcastReceiver沫换。對每一個設備臭蚁,系統(tǒng)將播出action_found意圖。這種意圖進行額外的領域extra_device和extra_class讯赏,包含一個藍牙設備和藍牙類垮兑,分別。例如漱挎,在這里的你如何登記辦理廣播設備時發(fā)現(xiàn)的:

// Create a BroadcastReceiver for ACTION_FOUND
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
    public void onReceive(Context context, Intent intent) {
        String action = intent.getAction();
        // When discovery finds a device
        if (BluetoothDevice.ACTION_FOUND.equals(action)) {
            // Get the BluetoothDevice object from the Intent
            BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
            // Add the name and address to an array adapter to show in a ListView
            mArrayAdapter.add(device.getName() + "\n" + device.getAddress());
        }
    }
};
// Register the BroadcastReceiver
IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
registerReceiver(mReceiver, filter); // Don't forget to unregister during onDestroy

連接設備

為了在兩個設備之間創(chuàng)建應用程序之間的連接系枪,您必須同時實現(xiàn)服務器端和客戶端機制,因為一個設備必須打開一個服務器套接字磕谅,另一個設備必須啟動連接(使用服務器設備的MAC地址啟動連接)私爷。當服務器和客戶端都BluetoothSocket在相同的RFCOMM通道上連接時,服務器和客戶端被認為是相互連接 的膊夹。此時衬浑,每個設備可以獲取輸入和輸出流,并且可以開始數據傳輸放刨,這將在“ 管理連接 ”一節(jié)中討論工秩。本節(jié)介紹如何啟動兩臺設備之間的連接。

服務器設備和客戶端設備都BluetoothSocket以不同的方式獲得所需的設備进统。當接收到連接時助币,服務器將收到該消息。當客戶端向服務器打開RFCOMM通道時麻昼,客戶端將收到該消息奠支。

一種實現(xiàn)技術是將每個設備自動準備為服務器,以便每個設備都有一個服務器套接字打開并監(jiān)聽連接抚芦。那么任一設備都可以啟動與其他設備的連接并成為客戶端倍谜÷趺或者,一個設備可以顯式地“主持”連接并按需打開服務器套接字尔崔,而另一個設備可以簡單地啟動連接答毫。

連接為服務器

當您要連接兩個設備時,必須通過持續(xù)打開來充當服務器BluetoothServerSocket季春。服務器套接字的目的是監(jiān)聽傳入的連接請求洗搂,并且當被接受時,提供連接BluetoothSocket载弄。當從BluetoothServerSocket獲取BluetoothSocket時耘拇,BluetoothServerSocket可以(應該)被丟棄,除非你想接受更多的連接宇攻。

以下是設置服務器套接字并接受連接的基本步驟:

  1. 獲得BluetoothServerSocket通過調用 listenUsingRfcommWithServiceRecord(String, UUID)惫叛。
    該字符串是您的服務的可標識名稱,系統(tǒng)將自動寫入設備上的新服務發(fā)現(xiàn)協(xié)議(SDP)數據庫條目(名稱是任意的逞刷,可以僅僅是您的應用程序名稱)嘉涌。UUID也包含在SDP條目中,并將作為與客戶端設備的連接協(xié)議的基礎夸浅。也就是說仑最,當客戶端嘗試與此設備連接時,它將攜帶唯一標識要連接的服務的UUID帆喇。這些UUID必須匹配才能接受連接(在下一步中)警医。
  2. 通過調用開始監(jiān)聽連接請求 accept()。
    這是一個阻塞調用番枚。當連接被接受或發(fā)生異常時法严,它將返回损敷。僅當遠程設備發(fā)送了一個與該偵聽服務器套接字注冊的UUID相匹配的UUID的連接請求時才接受連接葫笼。當成功時,accept()將返回一個已連接BluetoothSocket拗馒。
  3. 除非你想接受額外的連接路星,請打電話 close()。
    這將釋放服務器socket和它的所有資源诱桂,但并沒有關閉連接的BluetoothSocket一個已經被退回accept()洋丐。不像TCP / IP,RFCOMM只允許每個信道的一個連接的客戶端的時間挥等,所以在大多數情況下是有意義的調用close()在BluetoothServerSocket接受連接的套接字之后友绝。

accept()調用不應在主 Activity UI 線程中執(zhí)行,因為它是阻塞調用肝劲,并會阻止與應用的任何其他交互迁客。 在您的應用所管理的新線程中使用 BluetoothServerSocket 或 BluetoothSocket完成所有工作郭宝,這通常是一種行之有效的做法。 要終止 accept() 等被阻塞的調用掷漱,請通過另一個線程在 BluetoothServerSocket或 BluetoothSocket上調用 close()粘室,被阻塞的調用將會立即返回。 請注意卜范,BluetoothServerSocket或 BluetoothSocket 中的所有方法都是線程安全的方法衔统。

private class AcceptThread extends Thread {
    private final BluetoothServerSocket mmServerSocket;

    public AcceptThread() {
        // Use a temporary object that is later assigned to mmServerSocket,
        // because mmServerSocket is final
        BluetoothServerSocket tmp = null;
        try {
            // MY_UUID is the app's UUID string, also used by the client code
            tmp = mBluetoothAdapter.listenUsingRfcommWithServiceRecord(NAME, MY_UUID);
        } catch (IOException e) { }
        mmServerSocket = tmp;
    }

    public void run() {
        BluetoothSocket socket = null;
        // Keep listening until exception occurs or a socket is returned
        while (true) {
            try {
                socket = mmServerSocket.accept();
            } catch (IOException e) {
                break;
            }
            // If a connection was accepted
            if (socket != null) {
                // Do work to manage the connection (in a separate thread)
                manageConnectedSocket(socket);
                mmServerSocket.close();
                break;
            }
        }
    }

    /** Will cancel the listening socket, and cause the thread to finish */
    public void cancel() {
        try {
            mmServerSocket.close();
        } catch (IOException e) { }
    }
}

連接為客戶端

為了啟動與遠程設備(持有打開服務器套接字的設備)的連接,您必須首先獲取BluetoothDevice表示遠程設備的對象海雪。(BluetoothDevice有關查找設備的上述部分將介紹以下部分)锦爵。然后,您必須使用它 BluetoothDevice來獲取BluetoothSocket并啟動連接奥裸。

這是基本的過程:

使用BluetoothDevice棉浸,BluetoothSocket通過調用得到一個createRfcommSocketToServiceRecord(UUID)。
這將初始化一個BluetoothSocket將連接到的BluetoothDevice刺彩。在此處傳遞的UUID必須與服務器設備打開BluetoothServerSocket(使用listenUsingRfcommWithServiceRecord(String, UUID))時使用的UUID相匹配 迷郑。使用相同的UUID只是將UUID字符串硬編碼到應用程序中,然后從服務器和客戶端代碼引用它创倔。
通過調用啟動連接connect()嗡害。
在此呼叫之后,系統(tǒng)將在遠程設備上執(zhí)行SDP查找畦攘,以匹配UUID霸妹。如果查找成功并且遠程設備接受連接,則它將共享在連接期間使用的RFCOMM通道并connect()返回知押。這種方法是一個阻塞調用叹螟。如果出于任何原因,連接失敗或connect()方法超時(約12秒鐘后)台盯,則會引發(fā)異常罢绽。
因為connect()是一個阻塞調用,這個連接過程應該總是在與主活動線程分開的線程中執(zhí)行静盅。


private class ConnectThread extends Thread {
    private final BluetoothSocket mmSocket;
    private final BluetoothDevice mmDevice;

    public ConnectThread(BluetoothDevice device) {
        // Use a temporary object that is later assigned to mmSocket,
        // because mmSocket is final
        BluetoothSocket tmp = null;
        mmDevice = device;

        // Get a BluetoothSocket to connect with the given BluetoothDevice
        try {
            // MY_UUID is the app's UUID string, also used by the server code
            tmp = device.createRfcommSocketToServiceRecord(MY_UUID);
        } catch (IOException e) { }
        mmSocket = tmp;
    }

    public void run() {
        // Cancel discovery because it will slow down the connection
        mBluetoothAdapter.cancelDiscovery();

        try {
            // Connect the device through the socket. This will block
            // until it succeeds or throws an exception
            mmSocket.connect();
        } catch (IOException connectException) {
            // Unable to connect; close the socket and get out
            try {
                mmSocket.close();
            } catch (IOException closeException) { }
            return;
        }

        // Do work to manage the connection (in a separate thread)
        manageConnectedSocket(mmSocket);
    }

    /** Will cancel an in-progress connection, and close the socket */
    public void cancel() {
        try {
            mmSocket.close();
        } catch (IOException e) { }
    }
}
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末良价,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子蒿叠,更是在濱河造成了極大的恐慌明垢,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件市咽,死亡現(xiàn)場離奇詭異痊银,居然都是意外死亡,警方通過查閱死者的電腦和手機施绎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門溯革,熙熙樓的掌柜王于貴愁眉苦臉地迎上來泌射,“玉大人,你說我怎么就攤上這事鬓照∪劭幔” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵豺裆,是天一觀的道長拒秘。 經常有香客問我,道長臭猜,這世上最難降的妖魔是什么躺酒? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮蔑歌,結果婚禮上羹应,老公的妹妹穿的比我還像新娘。我一直安慰自己次屠,他們只是感情好园匹,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著劫灶,像睡著了一般裸违。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上本昏,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天供汛,我揣著相機與錄音,去河邊找鬼涌穆。 笑死怔昨,一個胖子當著我的面吹牛,可吹牛的內容都是我干的宿稀。 我是一名探鬼主播趁舀,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼原叮!你這毒婦竟也來了赫编?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤奋隶,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后悦荒,有當地人在樹林里發(fā)現(xiàn)了一具尸體唯欣,經...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年搬味,在試婚紗的時候發(fā)現(xiàn)自己被綠了境氢。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蟀拷。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖萍聊,靈堂內的尸體忽然破棺而出问芬,到底是詐尸還是另有隱情,我是刑警寧澤寿桨,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布此衅,位于F島的核電站,受9級特大地震影響亭螟,放射性物質發(fā)生泄漏挡鞍。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一预烙、第九天 我趴在偏房一處隱蔽的房頂上張望墨微。 院中可真熱鬧,春花似錦扁掸、人聲如沸翘县。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炼蹦。三九已至,卻和暖如春狸剃,著一層夾襖步出監(jiān)牢的瞬間掐隐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工钞馁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留虑省,地道東北人。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓僧凰,卻偏偏與公主長得像探颈,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子训措,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

推薦閱讀更多精彩內容

  • 藍牙 注:本文翻譯自https://developer.android.com/guide/topics/conn...
    RxCode閱讀 8,673評論 11 99
  • Android 平臺包含藍牙網絡堆棧支持伪节,憑借此項支持,設備能以無線方式與其他藍牙設備交換數據绩鸣。應用框架提供了通過...
    虎三呀閱讀 768評論 0 1
  • 公司的項目最近需要用到藍牙開發(fā)的相關內容怀大,因此特地查閱了Google官方文檔的內容并進行二次整理,希望能對需要學習...
    Chuckiefan閱讀 32,446評論 44 123
  • 最近項目使用藍牙呀闻,之前并沒有接觸化借,還是發(fā)現(xiàn)了很多坑,查閱了很多資料捡多,說的迷迷糊糊蓖康,今天特查看官方文檔铐炫。 說下遇到的...
    King9527閱讀 1,794評論 0 1
  • 好想喊一聲:媽媽 ———寫在母親節(jié)的話 媽媽,媽媽 每次我回家 就會喊一聲媽媽 我回來了 這時候 就會有一聲應答 ...
    牛你閱讀 414評論 0 0