Android Wifi连接控制、TCP、UDP通信,6.0以上适配

本文章包含内容

  1. Wifi连接控制、Wifi广播接收,适配了Android6.0以上的版本
  2. Wifi下的TCP通信
  3. Wifi下的UDP通信
  4. Github项目地址
  5. 码云项目地址

最近公司要开发智能家居,APP要作为遥控器和控制中心,其中的原理就是智能设备开机先作为一个热点,发射Wifi信号,然后手机连上这个热点(Wifi控制),手机和智能设备建立了连接后,将家里路由器wifi的账号密码通过TCP协议,Socket通信发送给智能设备,最后智能设备收到后就能连上家里的路由器了。


第一步当然就是Wifi控制了,网上资料挺多的,但是Andorid6.0以上的版本,由于权限问题,不能连上指定Wifi,这就把解决方法分享出来,有一些简单的代码,我用Kotlin来练手了,关键的还是用Java (#^.^#)。

  • Manifest里面添加权限
    
    <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    
    <uses-permission android:name="android.permission.INTERNET"/>
  • Andorid 6.0以上要动态权限
    参考资料是严振杰的动态权限方法
    Github
    Android 6.0 运行时权限管理最佳实践

  • Wifi广播,用于监听Wifi的状态,demo动态注册广播
    参考资料
    Android-WiFi开发之 WiFi广播监听
    Android SupplicantState

/**
 * Created by bao on 2018/3/21.
 * wifi状态广播
 */
public class WifiBroadcastReceiver extends BroadcastReceiver
{
    private WifiControlUtils wifiControlUtils;

    @Override
    public void onReceive(Context context, Intent intent)
    {
        wifiControlUtils = new WifiControlUtils(context);

        //wifi正在改变状态
        if (WifiManager.WIFI_STATE_CHANGED_ACTION.equals(intent.getAction()))
        {
            //获取wifi状态
            int wifiState = intent.getIntExtra(WifiManager.EXTRA_WIFI_STATE, WifiManager.WIFI_STATE_DISABLING);
            switch (wifiState)
            {
                case WifiManager.WIFI_STATE_DISABLED:
                    //wifi已经关闭
                    break;
                case WifiManager.WIFI_STATE_DISABLING:
                    //wifi正在关闭
                    break;
                case WifiManager.WIFI_STATE_ENABLED:
                    //wifi已经开启
                    break;
                case WifiManager.WIFI_STATE_ENABLING:
                    //wifi正在开启
                    break;
            }
        } else if (WifiManager.NETWORK_STATE_CHANGED_ACTION.equals(intent.getAction()))
        {
            //网络状态改变
            NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
            if (NetworkInfo.State.DISCONNECTED.equals(info.getState()))
            {
                //wifi网络连接断开
            } else if (NetworkInfo.State.CONNECTED.equals(info.getState()))
            {
                //获取当前网络,wifi名称
                ToastUtils.showLong(context.getString(R.string.wifi_connected, wifiControlUtils.getWifiInfo().getSSID()));
            }
        } else if (WifiManager.SUPPLICANT_STATE_CHANGED_ACTION.equals(intent.getAction()))
        {
            //wifi密码错误广播
            SupplicantState netNewState = intent.getParcelableExtra(WifiManager.EXTRA_NEW_STATE);
            //错误码
            int netConnectErrorCode = intent.getIntExtra(WifiManager.EXTRA_SUPPLICANT_ERROR, WifiManager.ERROR_AUTHENTICATING);
        }
    }
}
public class WifiControlActivity extends AppCompatActivity
{
    private WifiBroadcastReceiver wifiBroadcastReceiver;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.wifi_control_activity);
        ButterKnife.bind(this);

        //动态注册wifi状态广播
        wifiBroadcastReceiver = new WifiBroadcastReceiver();
        IntentFilter intentFilter = new IntentFilter();
        intentFilter.addAction(WifiManager.WIFI_STATE_CHANGED_ACTION);
        intentFilter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
        intentFilter.addAction(WifiManager.SUPPLICANT_STATE_CHANGED_ACTION);
        registerReceiver(wifiBroadcastReceiver, intentFilter);
    }

   @Override
    protected void onDestroy()
    {
        super.onDestroy();
        //注销广播
        unregisterReceiver(wifiBroadcastReceiver);
    }
}
  • Wifi控制工具,网上别人的资料在Andorid6.0 以上不能连接,对其工具进行改进一下,马上解决。
    参考资料 Android中自动连接到指定SSID的Wi-Fi
  1. 获取WifiManager,这里注意的就是context.getApplicationContext()获取,防止内存泄漏。
 public WifiControlUtils(Context context)
    {
        //获取wifiManager对象
        mWifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
    }
wifimanager.jpg

2.Wifi 的信息 WifiInfo,获取之前,要先判断 WifiManager 是否为空,因为 Wifi 状态会经常改变,所以获取之前判断一下是否为空,而且每次都重新获取,拿最新的 Wifi 信息。

/**
 * 获取wifi连接信息
 **/
public WifiInfo getWifiInfo()
{
    if (mWifiManager != null)
    {
        return mWifiManager.getConnectionInfo();
    }
    return null;
}
Android Wifi连接控制、TCP、UDP通信,6.0以上适配_第1张图片
wifiinfo.jpg

3.Andorid 6.0以上的版本,权限改变,连接Wifi不一样。

  • 对于6.0以上,如果你要连接不是自己创建的配置,只需要在mWifiManager.getConfiguredNetworks(),翻出以前连接过的的Wifi 配置,获取对应的netId,就能重新连接上。
  • 如果以前连接过的 Wifi 密码改了,但是名称没变,你是连不上的,也没权限去修改密码和删除(可能就是为了安全吧),你就要手动去处理这个Wifi 信息了。
  • APP没有权限删除之前的连接过的 Wifi ,包括APP以前本身创建的 Wifi(先创建了,重装或者更新后,都不算是自己创建了)。
  • 对于从来都没连接过的 Wifi,或者是删除过的 Wifi(相当于没连接过),和以前一样,只要用 SSID (Wifi名)、密码、加密方式创建新的 WifiConfiguration,无密码的就是要 SSID;然后 mWifiManager.enableNetwork 就连上了。


    Android Wifi连接控制、TCP、UDP通信,6.0以上适配_第2张图片
    不可移除wifi.jpg
Android Wifi连接控制、TCP、UDP通信,6.0以上适配_第3张图片
removeNetWork之后可以不用saveConfiguration.jpg
  • 我的改进后的处理方式就是这样的。
/**
 * 连接指定wifi
 * 6.0以上版本,直接查找时候有连接过,连接过的拿出wifiConfiguration用
 * 不要去创建新的wifiConfiguration,否者失败
 */
public void addNetWork(String SSID, String password, int Type)
{
    int netId = -1;
    /*先执行删除wifi操作,1.如果删除的成功说明这个wifi配置是由本APP配置出来的;
                       2.这样可以避免密码错误之后,同名字的wifi配置存在,无法连接;
                       3.wifi直接连接成功过,不删除也能用, netId = getExitsWifiConfig(SSID).networkId;*/
    if (removeWifi(SSID))
    {
        //移除成功,就新建一个
        netId = mWifiManager.addNetwork(createWifiInfo(SSID, password, Type));
    } else
    {
        //删除不成功,要么这个wifi配置以前就存在过,要么是还没连接过的
        if (getExitsWifiConfig(SSID) != null)
        {
            //这个wifi是连接过的,如果这个wifi在连接之后改了密码,那就只能手动去删除了
            netId = getExitsWifiConfig(SSID).networkId;
        } else
        {
            //没连接过的,新建一个wifi配置
            netId = mWifiManager.addNetwork(createWifiInfo(SSID, password, Type));
        }
    }

    //这个方法的第一个参数是需要连接wifi网络的networkId,第二个参数是指连接当前wifi网络是否需要断开其他网络
    //无论是否连接上,都返回true。。。。
    mWifiManager.enableNetwork(netId, true);
}

/**
 * 获取配置过的wifiConfiguration
 */
public WifiConfiguration getExitsWifiConfig(String SSID)
{
    wifiConfigurationList = mWifiManager.getConfiguredNetworks();
    for (WifiConfiguration wifiConfiguration : wifiConfigurationList)
    {
        if (wifiConfiguration.SSID.equals("\"" + SSID + "\""))
        {
            return wifiConfiguration;
        }
    }
    return null;
}

/**
 * 移除wifi,因为权限,无法移除的时候,需要手动去翻wifi列表删除
 * 注意:!!!只能移除自己应用创建的wifi。
 * 删除掉app,再安装的,都不算自己应用,具体看removeNetwork源码
 *
 * @param netId wifi的id
 */
public boolean removeWifi(int netId)
{
    return mWifiManager.removeNetwork(netId);
}

/**
 * 移除wifi
 *
 * @param SSID wifi名
 */
public boolean removeWifi(String SSID)
{
    if (getExitsWifiConfig(SSID) != null)
    {
        return removeWifi(getExitsWifiConfig(SSID).networkId);
    } else
    {
        return false;
    }
}
  1. 到这里基本结束了,TCP、UDP、Wifi更多详情,就下载源码看看吧,测试机小米6(Android8.0),三星S7edge(Andorid7.0),魅蓝note3(Android7.0),oppoR9s(Android7.0)均可以使用。小米6可以收wifi和把这个wifi热点分享出去,所以测试方便。
    TCP通信
    UDP通信

Android Wifi连接控制、TCP、UDP通信,6.0以上适配_第4张图片

Android Wifi连接控制、TCP、UDP通信,6.0以上适配_第5张图片Android Wifi连接控制、TCP、UDP通信,6.0以上适配_第6张图片

Android Wifi连接控制、TCP、UDP通信,6.0以上适配_第7张图片



你可能感兴趣的:(Android开发)