1. 程式人生 > >WifiDirect (WIFIP2P) 最全最詳細,應用於智慧硬體(智慧家居,車機,無人機)等。

WifiDirect (WIFIP2P) 最全最詳細,應用於智慧硬體(智慧家居,車機,無人機)等。

前沿:好久沒更部落格了,手都快生了。不過但凡長時間的積累之後就會有精品,我希望我這一篇文章能幫助各位正在“坑裡”的猿猿們。

產品:你給我調研一個手機連上無網WIFI 後能繼續使用手機流量的 需求。WIFI不能自動跳轉和改變,(WIFI和移動流量必須同時開啟)。
程式設計師:?????納尼??2333333~

初聽到需求,我於是去啪啪啪的搜了一圈,
搜:wifi和移動流量能同時使用麼?wifi連線後,能強行使用移動流量麼?等等。。。答案也是千奇百怪。有的需要改開發板,有需要用 wireless-tools 方式驅動WIFI ,通過命令啟動 WIFI 模組。也有需要使用wpa_supplicant 方式驅動WIFI .可想而知 ,都沒有許可權,不符合需求。


直到遇見了WIFIP2P,一切問題迎刃而解。


背景

  1. 使用場景以及連線原理簡介

    wifip2p是一種點對點,一對多的 角色。通俗點 Android中 稱之為WIFI直連,在IOS 中稱之為 Airdrop(隔空投送),這種功能很強大,尤其是在智慧家居,智慧硬體領域。因為藍芽的傳輸限制,無法滿足更多的需求(比如視訊資料的接收)。USB 可以滿足這一點不假,但是有距離限制。而且也不是很方便。所以一般我們採用WIFI 連線。這種模式也可以。

  2. 檢視WIFI網絡卡/手機網絡卡狀態
    如果:這時候你既要連線wifi裝置,又要需要請求網路處理資料,怎麼辦。如今的手機我們都知道只要wifi連線,便中斷手機移動資料。這一點我也已經確實的驗證過。
    執行 —> cmd —> adb shell —> netcfg
    通過檢視 rmnet0 (手機網絡卡) 和 wlan0(WIFI連線)的狀態 (DOWN 和 UP)。

  3. wifip2p作用
    這時候wifip2p 的優點就顯示出來的。既可以滿足通訊要求,又可以不影響手機網路。豈不是很nice。通過wifip2p裝置可以直接通過WiFi共享檔案,圖片等(類似藍芽傳輸),實現點對點的連線,通訊(要求兩裝置必須位於同一網段)。在Miracast應用場景中,一臺支援P2P的智慧手機可直接連線上一臺支援P2P的智慧電視,智慧手機隨後將自己的螢幕,或者媒體資源傳送給電視機去顯示或播放。
    顯然,藉助P2P技術,Wi-Fi裝置之間的直接相連將極大拓展Wi-Fi技術的使用場景。

  4. 如何判斷自己的裝置是否支援wifip2p
    判斷裝置是否支援WiFi直連是通過
    PackageManager的hasSystemFeature (API)
    ps:
    getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT);


程式碼實戰

官方文件:http://developer.android.com/intl/zh-tw/guide/topics/connectivity/wifip2p.html
官方Demo: http://download.csdn.net/detail/yichigo/5516627

具體步驟
- 設定應用程式許可權
- 建立一個廣播接收器
- 初始化對等點的搜尋
- 檢查裝置WIFI 是否開啟,
- 只有上面的一步開啟,才能開始搜尋對等點
- 搜尋到對等點列表,拿到列表資訊
- 建立一個對等點連線

1.首先把需要的許可權都配置好

    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

2.需要監聽WIFIP2P的廣播

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.NetworkInfo;
import android.net.wifi.p2p.WifiP2pDevice;
import android.net.wifi.p2p.WifiP2pDeviceList;
import android.net.wifi.p2p.WifiP2pInfo;
import android.net.wifi.p2p.WifiP2pManager;
import android.text.TextUtils;
import android.util.Log;

import java.util.ArrayList;
import java.util.List;


public class DirectBroadcastReceiver extends BroadcastReceiver {

    private static final String TAG = "DirectBroadcastReceiver";

    private WifiP2pManager mWifiP2pManager;

    private WifiP2pManager.Channel mChannel;
    /**是一個自定義監聽 回撥介面**/
    private WifiDirectActionListener mWifiDirectActionListener;

    public DirectBroadcastReceiver(WifiP2pManager wifiP2pManager, WifiP2pManager.Channel channel, DirectActionListener directActionListener) {
        mWifiP2pManager = wifiP2pManager;
        mChannel = channel;
        mDirectActionListener = directActionListener;
    }

    public static IntentFilter getIntentFilter() {
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION);
        intentFilter.addAction(WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION);
        return intentFilter;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        Log.e(TAG, "接收到廣播: " + intent.getAction());
        if (!TextUtils.isEmpty(intent.getAction())) {
            switch (intent.getAction()) {
                /**用於指示 Wifi P2P 是否可用**/
                case WifiP2pManager.WIFI_P2P_STATE_CHANGED_ACTION:
                    int state = intent.getIntExtra(WifiP2pManager.EXTRA_WIFI_STATE, -1);
                    if (state == WifiP2pManager.WIFI_P2P_STATE_ENABLED) {
                        mWifiDirectActionListener.wifiP2pEnabled(true);
                    } else {
                        mWifiDirectActionListener.wifiP2pEnabled(false);
                        List<WifiP2pDevice> wifiP2pDeviceList = new ArrayList<>();
                        mWifiDirectActionListener.onPeersAvailable(wifiP2pDeviceList);
                    }
                    break;

                /**表明可用的對等點的列表發生了改變 **/
                case WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:
                    mWifiP2pManager.requestPeers(mChannel, new WifiP2pManager.PeerListListener() {
                        @Override
                        public void onPeersAvailable(WifiP2pDeviceList peers) {
                            mWifiDirectActionListener.onPeersAvailable(peers.getDeviceList());
                        }
                    });
                    break;

                /**表示Wi-Fi對等網路的連線狀態發生了改變 **/
                case WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:
                    NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
                    if (networkInfo.isConnected()) {
                        mWifiP2pManager.requestConnectionInfo(mChannel, new WifiP2pManager.ConnectionInfoListener() {
                            @Override
                            public void onConnectionInfoAvailable(WifiP2pInfo info) {
                                if (info != null) {
                                    Log.i(TAG, "確實獲取到WiFip2pinfo");
                                    //!!!這裡很關鍵,只有真正的走到這裡,才是真正的建立了P2P連線。拿到了準備建立Socket通道的必要資訊。
                                } else {
                                    Log.i(TAG, "WiFip2pinfo 為null");
                                }
                                mWifiDirectActionListener.onConnectionInfoAvailable(info);
                            }
                        });
                        Log.i(TAG, "已連線P2P");
                    } else {
                        mWifiDirectActionListener.onDisconnection();
                        Log.i(TAG, "與P2P裝置已斷開連線");
                    }
                    break;

                /**表示該裝置的配置資訊發生了改變**/
                case WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:
                    mWifiDirectActionListener.onSelfDeviceAvailable((WifiP2pDevice) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
                    break;
                default:

                    break;
            }
        }
    }

}

自定義回撥監聽

public interface WifiDirectActionListener extends WifiP2pManager.ChannelListener {

    void wifiP2pEnabled(boolean enabled);

    void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo);

    void onDisconnection();

    void onSelfDeviceAvailable(WifiP2pDevice wifiP2pDevice);

    void onPeersAvailable(Collection<WifiP2pDevice> wifiP2pDeviceList);

}

3.初始化WIFIP2P的config,並且開始搜尋
【!!!在我開發過程,我發現需要真正的搜尋到列表,必須多臺裝置都處於搜尋狀態,否自不處於搜尋的那個裝置是不會出現在DeviceList中,可以嘗試熄屏,後臺狀態。】

    private WifiP2pManager mWifiP2pManager;
    private WifiP2pManager.Channel mChannel;
    private WifiP2pDevice mWifiP2pDevice;
    private BroadcastReceiver broadcastReceiver;
    private boolean mWifiP2pEnabled = false;
    private List<WifiP2pDevice> mWifiP2pDeviceList;

  private void initConfig() {
        mWifiP2pManager = (WifiP2pManager) getSystemService(Context.WIFI_P2P_SERVICE);
        mChannel = mWifiP2pManager.initialize(this, getMainLooper(), this);
        broadcastReceiver = new DirectBroadcastReceiver(mWifiP2pManager, mChannel, this);
        registerReceiver(broadcastReceiver, DirectBroadcastReceiver.getIntentFilter());
        //如果需要P2P裝置列表的話,可以在這裡初始化一個集合
        mWifiP2pDeviceList = new ArrayList<>();
    }

接著開始搜尋列表

   mWifiP2pManager.discoverPeers(mChannel, new WifiP2pManager.ActionListener() {
                    @Override
                    public void onSuccess() {
                        showToast("Success");
                    }

                    @Override
                    public void onFailure(int reasonCode) {
                        showToast("Failure");

                    }
                });

如果寫了Adapter 就可以直接看到列表有多少個 對等點。同樣也可以直接在廣播中打log

 case WifiP2pManager.WIFI_P2P_PEERS_CHANGED_ACTION:
 ?????
 break

同時,我們可以獲取到當前裝置的一些基本資訊。在廣播

case WifiP2pManager.WIFI_P2P_THIS_DEVICE_CHANGED_ACTION:
     mDirectActionListener.onSelfDeviceAvailable((WifiP2pDevice) intent.getParcelableExtra(WifiP2pManager.EXTRA_WIFI_P2P_DEVICE));
    break;
    @Override
    public void onSelfDeviceAvailable(WifiP2pDevice wifiP2pDevice) {
        Log.i(TAG, "DeviceName: " + wifiP2pDevice.deviceName);
        Log.i(TAG, "DeviceAddress: " + wifiP2pDevice.deviceAddress);
        Log.i(TAG, "Status: " + wifiP2pDevice.status);
      //注意這裡的 wifiP2pDevice.status 並不是真正的連線狀態,但是如果P2P連線成功,這裡的狀態一定是 wifiP2pDevice.status ==0 。後面總結會具體講述
    }

先貼出一張API引數資訊。
這裡寫圖片描述

4.在接著就是連線P2P
注意:這裡很值得說一說。我們呼叫manager中的connect方法,
①如果連線之前沒有建立一個組,系統會自動建立一個組,並且隨機分配誰是GroupOwner即誰是組長,
②建立一個組,誰建立誰就是組長。
③這也關係到誰是客戶端誰是伺服器,

為什麼要重點提示誰是組長?因為這裡有坑,就是組長只有接受資料的能力,非組長擁有傳送資料的能力。並不是誰都可以隨便傳送和接收,因為Socket 建立需要IP,而WIFIP2P 連線後並不能獲取到客戶機的IP。只能獲取到組長的IP 。也就是 wifiP2pInfo.groupOwnerAddress.getHostAddress()

PS: 一開始我也不知道問題出在那裡,後來就查到大家都有這種問題。
https://bbs.csdn.net/topics/390780179/

最近在研究wifi direct
,主要是通過IWIFIP2PManager類來操作,但是我發現,當成功建立連線後,好像從ConnectInfo中(WIFIP2PInfo)中只能獲取GroupOwner的IpAddress,並沒有API可以提供其他Device的IpAdress。這個會導致上層應用無法獲得對端Ip地址,而無法傳遞資料等。由於在不同的場景下,任何裝置都有可能是GroupOwner,而我們除了GroupOwner的Ip
Address外,還希望能獲取對端以及Group中所有成員的Ip Address,包括自己的Ip Address.

   private void connect() {
        WifiP2pConfig config = new WifiP2pConfig();
        if (config.deviceAddress != null && mWifiP2pDevice != null) {
            config.deviceAddress = mWifiP2pDevice.deviceAddress;
            config.wps.setup = WpsInfo.PBC;
            mWifiP2pManager.connect(mChannel, config, new WifiP2pManager.ActionListener() {
                @Override
                public void onSuccess() {
                    Log.e(TAG, "connect onSuccess");
                }

                @Override
                public void onFailure(int reason) {
                    Log.e(TAG, "connect fail");
                }
            });
        }
    }

主動建立組長

    private void createroup() {
        mWifiP2pManager.createGroup(mChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onSuccess() {
                Log.e(TAG, "createGroup onSuccess");
                showToast("onSuccess");
            }

            @Override
            public void onFailure(int reason) {
                Log.e(TAG, "createGroup onFailure: " + reason);
                showToast("onFailure");
            }
        });
    }

同樣,斷開連線就是 移除組就可以了。

    private void disconnect() {
        mWifiP2pManager.removeGroup(mChannel, new WifiP2pManager.ActionListener() {
            @Override
            public void onFailure(int reasonCode) {
                Log.i(TAG, "disconnect onFailure:" + reasonCode);
            }

            @Override
            public void onSuccess() {
                Log.i(TAG, "disconnect onSuccess");
            }
        });
    }

4.資料連線階段,非組長(群主)程式碼
開啟非同步操作。

  new WifiClientTask(this, fileTransfer).execute(wifiP2pInfo.groupOwnerAddress.getHostAddress());
/**這裡的 fileTransfer 就是需要傳輸的 Object 自定義個Bean 就好 ,重寫 Serializable
    而wifiP2pInfo.groupOwnerAddress.getHostAddress() 就是組IP**/
public class WifiClientTask extends AsyncTask<String, Integer, Boolean> {

    private ProgressDialog progressDialog;

    private FileTransfer fileTransfer;

    private static final int PORT = 4786;

    private static final String TAG = "WifiClientTask";

    public WifiClientTask(Context context, FileTransfer fileTransfer) {
        this.fileTransfer = fileTransfer;
        progressDialog = new ProgressDialog(context);
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.setCancelable(false);
        progressDialog.setCanceledOnTouchOutside(false);
        progressDialog.setTitle("正在傳送檔案");
        progressDialog.setMax(100);
    }

    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }

    @Override
    protected Boolean doInBackground(String... strings) {
        fileTransfer.setMd5(Md5Util.getMd5(new File(fileTransfer.getFilePath())));
        Log.i(TAG, "檔案的MD5碼值是:" + fileTransfer.getMd5());
        Socket socket = null;
        OutputStream outputStream = null;
        ObjectOutputStream objectOutputStream = null;
        InputStream inputStream = null;
        try {
            socket = new Socket();
            socket.bind(null);
            Log.i(TAG, "IP:" + strings[0] + "PORT:" + PORT);
            socket.connect((new InetSocketAddress(strings[0], PORT)), 10000);
            outputStream = socket.getOutputStream();
            objectOutputStream = new ObjectOutputStream(outputStream);
            objectOutputStream.writeObject(fileTransfer);
            inputStream = new FileInputStream(new File(fileTransfer.getFilePath()));
            long fileSize = fileTransfer.getFileLength();
            long total = 0;
            byte buf[] = new byte[512];
            int len;
            while ((len = inputStream.read(buf)) != -1) {
                outputStream.write(buf, 0, len);
                total += len;
                int progress = (int) ((total * 100) / fileSize);
                publishProgress(progress);
                Log.i(TAG, "檔案傳送進度:" + progress);
            }
            outputStream.close();
            objectOutputStream.close();
            inputStream.close();
            socket.close();
            outputStream = null;
            objectOutputStream = null;
            inputStream = null;
            socket = null;
            Log.i(TAG, "檔案傳送成功");
            return true;
        } catch (Exception e) {
            Log.i(TAG, "檔案傳送異常 Exception: " + e.getMessage());
        } finally {
            if (outputStream != null) {
                try {
                    outputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (objectOutputStream != null) {
                try {
                    objectOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (socket != null) {
                try {
                    socket.close();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return false;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        progressDialog.setProgress(values[0]);
    }

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        progressDialog.cancel();
        Log.i(TAG, "onPostExecute: " + aBoolean);
    }

}

而Socket.accept 等待方 開啟IntentService

public class WifiServerService extends IntentService {

    private static final String TAG = "WifiServerService";

    public interface OnProgressChangListener {

        //當傳輸進度發生變化時
        void onProgressChanged(FileTransfer fileTransfer, int progress);

        //當傳輸結束時
        void onTransferFinished(File file);

    }

    private ServerSocket serverSocket;

    private InputStream inputStream;

    private ObjectInputStream objectInputStream;

    private FileOutputStream fileOutputStream;

    private OnProgressChangListener progressChangListener;

    private static final int PORT = 4786;

    public class MyBinder extends Binder {
        public WifiServerService getService() {
            return WifiServerService.this;
        }
    }

    public WifiServerService() {
        super("WifiServerService");
        Log.i(TAG, "WifiServerService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.i(TAG, "onCreate");
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.i(TAG, "onBind");
        return new MyBinder();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        Log.i(TAG, "onHandleIntent");
        clean();
        File file = null;
        try {
            serverSocket = new ServerSocket();
            serverSocket.setReuseAddress(true);
            serverSocket.bind(new InetSocketAddress(PORT));
            Socket client = serverSocket.accept();
            Log.i(TAG, "客戶端IP地址 : " + client.getInetAddress().getHostAddress());
            inputStream = client.getInputStream();
            objectInputStream = new ObjectInputStream(inputStream);
            /**如果你寫的是2個程式,儘管FileTransfer一樣,但是也要注意包名,其實只要Socket 連線通,客戶端IP 拿到,就達到了目標。**/
            FileTransfer fileTransfer = (FileTransfer) objectInputStream.readObject();
            Log.i(TAG, "待接收的檔案: " + fileTransfer);
            String name = new File(fileTransfer.getFilePath()).getName();
            //將檔案儲存至指定位置
            file = new File(Environment.getExternalStorageDirectory() + "/" + name);
            fileOutputStream = new FileOutputStream(file);
            byte buf[] = new byte[512];
            int len;
            long total = 0;
            int progress;
            while ((len = inputStream.read(buf)) != -1) {
                fileOutputStream.write(buf, 0, len);
                total += len;
                progress = (int) ((total * 100) / fileTransfer.getFileLength());
                Log.i(TAG, "檔案接收進度: " + progress);
                if (progressChangListener != null) {
                    progressChangListener.onProgressChanged(fileTransfer, progress);
                }
            }
            serverSocket.close();
            inputStream.close();
            objectInputStream.close();
            fileOutputStream.close();
            serverSocket = null;
            inputStream = null;
            objectInputStream = null;
            fileOutputStream = null;
            Log.i(TAG, "檔案接收成功,檔案的MD5碼是:" + Md5Util.getMd5(file));
        } catch (Exception e) {
            Log.i(TAG, "檔案接收 Exception: " + e.getMessage());
        } finally {
            clean();
            if (progressChangListener != null) {
                progressChangListener.onTransferFinished(file);
            }
            //再次啟動服務,等待客戶端下次連線
            startService(new Intent(this, WifiServerService.class));
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        clean();
    }

    public void setProgressChangListener(OnProgressChangListener progressChangListener) {
        this.progressChangListener = progressChangListener;
    }

    private void clean() {
        if (serverSocket != null) {
            try {
                serverSocket.close();
                serverSocket = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (inputStream != null) {
            try {
                inputStream.close();
                inputStream = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (objectInputStream != null) {
            try {
                objectInputStream.close();
                objectInputStream = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (fileOutputStream != null) {
            try {
                fileOutputStream.close();
                fileOutputStream = null;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

其實Demo 我也是從githup上拿到的。但是同樣我也將我自己遇到的坑總結了一翻,徹底明白了WIFIP2P 。
https://github.com/leavesC/WifiP2P


總結【精華】

  1. WIFIP2P連線成功後,要注意,只有非群主才能向群主傳送Socket建立,並能成功傳送資料。
    ~~~~~
    原因:當成功建立連線後,從WIFIP2PInfo中只能獲取GroupOwner的IpAddress,並沒有API可以提供其他Device的IpAdress。這個會導致上層應用無法獲得對端Ip地址,而無法傳遞資料等。由於在不同的場景下,任何裝置都有可能是GroupOwner,而我們除了GroupOwner的Ip Address外,還希望能獲取對端以及Group中所有成員的Ip Address,包括自己的Ip Address.
  2. WIFIP2P 建連後(connect方法),如果連線之前沒有建立一個組,系統會自動建立一個組,並且隨機分配誰是GroupOwner即誰是組長,這也關係到誰是客戶端誰是伺服器.
    ~~~~~
    問題:這有個坑,非常大的機率 他會一直鎖定一個Device 為群主,不管是A 連線B ,還是B 連線 A, 還是A 連線C。所以一定要確定 wifiinfo 中
    wifiP2pInfo.groupFormed——–一定要為true ,這樣wifiinfo才包含有用資訊
    wifiP2pInfo.isGroupOwner——–一定要為false,是非群主,見1.才有可能傳送資訊。
    ~~~~~
    如何解決:API 也有方法提供
    呼叫 wifiP2pManager.createGroup(channel, new WifiP2pManager.ActionListener() {},在未連線p2p 之前,在你想要當做伺服器的(AP)的機子上提前 createGroup,並在開始連線。就可以結局隨機組長的問題。

  3. WifiP2pDevice 的意思是【本機裝置資訊】,所以千萬不要誤以為是獲取到的是另外一臺裝置的裝置資訊。
    其中常用有:
    wifiP2pDevice.deviceName 本裝置名字
    wifiP2pDevice.deviceAddress 本裝置的MAC地址
    wifiP2pDevice.status 裝置連線狀態
    ~~~~~
    重點!!! 千萬不要以為 wifiP2pDevice.status
    (0是連線 ,1是邀請中,3是未連線,但是可用,4是不可用)
    為0的時候就是連線了,問題在於:p2p連線了,status 一定為0,但是status 為0 ,p2p不一定真正的連線了。這種情況在連線【智慧裝置】的時候容易出現,我就在此被坑了。真正的wifip2p 連線成功,必須要 在
    WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION
    廣播中獲得 為true
    intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO).isConnected()
    並且通過
    mWifiP2pManager.requestConnectionInfo(?,?);的回撥監聽,拿到wifiinfo的實體 ,且不為空。 才算的上你真正的加入p2p組。

  4. 至此,wifip2p 連線問題 的問題點基本已經解決。真正連線後p2p 後就要建立Socket連線。一般是沒有問題的,有問題也只有2點

    1.)檔案路徑沒有獲取到,我們通過intent 獲取到的 一般是 Uri
    content://com.android.providers.media.documents/document/image%3A63
    而我們想要的是檔案絕對路徑
    /data/hw_init/version/region_comm/china/media/Pre-loaded/Pictures/01-04.jpg
    這樣才能 通過 File file = new File(path); 檔案。
    所以注意這一點路徑轉換過程 是否為空
    ~~
    2.)socket 伺服器等待方 serverSocket.accept();
    其實也只是 要保證是否在等待。
    樓主使用的是Githup的Demo ,其中serverSocket.accept(); 是在intentservice 的
    onHandleIntent 中開啟的,這個intentservice 的 開啟有2種方式,startService 和
    bindService 一般我們喜歡使用bindService ,畢竟資料接收後需要處理,通知UI 執行緒。
    但是bingService 的生命週期 是不走 onHandleIntent. 而 startService 是走的。具體看我另外一篇部落格對Service 和IntentService 的 解釋。(下面是連結)
    https://blog.csdn.net/qq_31332467/article/details/82430638

  5. 在開發過程中發現,WIFIP2P連線,必須雙向裝置都處於discoverPeers狀態中,如果一方不在該狀態中,則另一方根本拿不到裝置列表,拿不到就談不了最基本的連線

後記

因為我連線的硬體是車機,車機開發板中有很多問題,可能對Android開發板做了修改,我這邊就遇到2個問題:

1.一個車機是有WIFI ,但是卻沒有直連模組,問了供貨商,他們說沒改過。可是開了廣播後,無論如何都拿不到廣播,根本連onReceive 都沒走。那就什麼都不用談了,列表都拿不到,還說什麼。
2.換了一臺車機(另一個供貨商的),這個就很順利的拿到了列表。果然供應商都是。。。。但是這個更坑,讓我白高興了一場。

順利拿到了列表,也順利連線上了,只是mWifiP2pManager.connect()中回撥成功。我以為連線OK,就開始Socket建連,可無論如何都是提示 Connection refusal!
我怎麼就不明白,難道開發商的車機覺得我的客戶端IP 非法,給我拒絕了?
一直就認為是套接字的問題。後來才發現,其實根本就沒連線上,關鍵在廣播中的

 case WifiP2pManager.WIFI_P2P_CONNECTION_CHANGED_ACTION:
                    NetworkInfo networkInfo = intent.getParcelableExtra(WifiP2pManager.EXTRA_NETWORK_INFO);
                    if (networkInfo.isConnected()) {
                        mWifiP2pManager.requestConnectionInfo(mChannel, new WifiP2pManager.ConnectionInfoListener() {
                            @Override
                            public void onConnectionInfoAvailable(WifiP2pInfo info) {
                                if (info != null) {
                                  /**!!!!只有真正的走到這裡才是真正連線!!!!**/
                                } else {
                                    Log.i(TAG, "WiFip2pinfo 為null");
                                }
                                mDirectActionListener.onConnectionInfoAvailable(info);
                            }
                        });
                        Log.i(TAG, "已連線P2P");
                    } else {
                        mDirectActionListener.onDisconnection();
                        Log.i(TAG, "與P2P裝置已斷開連線");
                    }
                    break;

然後在賦值WifiP2pInfo 的時候,更要注意:

 @Override
    public void onConnectionInfoAvailable(WifiP2pInfo wifiP2pInfo) {  

        Log.i(TAG, "onConnectionInfoAvailable groupFormed: " + wifiP2pInfo.groupFormed);
        Log.i(TAG, "onConnectionInfoAvailable isGroupOwner: " + wifiP2pInfo.isGroupOwner);
        Log.i(TAG, "onConnectionInfoAvailable getHostAddress: " + wifiP2pInfo.groupOwnerAddress.getHostAddress());
       /**必須if條件成立才可以開啟 AsyncTask**/
        if (wifiP2pInfo.groupFormed && !wifiP2pInfo.isGroupOwner) {
            Log.e(TAG, "F-Z-C-G");
            this.wifiP2pInfo = wifiP2pInfo;
        }
    }

本章完結