Android之WIFI通信-P2P的discoverPeers分析

簡單概況啟動過程

1.
WifiP2pService --- > WifiP2pServiceImpl

2.WifiP2pServiceImp構(gòu)造方法
    public WifiP2pServiceImpl(Context context) {
        mContext = context;

        mNetworkInfo = new NetworkInfo(ConnectivityManager.TYPE_WIFI_P2P, 0, NETWORKTYPE, "");

        mP2pSupported = mContext.getPackageManager().hasSystemFeature(
                PackageManager.FEATURE_WIFI_DIRECT);

        mThisDevice.primaryDeviceType = mContext.getResources().getString(
                com.android.internal.R.string.config_wifi_p2p_device_type);

        HandlerThread wifiP2pThread = new HandlerThread("WifiP2pService");
        wifiP2pThread.start();
        mClientHandler = new ClientHandler(wifiP2pThread.getLooper());

        mP2pStateMachine = new P2pStateMachine(TAG, wifiP2pThread.getLooper(), mP2pSupported);
        mP2pStateMachine.start();//狀態(tài)機
    }

前言

7.1 源碼
要看懂源碼旨巷,就需要深入了解狀態(tài)機(StateMachine)和雙Handler通信(AsyncChannel)

案例
WifiP2pManager.discoverPeers
客戶端:
1.看discoverPeers代碼

    public void discoverPeers(Channel c, ActionListener listener) {
        checkChannel(c);//檢測c是否存在巨缘,如果不存在,直接拋異常處理契沫。這也說明了带猴,channel首先被初始化的意義
        c.mAsyncChannel.sendMessage(DISCOVER_PEERS, 0, c.putListener(listener));
    }
    
   private static void checkChannel(Channel c) {
        if (c == null) throw new IllegalArgumentException("Channel needs to be initialized");
    }
    
    看wifi和藍(lán)牙代碼,必定涉及StateMachine懈万,同時也會涉及Handler跨進程通信(AsyncChannel)

2.c.mAsyncChannel

mAsyncChannel是Handler跨進程通信的關(guān)鍵類拴清。也就是說,三方應(yīng)用和WifiP2pService服務(wù)進行跨進程通信的實現(xiàn)方式就是采用Handler会通。了解了這些口予,則明白為什么要initialize,并且要傳入Looper對象

public Channel initialize(Context srcContext, Looper srcLooper, ChannelListener listener)

3.c.putListener(listener)

這種回調(diào)方法的處理方式涕侈,確實給人眼前一亮

        private int putListener(Object listener) {//返回值為key
            if (listener == null) return INVALID_LISTENER_KEY;
            int key;
            synchronized (mListenerMapLock) {
                do {
                    key = mListenerKey++;
                } while (key == INVALID_LISTENER_KEY);
                mListenerMap.put(key, listener);//把listener放在map中沪停。
            }
            return key;
        }
        //private HashMap<Integer, Object> mListenerMap = new HashMap<Integer, Object>();
也就是說,在Handler通信中裳涛,把listener轉(zhuǎn)化成arg2發(fā)送出去

4.sticky broadcast的使用:
系統(tǒng)發(fā)送sticky 廣播后木张,app才注冊此廣播,是否可以接收到廣播端三?
答案:能舷礼。具體原理,我們這里不分析郊闯。

    private void sendP2pStateChangedBroadcast(boolean enabled) {
        logd(Debug.getCallers(5));
        final Intent intent = new Intent(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
        if (enabled) {
            intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
                    WifiP2pManager.WIFI_P2P_STATE_ENABLED);
        } else {
            intent.putExtra(WifiP2pManager.EXTRA_WIFI_STATE,
                    WifiP2pManager.WIFI_P2P_STATE_DISABLED);
        }
        mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
    }
    
    這也就是為什么妻献,我們注冊:WIFI_P2P_THIS_DEVICE_CHANGED_ACTION蛛株、WIFI_P2P_DISCOVERY_CHANGED_ACTION、WIFI_P2P_STATE_CHANGED_ACTION育拨、WIFI_P2P_CONNECTION_CHANGED_ACTION谨履,啟動app之后,會立馬接收到的原因

系統(tǒng)服務(wù)
1.WifiP2pServiceImpl

先要回答一個問題:WifiP2pServiceImpl中的狀態(tài)機熬丧,在客戶端調(diào)用之前笋粟,已經(jīng)處于什么狀態(tài)?
如果wifi是打開的析蝴,則處于InactiveState矗钟。
具體原因:
先說思路
1)WifiP2pServiceImpl和WifiStateMachine是有聯(lián)動的,也是通過handler通信
a.WifiStateMachine中的mWifiP2pChannel為聯(lián)動的變量
b.WifiStateMachine中的mP2pSupported為關(guān)鍵變量

2)WifiP2pServiceImpl和WifiMonitor是有聯(lián)動的嫌变,在構(gòu)造方法中
WifiP2pServiceImpl.P2pStateMachine構(gòu)造方法中使用如下:
    mWifiMonitor.registerHandler(interfaceName,
        WifiMonitor.AP_STA_CONNECTED_EVENT, getHandler());

2.回到狀態(tài)機

InactiveState沒有處理消息DISCOVER_PEERS,傳給了父狀態(tài)P2pEnabledState躬它,關(guān)注核心點:
                case WifiP2pManager.DISCOVER_PEERS:
                    if (mDiscoveryBlocked) {
                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                                WifiP2pManager.BUSY);
                        break;
                    }
                    // do not send service discovery request while normal find operation.
                    clearSupplicantServiceRequest();
                    if (mWifiNative.p2pFind(DISCOVER_TIMEOUT_S)) {//調(diào)用jni腾啥。注意:通過WifiMonitor回復(fù)WifiMonitor.P2P_DEVICE_FOUND_EVENT
                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_SUCCEEDED);//客戶端調(diào)用的回調(diào)回復(fù)
                        sendP2pDiscoveryChangedBroadcast(true);//發(fā)送廣播,開始掃描發(fā)現(xiàn)對等設(shè)備
                    } else {
                        replyToMessage(message, WifiP2pManager.DISCOVER_PEERS_FAILED,
                                WifiP2pManager.ERROR);
                    }
                    break;


                case WifiMonitor.P2P_DEVICE_FOUND_EVENT://執(zhí)行jni層冯吓,底層給的回復(fù)
                    WifiP2pDevice device = (WifiP2pDevice) message.obj;
                    if (mThisDevice.deviceAddress.equals(device.deviceAddress)) break;
                    mPeers.updateSupplicantDetails(device);
                    sendPeersChangedBroadcast();//發(fā)送廣播倘待,帶有掃描到的設(shè)備

總結(jié)

1.discoverPeers(Channel c, ActionListener listener)的回調(diào)僅僅是判斷是否jni執(zhí)行成功
2.執(zhí)行成功,涉及需要進一步掃描設(shè)備组贺,所以通過廣播把數(shù)據(jù)報告出來凸舵,而不是通過回調(diào)
總之,回調(diào)的是執(zhí)行狀態(tài)失尖,結(jié)果通過廣播通知

案例
connect

android.net.wifi.p2p.PEERS_CHANGED
android.net.wifi.p2p.DISCOVERY_STATE_CHANGE
android.net.wifi.p2p.PEERS_CHANGED
android.net.wifi.p2p.THIS_DEVICE_CHANGED
android.net.wifi.p2p.CONNECTION_STATE_CHANGE
android.net.wifi.p2p.PEERS_CHANGED

額外發(fā)現(xiàn)

1.日志格式
        if (DBG) log("halfConnectSync srcHandler to the dstMessenger  E");

        // We are connected
        connected(srcContext, srcHandler, dstMessenger);

        if (DBG) log("halfConnectSync srcHandler to the dstMessenger X");
        
        為什么用E X
        想一下execute這個單詞
        
2.WifiP2pServiceImp和WifiStateMachine關(guān)聯(lián)啊奄?
兩者之間也通過雙handler通信

05-19 13:12:35.216   414   414 I WifiP2pService: Registering wifip2p
05-19 13:12:35.216   414   486 D WifiP2pService: P2pDisabledState
05-19 13:12:35.243   414   486 D WifiP2pService: P2pDisabledState what 69633
05-19 13:12:35.243   414   486 D WifiP2pService: DefaultState what 69633
05-19 13:12:35.243   414   486 D WifiP2pService: P2pDisabledState what 69632
05-19 13:12:35.243   414   486 D WifiP2pService: DefaultState what 69632
05-19 13:12:35.243   414   486 D WifiP2pService: Full connection with WifiStateMachine established
05-19 13:12:38.160   414   486 D WifiP2pService: P2pDisabledState what 131203 //CMD_ENABLE_P2P
05-19 13:12:38.177   414   486 E WifiP2pService: Unable to change interface settings: java.lang.IllegalStateException: command '25 interface setcfg p2p0 0.0.0.0 0 up' failed with '400 25 Failed to clear address (No such device)'
05-19 13:12:38.177   414   486 D WifiP2pService: P2pEnablingState
05-19 13:12:38.177   414   486 D WifiP2pService: P2pEnablingState what 147457  //SUP_CONNECTION_EVENT
05-19 13:12:38.177   414   486 D WifiP2pService: P2p socket connection successful

問題

1.mWifiP2pManager.listen用來做什么?

2.mWifiP2pManager.createGroup 怎么使用掀潮?

3.mWifiP2pManager.setDeviceName  是否可以默認(rèn)菇夸? -- 可以,更改SettingsProvider即可

    private String getPersistedDeviceName() {
        String deviceName = Settings.Global.getString(mContext.getContentResolver(),
                Settings.Global.WIFI_P2P_DEVICE_NAME);
        if (deviceName == null) {
            /* We use the 4 digits of the ANDROID_ID to have a friendly
             * default that has low likelihood of collision with a peer */
            String id = Settings.Secure.getString(mContext.getContentResolver(),
                    Settings.Secure.ANDROID_ID);
            return "Android_" + id.substring(0,4);
        }
        return deviceName;
    }

    private boolean setAndPersistDeviceName(String devName) {
        if (devName == null) return false;

        if (!mWifiNative.setDeviceName(devName)) {
            loge("Failed to set device name " + devName);
            return false;
        }

        mThisDevice.deviceName = devName;
        mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);

        Settings.Global.putString(mContext.getContentResolver(),
                Settings.Global.WIFI_P2P_DEVICE_NAME, devName);
        sendThisDeviceChangedBroadcast();
        return true;
    }
    
    第一次初始化
    private void initializeP2pSettings() {
        mWifiNative.setPersistentReconnect(true);
        mThisDevice.deviceName = getPersistedDeviceName();//初始化
        mWifiNative.setDeviceName(mThisDevice.deviceName);//設(shè)置1
        // DIRECT-XY-DEVICENAME (XY is randomly generated)
        mWifiNative.setP2pSsidPostfix("-" + mThisDevice.deviceName);//設(shè)置2
        mWifiNative.setDeviceType(mThisDevice.primaryDeviceType);
        // Supplicant defaults to using virtual display with display
        // which refers to a remote display. Use physical_display
        mWifiNative.setConfigMethods("virtual_push_button physical_display keypad");
        // STA has higher priority over P2P
        mWifiNative.setConcurrencyPriority("sta");

        mThisDevice.deviceAddress = mWifiNative.p2pGetDeviceAddress();
        updateThisDevice(WifiP2pDevice.AVAILABLE);//廣播更新狀態(tài)
        if (DBG) logd("DeviceAddress: " + mThisDevice.deviceAddress);

        mClientInfoList.clear();
        mWifiNative.p2pFlush();
        mWifiNative.p2pServiceFlush();
        mServiceTransactionId = 0;
        mServiceDiscReqId = null;

        updatePersistentNetworks(RELOAD);
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末仪吧,一起剝皮案震驚了整個濱河市庄新,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌薯鼠,老刑警劉巖择诈,帶你破解...
    沈念sama閱讀 218,525評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異出皇,居然都是意外死亡羞芍,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,203評論 3 395
  • 文/潘曉璐 我一進店門恶迈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涩金,“玉大人谱醇,你說我怎么就攤上這事〔阶觯” “怎么了副渴?”我有些...
    開封第一講書人閱讀 164,862評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長全度。 經(jīng)常有香客問我煮剧,道長,這世上最難降的妖魔是什么将鸵? 我笑而不...
    開封第一講書人閱讀 58,728評論 1 294
  • 正文 為了忘掉前任勉盅,我火速辦了婚禮,結(jié)果婚禮上顶掉,老公的妹妹穿的比我還像新娘草娜。我一直安慰自己,他們只是感情好痒筒,可當(dāng)我...
    茶點故事閱讀 67,743評論 6 392
  • 文/花漫 我一把揭開白布宰闰。 她就那樣靜靜地躺著,像睡著了一般簿透。 火紅的嫁衣襯著肌膚如雪移袍。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,590評論 1 305
  • 那天老充,我揣著相機與錄音葡盗,去河邊找鬼束世。 笑死统舀,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的骡苞。 我是一名探鬼主播巷嚣,決...
    沈念sama閱讀 40,330評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼蔚约,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了涂籽?” 一聲冷哼從身側(cè)響起苹祟,我...
    開封第一講書人閱讀 39,244評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎评雌,沒想到半個月后树枫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,693評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡景东,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,885評論 3 336
  • 正文 我和宋清朗相戀三年砂轻,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斤吐。...
    茶點故事閱讀 40,001評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡搔涝,死狀恐怖厨喂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情庄呈,我是刑警寧澤蜕煌,帶...
    沈念sama閱讀 35,723評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站诬留,受9級特大地震影響斜纪,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜文兑,卻給世界環(huán)境...
    茶點故事閱讀 41,343評論 3 330
  • 文/蒙蒙 一盒刚、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧绿贞,春花似錦因块、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,919評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至寨辩,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間歼冰,已是汗流浹背靡狞。 一陣腳步聲響...
    開封第一講書人閱讀 33,042評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留隔嫡,地道東北人甸怕。 一個月前我還...
    沈念sama閱讀 48,191評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像腮恩,于是被迫代替她去往敵國和親梢杭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,955評論 2 355

推薦閱讀更多精彩內(nèi)容

  • Wifi Direct功能早在Android 4.0就以經(jīng)加入Android系統(tǒng)了秸滴,但是一直沒有很好的被支持武契,主要...
    留給時光吧閱讀 16,614評論 4 13
  • 藍(lán)牙無線技術(shù)是一種全球通用的短距離無線技術(shù),具有耗電量低荡含、成本低咒唆、安全性、穩(wěn)定性释液、易用性等優(yōu)點全释,尤其在物聯(lián)網(wǎng)設(shè)備上...
    Slowfever_490d閱讀 861評論 0 1
  • 藍(lán)牙無線技術(shù)是一種全球通用的短距離無線技術(shù),具有耗電量低误债、成本低浸船、安全性妄迁、穩(wěn)定性、易用性等優(yōu)點李命,尤其在物聯(lián)網(wǎng)設(shè)備上...
    嫑臉的大頭閱讀 824評論 0 2
  • Android面試題 Android面試題包括Android基礎(chǔ)登淘,還有一些源碼級別的、原理這些等项戴。所以想去大公司面...
    陳二狗想吃肉閱讀 4,807評論 0 21
  • 概述 介紹Wi-Fi聯(lián)盟推出的另外一項重要技術(shù)規(guī)范WiFi P2P形帮。 該規(guī)范的商品名為Wi-Fi Direct, ...
    傀儡世界閱讀 44,942評論 5 18