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里面添加权限
    <!-- wifi控制和状态权限 -->
    <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" />
    <!-- 6.0以上打开蓝牙和wifi最好加上定位权限,获取wifi列表要用 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <!-- 获取IP地址 -->
    <uses-permission android:name="android.permission.INTERNET"/>
/**
 * 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);
    }
}
  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;
}
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 就连上了。


    不可移除wifi.jpg
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通信

1.gif
2.gif
3.gif
tcp.gif
udp.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,233评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,013评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,030评论 0 241
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,827评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,221评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,542评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,814评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,513评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,225评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,497评论 2 244
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,998评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,342评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,986评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,055评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,812评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,560评论 2 271
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,461评论 2 266