Android wifi通信 开发(wifi列表 +连接具体的wifi热点+socket的获取) 之 解决热点连成功的时延问题

写这篇博客前,说一些题外话:楼主是在一家做嵌入式研发的公司当然也有软件研发,总之就是以软硬件通信为主把数据展示到前端,所
以不可避免的有 wifi 蓝牙 网口 串口 等这些通信媒介,网上的wifi通信千篇一律下面我来总结我的wifi通信 ,可以负责的告诉大家这是公
司目前在用的,起码稳定是可以维持住的,欢迎大家指证。
转载请附上本文链接squery的博客链接地址: http://blog.csdn.net/shentanweilan9

wifi通信设计到以下3点, 当然 肯定要建立一个工具类来对外暴露这三个以上的方法

  • wifi列表获取展示
  • wifi连接
  • 获取热点ip建立socket

wifi连接通信的工具类WifiAdmin

这个工具类中主要方法如下:

  1. startScan() 扫描wifi
  2. getWifiList() 获取wifi列表
  3. connectToTarget() 连接wifi 这里包含两个一种是不知道加密方式的 另一种加密方式固定 两种都需要密码
  4. intToIp() 转换成ip地址

整个类代码如下:

package com.pswx.squery.wifi_phone.utils;

import android.content.Context;
import android.net.DhcpInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;

import java.lang.reflect.Method;
import java.util.List;

/**
 * Created by squery on 2017/8/17.
 */
public class WifiAdmin {
    public static final String SSID = "test";
    public static final String PassWord = "12345678";
    private final DhcpInfo mDhcpInfo;
    private WifiManager mWifiManager;//wifimanager 对象
    private WifiInfo mWifiInfo; // 定义WifiInfo对象
    private List<ScanResult> mWifiList;  // 扫描出的网络连接列表
    private List<WifiConfiguration> mWifiConfiguration;  // 网络连接列表
    WifiManager.WifiLock mWifiLock;  // 定义一个WifiLock
    private static final int NOPASSWORD = 0;
    private static final int PASSWORD_WPA = 1;
    private static final int PASSWORD_WEP = 2;
    private static final int PASSWORD_WPA2 = 3;

    // 构造器
    public WifiAdmin(Context context) {
        // 取得WifiManager对象
        mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        // 取得WifiInfo对象
        mWifiInfo = mWifiManager.getConnectionInfo();
        mDhcpInfo = mWifiManager.getDhcpInfo();
    }

    //打开wifi
    public void openWifi() {
        if (!mWifiManager.isWifiEnabled()) {
            mWifiManager.setWifiEnabled(true);
        }
    }

    //关闭WIFI
    public void closeWifi() {
        if (mWifiManager.isWifiEnabled()) {
            mWifiManager.setWifiEnabled(false);
        }
    }

    //创建热点
    public void createAp() {
        if (mWifiManager.isWifiEnabled()) {
            mWifiManager.setWifiEnabled(false);
        }
        try {
            WifiConfiguration apConfiguration = new WifiConfiguration();
            apConfiguration.SSID = WifiAdmin.SSID;
            apConfiguration.preSharedKey = WifiAdmin.PassWord;
            apConfiguration.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
            Method method = mWifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
            method.invoke(mWifiManager, apConfiguration, true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //关闭WiFi热点
    public void closeWifiAp() {
        if (isWifiApEnabled()) {
            try {
                Method method = mWifiManager.getClass().getMethod("getWifiApConfiguration");
                method.setAccessible(true);
                WifiConfiguration config = (WifiConfiguration) method.invoke(mWifiManager);
                Method method2 = mWifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, boolean.class);
                method2.invoke(mWifiManager, config, false);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }


    //热点开关是否打开
    public boolean isWifiApEnabled() {
        try {
            Method method = mWifiManager.getClass().getMethod("isWifiApEnabled");
            method.setAccessible(true);
            return (Boolean) method.invoke(mWifiManager);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    // 检查当前WIFI状态
    public int checkState() {
        return mWifiManager.getWifiState();
    }

    // 锁定WifiLock
    public void acquireWifiLock() {
        mWifiLock.acquire();
    }

    // 解锁WifiLock
    public void releaseWifiLock() {
        // 判断时候锁定
        if (mWifiLock.isHeld()) {
            mWifiLock.acquire();
        }
    }


    // 创建一个WifiLock
    public void creatWifiLock() {
        mWifiLock = mWifiManager.createWifiLock("Test");
    }

    // 得到配置好的网络
    public List<WifiConfiguration> getConfiguration() {
        return mWifiConfiguration;
    }


    // 指定配置好的网络进行连接
    public void connectConfiguration(int index) {
        // 索引大于配置好的网络索引返回
        if (index > mWifiConfiguration.size()) {
            return;
        }
        // 连接配置好的指定ID的网络
        mWifiManager.enableNetwork(mWifiConfiguration.get(index).networkId, true);
    }

    /**
     * 扫描WIFI
     */
    public void startScan() {
        mWifiManager.startScan();
        // 得到扫描结果
        mWifiList = mWifiManager.getScanResults();
        // 得到配置好的网络连接
        mWifiConfiguration = mWifiManager.getConfiguredNetworks();
    }

    // 得到网络列表
    public List<ScanResult> getWifiList() {
        return mWifiList;
    }

    // 查看扫描结果
    public StringBuilder lookUpScan() {
        StringBuilder stringBuilder = new StringBuilder();
        for (int i = 0; i < mWifiList.size(); i++) {
            stringBuilder.append("Index_" + new Integer(i + 1).toString() + ":");
            // 将ScanResult信息转换成一个字符串包
            // 其中把包括:BSSID、SSID、capabilities、frequency、level
            stringBuilder.append((mWifiList.get(i)).toString());
            stringBuilder.append("/n");
        }
        return stringBuilder;
    }

    // 得到接入点的BSSID
    public String getSSID() {
        return (mWifiInfo == null) ? "NULL" : mWifiInfo.getSSID();
    }

    // 得到MAC地址
    public String getMacAddress() {
        return (mWifiInfo == null) ? "NULL" : mWifiInfo.getMacAddress();
    }

    // 得到接入点的BSSID
    public String getBSSID() {
        return (mWifiInfo == null) ? "NULL" : mWifiInfo.getBSSID();
    }


    // 得到IP地址
    public int getIPAddress() {
        return (mWifiInfo == null) ? 0 : mWifiInfo.getIpAddress();
    }

    public int getServerIPAddress() {
        return (mWifiInfo == null) ? 0 : mDhcpInfo.serverAddress;
    }


    // 得到连接的ID
    public int getNetworkId() {
        return (mWifiInfo == null) ? 0 : mWifiInfo.getNetworkId();
    }

    // 得到WifiInfo的所有信息包
    public String getWifiInfo() {
        return (mWifiInfo == null) ? "NULL" : mWifiInfo.toString();
    }


    // 添加一个网络并连接
    public int addNetwork(WifiConfiguration wcg) {
        int wcgID = mWifiManager.addNetwork(wcg);
        boolean b = mWifiManager.enableNetwork(wcgID, true);
        mWifiManager.reassociate();
        LogUtils.e("b--" + b);
        return wcgID;
    }


    // 创建wificonfig
    public WifiConfiguration createWifiConfig(String SSID, String Password, int Type) {
        WifiConfiguration config = new WifiConfiguration();
        config.allowedAuthAlgorithms.clear();
        config.allowedGroupCiphers.clear();
        config.allowedKeyManagement.clear();
        config.allowedPairwiseCiphers.clear();

        config.allowedProtocols.clear();
        config.SSID = "\"" + SSID + "\"";


        //如果设备大于6.0配置的时候就不需要双引号,加了就连接不上了
        if (Build.VERSION.SDK_INT >= 23) {
            config.SSID = SSID;
        } else {
            config.SSID = "\"" + SSID + "\"";
        }

        WifiConfiguration tempConfig = isExsits(SSID);
        if (tempConfig != null) {// 去除自动保存的 wifi
            disconnectWifi(tempConfig.networkId);
        }

        if (Type == NOPASSWORD) // WIFICIPHER_NOPASS
        {
            config.hiddenSSID = true;
            //config.wepKeys[0] = "";
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
            //config.wepTxKeyIndex = 0;
        }
        if (Type == PASSWORD_WPA) // WIFICIPHER_WPA
        {
            config.preSharedKey = "\"" + Password + "\"";
            config.hiddenSSID = true;
            config.allowedAuthAlgorithms
                    .set(WifiConfiguration.AuthAlgorithm.OPEN);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
            config.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            config.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
            config.status = WifiConfiguration.Status.ENABLED;
        }
        if (Type == PASSWORD_WEP) // WIFICIPHER_WEP
        {
            config.hiddenSSID = true;
            config.wepKeys[0] = "\"" + Password + "\"";
            config.allowedAuthAlgorithms
                    .set(WifiConfiguration.AuthAlgorithm.SHARED);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            config.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.WEP40);
            config.allowedGroupCiphers
                    .set(WifiConfiguration.GroupCipher.WEP104);
            config.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
            config.wepTxKeyIndex = 0;
        }
        return config;
    }

    /**
     * 判断wifi是否存在
     *
     * @param SSID
     * @return
     */
    public WifiConfiguration isExsits(String SSID) {
        List<WifiConfiguration> existingConfigs = mWifiManager.getConfiguredNetworks();
        if (!ArrayUtils.isEmpty(existingConfigs)) {
            for (WifiConfiguration existingConfig : existingConfigs) {
                if (existingConfig.SSID.equals("\"" + SSID + "\"")) {
                    return existingConfig;
                }
            }
        }
        return null;
    }


    /**
     * 连接目标热点
     *
     * @param scanResult 热点的加密方式
     */
    public int connectToTarget(ScanResult scanResult, String password) {
        int mNetworkID = 0;
        int password_type = 0;
        WifiConfiguration mTargetWifiCfg;
        if (scanResult != null) {
            if (scanResult.capabilities.contains("WPA") || scanResult.capabilities.contains("wpa")) {
                password_type = PASSWORD_WPA;
            } else if (scanResult.capabilities.contains("WEP") || scanResult.capabilities.contains("wep")) {
                password_type = PASSWORD_WEP;
            } else if (scanResult.capabilities.contains("WPA2") || scanResult.capabilities.contains("wpa2")) {
                password_type = PASSWORD_WPA2;
            } else {
                password_type = NOPASSWORD;
            }
        }
        //LogUtils.e(scanResult.SSID+"::::::::::::::::::::" + password_type); //password_type=1 WPA
        mTargetWifiCfg = createWifiConfig(scanResult.SSID, password, password_type);// 获得wificonfig
        mNetworkID = addNetwork(mTargetWifiCfg);
        return mNetworkID;
    }

    public int connectToTarget(String SSID, String password) {
        int mNetworkID = 0;
        int password_type = PASSWORD_WPA;
        WifiConfiguration mTargetWifiCfg;
        mTargetWifiCfg = createWifiConfig(SSID, password, password_type);// 获得wificonfig
        mNetworkID = addNetwork(mTargetWifiCfg);
        return mNetworkID;
    }


    // 断开指定ID的网络
    public void disconnectWifi(int netId) {
        mWifiManager.disableNetwork(netId);
        mWifiManager.disconnect();
        mWifiManager.removeNetwork(netId);
    }


    /**
     * 转换IP地址
     *
     * @param i
     * @return
     */
    public String intToIp(int i) {
        return (i & 0xFF) + "." + ((i >> 8) & 0xFF) + "." + ((i >> 16) & 0xFF)
         + "." + ((i >> 24) & 0xFF);
    }
}

wifi列表获取与展示

wifi列表的获取,大致思路是:扫描附近wifi这个时候android系统会广播一条通知SCAN_RESULTS_AVAILABLE_ACTION
注意:** 每扫描一次会广播单条具体的通知,这个不同于wifi连接的通知(wifi连接通知 不同手机广播的通知条数不同 通知类型也不同) ** 然后就是在广播监听里面获取到wifi列表 并更新adapter 也就是更新ui

发送广播 可以在一进入页面就去扫描 也可以通过按钮点击事件来触发 代码如下:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setLayoutId(R.layout.activity_wifi_list);
        super.onCreate(savedInstanceState);
        initTitle(R.mipmap.left_arrow, R.string.back, "wifi列表", R.string.showelctric, 0);
        mWifiAdmin.openWifi();// 第一次进来时候显示列表
        scanFlag = 1;
        mWifiAdmin.startScan();//开始扫描  发送通知
        AppUtils.getInstance().showLoading(this);
    }

@OnClick({R.id.lear_left, R.id.btn_scan})
    void onClicks(View v) {
        int flag = 0;
        switch (v.getId()) {
            case R.id.lear_left:
                mConnectThread = null;
                finish();
                break;
            case R.id.btn_scan:
                mWifiAdmin.openWifi();
                mWifiAdmin.startScan();//开始扫描  发送通知
                AppUtils.getInstance().showLoading(this);
                scanFlag = 1;
                break;
        }
    }

获取wifi列表 并更新adapter 当然要注册广播监听 页面销毁时候要注销广播监听 代码如下:

  @Override
    public void onResume() {// 注册广播
        super.onResume();
        registerReceiver(wifiScanReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));

    }

    @Override
    public void onPause() {// 注销广播
        super.onPause();
        unregisterReceiver(wifiScanReceiver);
    }

    private BroadcastReceiver wifiScanReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context arg0, Intent intent) {
            if (intent.getAction().equals(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION)) {//扫描完毕 更新Ui
                    AppUtils.getInstance().dismissLoading(WifiListActivity.this);
                    mWifiList.clear();
                    for (ScanResult scanResult : mWifiAdmin.getWifiList()) {
                        if (scanResult.SSID.startsWith("orbis_"))  // 过滤掉其他的,添加自己需要的wifi热点
                            mWifiList.add(new CustomScan(false, scanResult));
                    }
                    //LogUtils.e("扫描的wifi列表:::\t" + mWifiAdmin.lookUpScan().toString());
                    mCommonAdapter.notifyDataSetChanged();
            }
        }
    };

wifi连接以及处理连接时延问题

wifi连接:就是连接具体的wifi热点 这个很好理解
连接时延: 执行了连接代码 但是系统反应需要一段时间并不是马上连接上wifi热点的,这个时间就是连接时延
注意:如果大家不去考虑这个时延,那样会出现很大的问题,那样我们通过ip获取建立socket时候会报一个socket untouched异常 具体异常名字我没有详记

  1. 首先我们不能通过mWifiAdmin.connectToTarget(customScan.getScanResult(), "123456789");发送的通知 以及捕
    获具体通知进行wifi连接的处理 因为上述我已经阐明 通知的种类以及条数都不是固定的
  2. 我们不能认为mWifiAdmin.connectToTarget(customScan.getScanResult(), "123456789");执行完瞬间就可以连
    接上wifi了不能单纯的只根据这个 返回值是不是-1 来判断连接wifi成功与否 当然返回-1 肯定是失败 但是返回!-1的时
    候,由于连接是需要一段时间的,所以我们要加一个循环判断是否连接成功的线程 这样就可以在连接成功后进行我们自己的业务处理了.
  3. 循环判断wifi连接成功的线程需要注意 1.循环次数 2.失败后的界面反馈 可以起线程 那样需要handler来传递
    失败后的信息 也可以用异步任务

1和2 有两种可能 通过wifi列表连接具体wifi的 也可以 直接连接某个wifi热点的 代码如下:

@OnClick({R.id.btn_jump_temp, R.id.btn_scan})
    void onClicks(View v) {
        int flag = 0;
        switch (v.getId()) {
            case R.id.btn_scan:
                mWifiAdmin.openWifi();
                mWifiAdmin.startScan();//开始扫描  发送通知
                AppUtils.getInstance().showLoading(this);
                scanFlag = 1;
                break;
        case R.id.btn_jump_temp:
            if (!ArrayUtils.isEmpty(mWifiList)) {
                for (CustomScan customScan : mWifiList) {
                    if (customScan.isSelected()) {
                        flag = 1;
                        if (!TextUtils.isEmpty(mEdiPhoneNum.getText())) {
                            int i = mWifiAdmin.connectToTarget(customScan.getScanResult(), "123456789");// 发送通知
                            if (i != -1) {
                                AppUtils.getInstance().showLoading(WifiListActivity.this);
                                jumpNum = 1;
                                mConnectThread = new ConnectThread();
                                mConnectThread.start();
                                mLearRight.setClickable(true);
                            } else {
                                Toast.makeText(WifiListActivity.this, "连接失败", Toast.LENGTH_LONG).show();
                                mLearRight.setClickable(true);
                            }
                        }
                    }
                }
                if (flag == 0) {
                    Toast.makeText(this, "请先选择一个wifi热点", Toast.LENGTH_LONG).show();
                    mLearRight.setClickable(true);
                    return;
                }
            } else {
                Toast.makeText(this, "没有wifi列表", Toast.LENGTH_LONG).show();
                mLearRight.setClickable(true);
                return;
            }
            break;
        }
    }

@OnClick({R.id.btn_jump_temp, R.id.btn_scan})
    void onClicks(View v) {
        int flag = 0;
        switch (v.getId()) {
            case R.id.btn_scan:
                mWifiAdmin.openWifi();
                mWifiAdmin.startScan();//开始扫描  发送通知
                AppUtils.getInstance().showLoading(this);
                scanFlag = 1;
                break;
        case R.id.btn_jump_temp:
            if (!DeviceUtils.isWifiConnected(WifiListNullActivity.this)
                    || TextUtils.isEmpty(PreferenceSettingUtils.getIP(this))) {
                jumpNum = 1;
                AppUtils.getInstance().showLoading(WifiListNullActivity.this, "分机连接中...");
                 int i = mWifiAdmin.connectToTarget(PreferenceSettingUtils.getWifiName(WifiListNullActivity.this),
                                    PreferenceSettingUtils.getWifiPassword(WifiListNullActivity.this));// 发送通知
                    if (i == -1) {
                    Toast.makeText(WifiListNullActivity.this, "附近没有找到指定的分机", Toast.LENGTH_LONG).show();
                    }
                mConnectThread = new ConnectThread();
                mConnectThread.start();
            } else {
                startActivity(new Intent(this, DetctTempActivity.class));
            }
            break;
        }
    }

3.也有两种 起线程异步任务 代码如下:

    public class ConnectThread extends Thread {// 连接Thread
        @Override
        public void run() {
            int count = 0;
            while (true) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                if (DeviceUtils.isWifiConnected(WifiListActivity.this)) {//  连接成功
                    WifiAdmin wifiAdmin = new WifiAdmin(WifiListActivity.this);
                    String ip = wifiAdmin.intToIp(wifiAdmin.getServerIPAddress());
                    PreferenceSettingUtils.setIP(WifiListActivity.this, ip);
                    // 启动net work
                    try {
                        Message msg = new Message();
                        msg.obj = "WIFI连接成功!";
                        msg.what = 1;
                        LinkDetectedHandler.sendMessage(msg);
                    } catch (Exception e) {
                        Message msg = new Message();
                        msg.obj = "获取socket失败";
                        msg.what = 0;
                        LinkDetectedHandler.sendMessage(msg);
                        e.printStackTrace();
                    }
                    break;
                }
                if (count++ > 10) {
                    Message msg = new Message();
                    msg.obj = "WIFI连接失败!";
                    msg.what = -1;
                    LinkDetectedHandler.sendMessage(msg);
                    break;
                }
            }
            super.run();
        }
    }



public class SocketConnectTask extends AsyncTask<Integer, Void, Integer> {
    //后面尖括号内分别是参数(线程休息时间),进度(publishProgress用到),返回值 类型

    private Context mContext;
    private Thread mThread;


    public SocketConnectTask(Context context, Thread thread) {
        mContext = context;
        mThread = thread;
    }

    public SocketConnectTask(Context context) {
        mContext = context;
    }
    /*
        * 第一个执行的方法
        * 执行时机:在执行实际的后台操作前,被UI 线程调用
        * 作用:可以在该方法中做一些准备工作,如在界面上显示一个进度条,或者一些控件的实例化,这个方法可以不用实现。
        * @see android.os.AsyncTask#onPreExecute()
        */
    @Override
    protected void onPreExecute() {
        //AppUtils.getInstance().showLoading(mContext);
        super.onPreExecute();
    }

     /*
     * 执行时机:在onPreExecute 方法执行后马上执行,该方法运行在后台线程中
     * 作用:主要负责执行那些很耗时的后台处理工作。该方法是抽象方法,子类必须实现。
     * @see android.os.AsyncTask#doInBackground(Params[])
     */

    @Override
    protected Integer doInBackground(Integer... params) {
        int count = 0;
        while (true) {
            try {
                Thread.sleep(2000);
                if (DeviceUtils.isWifiConnected(mContext)) {
                    Thread.sleep(1000);
                    WifiAdmin wifiAdmin = new WifiAdmin(mContext);
                    String ip = wifiAdmin.intToIp(wifiAdmin.getServerIPAddress());
                    PreferenceSettingUtils.setIP(mContext, ip);
                    return 1;
                }
                if (count++ > 10) return -1;

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }


    /*
    * 执行时机:在doInBackground 执行完成后,将被UI 线程调用
    * 作用:后台的计算结果将通过该方法传递到UI 线程,并且在界面上展示给用户
    * result:上面doInBackground执行后的返回值,所以这里是"执行完毕"
    * @see android.os.AsyncTask#onPostExecute(java.lang.Object)
    */
    @Override
    protected void onPostExecute(Integer result) {
        //AppUtils.getInstance().dismissLoading(mContext);
        switch (result) {
            case 1:
                if (mThread != null) {
                    mThread.start();
                }
                Toast.makeText(mContext, "wifi连接成功", Toast.LENGTH_SHORT).show();
                break;
            case -1:
                Toast.makeText(mContext, "wifi连接异常", Toast.LENGTH_SHORT).show();
                break;
        }

        super.onPostExecute(result);
    }
}

总结:wifi连接就这些了 其主要问题 就是在连接到热点 建立socket的时候一定要注意这个时延 不能去通过通知的捕获来处理业务逻辑socket建立的成功与否 是在wifi热点必须连接成功的前提下。下面章节我可能会叙述一些 wifi通信协议以及通信中线程中处理业务的问题。如有疑问可以联系我。

猜你喜欢

转载自blog.csdn.net/shentanweilan9/article/details/79142565
今日推荐