Android 开启WiFi 热点的一些适配方案

栏目: IOS · Android · 发布时间: 6年前

内容简介:博主又来更新文章了,有点墨迹哈,很久才来一篇文章,不讲究文章量的大小,只在乎内容的实用性,帮助每一个开发者,避过一些不必要的坑,废话不多说了,文章的内容就是说各种版本手机通过代码如何开启热点,文章也比较简洁,不会有太多的啰嗦话首先呢,通过Android 对应Api的源码(哈哈哈上来就源码,不看源码怎么知道开热点啊)就可以找到如何开热点的,在我们手机的设置页面肯定会有开启热点的功能,然后呢 就去找对于版本号的Api, 代码我就贴出来了,简洁的代码 有兴趣的同学可以去源码官网查看TetherSettings.

博主又来更新文章了,有点墨迹哈,很久才来一篇文章,不讲究文章量的大小,只在乎内容的实用性,帮助每一个开发者,避过一些不必要的坑,废话不多说了,文章的内容就是说各种版本手机通过代码如何开启热点,文章也比较简洁,不会有太多的啰嗦话

不同版本开启热点的方式

首先呢,通过Android 对应Api的源码(哈哈哈上来就源码,不看源码怎么知道开热点啊)就可以找到如何开热点的,在我们手机的设置页面肯定会有开启热点的功能,然后呢 就去找对于版本号的Api, 代码我就贴出来了,简洁的代码 有兴趣的同学可以去源码官网查看TetherSettings.java 源码

  • 6.0之前的开启wifi 热点的方式
public void onClick(DialogInterface dialogInterface, int button) {
       if (button == DialogInterface.BUTTON_POSITIVE) {
           mWifiConfig = mDialog.getConfig();
           if (mWifiConfig != null) {
               /**
                * if soft AP is stopped, bring up
                * else restart with new config
                * TODO: update config on a running access point when framework support is added
                */
               if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
                   mWifiManager.setWifiApEnabled(null, false);
                   mWifiManager.setWifiApEnabled(mWifiConfig, true);
               } else {
                   mWifiManager.setWifiApConfiguration(mWifiConfig);
               }
               int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig);
               mCreateNetwork.setSummary(String.format(getActivity().getString(CONFIG_SUBTEXT),
                       mWifiConfig.SSID,
                       mSecurityType[index]));
           }
       }
   }


复制代码

从源码上可以看到 开启热是通过wifimanager setWifiApEnabled的方式走的

  • 8.0之后开启wifi热点的方式
public void onClick(DialogInterface dialogInterface, int button) {
       if (button == DialogInterface.BUTTON_POSITIVE) {
           mWifiConfig = mDialog.getConfig();
           if (mWifiConfig != null) {
               /**
                * if soft AP is stopped, bring up
                * else restart with new config
                * TODO: update config on a running access point when framework support is added
                */
               if (mWifiManager.getWifiApState() == WifiManager.WIFI_AP_STATE_ENABLED) {
                   Log.d("TetheringSettings",
                           "Wifi AP config changed while enabled, stop and restart");
                   mRestartWifiApAfterConfigChange = true;
                   mCm.stopTethering(TETHERING_WIFI);
               }
               mWifiManager.setWifiApConfiguration(mWifiConfig);
               int index = WifiApDialog.getSecurityTypeIndex(mWifiConfig);
               mCreateNetwork.setSummary(String.format(getActivity().getString(CONFIG_SUBTEXT),
                       mWifiConfig.SSID,
                       mSecurityType[index]));
           }
       }
   }

复制代码

从上面的代码可以看到变了,变得不认识了,不是通过wifiManager的方式走的了,而是通过更新config 文件然后通过别的方式来开启热点的,下面的代码就是开启热点的方式。

private void startTethering(int choice) {
       if (choice == TETHERING_BLUETOOTH) {
           // Turn on Bluetooth first.
           BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
           if (adapter.getState() == BluetoothAdapter.STATE_OFF) {
               mBluetoothEnableForTether = true;
               adapter.enable();
               mBluetoothTether.setSummary(R.string.bluetooth_turning_on);
               mBluetoothTether.setEnabled(false);
               return;
           }
       }

       mCm.startTethering(choice, true, mStartTetheringCallback, mHandler);
   }

复制代码

通过上面代码可以看到 开启的方式是走ConnectivityManager方式来开启热点的。而不是通过wifimanager了。

  • 6.0-8.0(7.0,7.1)的开启方式 查看setting源码可以看到同样也是通过8.0方式走的,通过ConnectivityManager 进行开启wifi 热点的。但是虽然是通过8.0方式走的,我们却没有权限进行开启。很奇怪下面我会相信讲解为什么。 7.0也是可以通过wifimanager开启热点的,但是到了7.1之后有些手机是可以的,有些手机是可以开启热点但是无法连接。然后看了7.1源码可以看到7.1之后开启源码的方式也是通过ConnectivityManager方式来进行开启的。不是通过wifimanager 进行开启热点。但是为什么还可以用wifimanager呢,是因为在7.1这个wifimanager的setWifiApEnabled方式没有废弃还是可以用的。

不同版本我们如何通过代码进行开启热点。

在说如何开启热点的代码时候,还是会带大家看一点点的源码。然后看看如何在自己项目中用代码进行开启。

  • 上面也介绍了通过哪个类和哪个方法进行开启wifi,但是知道了还是不行的。
  • 首选我们来看一下6.0 Wifimanager的方式通过setWifiApEnabled进行开启热点方式的源码
/**
    * Start AccessPoint mode with the specified
    * configuration. If the radio is already running in
    * AP mode, update the new configuration
    * Note that starting in access point mode disables station
    * mode operation
    * @param wifiConfig SSID, security and channel details as
    *        part of WifiConfiguration
    * @return {@code true} if the operation succeeds, {@code false} otherwise
    *
    * @hide Dont open up yet
    */
   public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
       try {
           mService.setWifiApEnabled(wifiConfig, enabled);
           return true;
       } catch (RemoteException e) {
           return false;
       }
   }

复制代码

可以看到这个Api已经被隐藏了。大兄弟们,你们是没法玩耍的。只能干瞪眼

  • 然后我们在看一下7.0,7.1 Wifimanager的方式通过setWifiApEnabled开热点的方式。发现和6.0没有什么区别
/**
    * Start AccessPoint mode with the specified
    * configuration. If the radio is already running in
    * AP mode, update the new configuration
    * Note that starting in access point mode disables station
    * mode operation
    * @param wifiConfig SSID, security and channel details as
    *        part of WifiConfiguration
    * @return {@code true} if the operation succeeds, {@code false} otherwise
    *
    * @hide
    */
   @SystemApi
   public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
       try {
           mService.setWifiApEnabled(wifiConfig, enabled);
           return true;
       } catch (RemoteException e) {
           throw e.rethrowFromSystemServer();
       }
   }


复制代码

看到没有变成了系统级别的Api 不紧隐藏了,还变成了系统级别的Api,而且发现出现exec的时候直接返回exception而不是返回false 直接返回false 这个无关紧要

  • 然后我们在看一下8.1的Wifimanager setWifiApEnabled 源码,发现这个api已经被废弃了。
/**
    * This call is deprecated and removed.  It is no longer used to
    * start WiFi Tethering.  Please use {@link ConnectivityManager#startTethering(int, boolean,
    * ConnectivityManager#OnStartTetheringCallback)} if
    * the caller has proper permissions.  Callers can also use the LocalOnlyHotspot feature for a
    * hotspot capable of communicating with co-located devices {@link
    * WifiManager#startLocalOnlyHotspot(LocalOnlyHotspotCallback)}.
    *
    * @param wifiConfig SSID, security and channel details as
    *        part of WifiConfiguration
    * @return {@code false}
    *
    * @hide
    * @deprecated This API is nolonger supported.
    * @removed
    */
   @SystemApi
   @Deprecated
   @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED)
   public boolean setWifiApEnabled(WifiConfiguration wifiConfig, boolean enabled) {
       String packageName = mContext.getOpPackageName();

       Log.w(TAG, packageName + " attempted call to setWifiApEnabled: enabled = " + enabled);
       return false;
   }

复制代码

也就是说这个api啊 在8.1的时候已经被完全废弃了。直接给你返回false。只能通过8.0的方式进行开启wifi 热点了

那么接下来说的就是手动编写的代码了

由于都是隐藏的api 所以开启热点的方式几乎都是通过反射来进行的

  • 7.0之前的方式(代码直接copy)就不一一解释了。网上代码很多
public boolean createAp(boolean isOpen) {
        StringBuffer sb = new StringBuffer();
        try {
            mWifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
            if (mWifiManager.isWifiEnabled()) {
                mWifiManager.setWifiEnabled(false);
            }
            sb.append(1);
            WifiConfiguration netConfig = new WifiConfiguration();
            netConfig.SSID = "xiaomeng";
            netConfig.preSharedKey = "11111111";
            Log.d("oye", "WifiPresenter:createAp----->netConfig.SSID:"
                    + netConfig.SSID + ",netConfig.preSharedKey:" + netConfig.preSharedKey + ",isOpen=" + isOpen);
            netConfig.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
            netConfig.allowedProtocols.set(WifiConfiguration.Protocol.RSN);
            netConfig.allowedProtocols.set(WifiConfiguration.Protocol.WPA);
            sb.append(2);
            if (isOpen) {
                netConfig.allowedKeyManagement.set(4);
                sb.append(3);
            } else {
                netConfig.allowedKeyManagement.set(4);
                sb.append(4);
            }
            netConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.CCMP);
            netConfig.allowedPairwiseCiphers.set(WifiConfiguration.PairwiseCipher.TKIP);
            netConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.CCMP);
            netConfig.allowedGroupCiphers.set(WifiConfiguration.GroupCipher.TKIP);
            sb.append(5);

            Method method = mWifiManager.getClass().getMethod("setWifiApEnabled", WifiConfiguration.class, Boolean.TYPE);
            sb.append(9);
            return (boolean) method.invoke(mWifiManager, netConfig, true);
            

        } catch (NoSuchMethodException e) {
            sb.append(10 + (e.getMessage()));
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            sb.append(11 + (e.getMessage()));
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            sb.append(12 + (e.getMessage()));
            e.printStackTrace();
        } 
        log.setText(sb.toString());

        return false;
    }

复制代码
  • 8.0的方式有两种方式开启热点

第一种直接调用系统提供的Api 但是不稳定 不靠谱(而且热点的名称和密码都是随机的)

WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
            wifiManager.startLocalOnlyHotspot(new WifiManager.LocalOnlyHotspotCallback() {

                @TargetApi(Build.VERSION_CODES.O)
                @Override
                public void onStarted(WifiManager.LocalOnlyHotspotReservation reservation) {
                    super.onStarted(reservation);
                    WifiConfiguration wifiConfiguration = reservation.getWifiConfiguration();
                    ssid = wifiConfiguration.SSID;
                    //可以通过config拿到开启热点的账号和密码
                    mHandler.obtainMessage(2018, wifiConfiguration).sendToTarget();
                }

                @Override
                public void onStopped() {
                    super.onStopped();
                }

                @Override
                public void onFailed(int reason) {
                    super.onFailed(reason);
                }

            }, mHandler);

复制代码

第二种开启的方式 有些机型会报NoSuchMethodException的异常 是因为有些机型他会多出来一个参数

private void startTethering() {
        mConnectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        if (mWifiManager != null) {
            int wifiState = mWifiManager.getWifiState();
            boolean isWifiEnabled = ((wifiState == WifiManager.WIFI_STATE_ENABLED) || (wifiState == WifiManager.WIFI_STATE_ENABLING));
            if (isWifiEnabled)
                mWifiManager.setWifiEnabled(false);
        }
        if (mConnectivityManager != null) {
            try {
                Field internalConnectivityManagerField = ConnectivityManager.class.getDeclaredField("mService");
                internalConnectivityManagerField.setAccessible(true);
                WifiConfiguration apConfig = new WifiConfiguration();
                apConfig.SSID = "cuieney";
                apConfig.preSharedKey = "12121212";

                StringBuffer sb = new StringBuffer();
                Class internalConnectivityManagerClass = Class.forName("android.net.IConnectivityManager");
                ResultReceiver dummyResultReceiver = new ResultReceiver(null);
                try {
                    
                    WifiManager wifiManager = (WifiManager) getApplicationContext().getSystemService(WIFI_SERVICE);
                    Method mMethod = wifiManager.getClass().getMethod("setWifiApConfiguration", WifiConfiguration.class);
                    mMethod.invoke(wifiManager, apConfig);
                    Method startTetheringMethod = internalConnectivityManagerClass.getDeclaredMethod("startTethering",
                            int.class,
                            ResultReceiver.class,
                            boolean.class);

                    startTetheringMethod.invoke(internalConnectivityManagerClass,
                            0,
                            dummyResultReceiver,
                            true);
                } catch (NoSuchMethodException e) {
                    Method startTetheringMethod = internalConnectivityManagerClass.getDeclaredMethod("startTethering",
                            int.class,
                            ResultReceiver.class,
                            boolean.class,
                            String.class);

                    startTetheringMethod.invoke(internalConnectivityManagerClass,
                            0,
                            dummyResultReceiver,
                            false,
                            context.getPackageName());
                } catch (InvocationTargetException e) {
                    sb.append(11 + (e.getMessage()));
                    e.printStackTrace();
                } finally {
                    log.setText(sb.toString());
                }


            } catch (Exception e) {
                Log.e("WifiApManager.startTethering", Log.getStackTraceString(e));
            }
        }
    }

复制代码

通过ConnectivityManager的startTethering方式进行开启wifi热点。可以配置名称和密码。是以系统级别的热点开启方式。

  • 7.1方式开启wifi热点

    首先呢,7.1的方式比较奇葩 有的机型可以通过Wifimanager的方式进行开启,也能正常连接,但是有的能开启但是无法正常连接。

    下面有几种方式可以成功的开启7.1热点同时也能连接上网

  1. 第一种方式,通过编译修改源码的方式进行,把ConnectivityManager这个包下面的源码单独编译一个jar 然后导入jar包到项目中使用。
  2. 第二种方式,通过8.0的方式走ConnectivityManager 的startTethering方式进行开启热点,但是要注意的是7.1的startTethering 参数和8.0的startTethering参数有所不同注意修改。但是即使这样还是会报一个错误(need MANAGE_USERS or CREATE_USERS permission to: query user)没有权限调用这个Api同时还会出现一个Exec异常InvocationTargetException 就是说你这个app不是系统app需要权限,那我们就把他变成系统app,如何才能变成呢,下载签名 工具 签名包可以在这里下载 然后通过命令 java -jar signapk.jar platform.x509.pem platform.pk8 src.apk dst.apk 进行签名 adb push 到你的手机里的/system/app/ 目录下 。别忘了chmod更改 apk权限 同时reboot 最后可以通过adb shell dumpsys package 包名 查看你的app所有权限。自此 7.1版本的热点就可以启动了。 具体操作可以给老铁们一份连接
  3. 第三种方式,和大家说一下,之所以开启了wifi热点成功,但是却没有成功连接是因为。DHCP没有给这个热点分配IP地址,热点开启了,却没有激活这个热点是因为没有分配IP,这个我在尝试,有结果了立马给老铁们上代码。

ending

花了个把小时写的东西,希望给老铁们带来的是知识的储备而不是时间的浪费。不早了不早了下班了,


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Dive Into Python 3

Dive Into Python 3

Mark Pilgrim / Apress / 2009-11-6 / USD 44.99

Mark Pilgrim's Dive Into Python 3 is a hands-on guide to Python 3 (the latest version of the Python language) and its differences from Python 2. As in the original book, Dive Into Python, each chapter......一起来看看 《Dive Into Python 3》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

URL 编码/解码
URL 编码/解码

URL 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换