您当前的位置:首页 > 计算机 > 编程开发 > 安卓(android)开发

PowerManager.WakeLock和WifiManager.WifiLock

时间:02-05来源:作者:点击数:

1、PowerManager.WakeLock

WakeLock翻译过来意思为:唤醒锁。作用就是用于保持设备运行,如手机熄屏后可能CPU就停止运转了,通过获取唤醒锁,就可以保持CPU不停止。此功能需要android.permission.WAKE_LOCK权限。

用法上相当简单,查看API中的函数即可知道如何使用。在官网也有对此的一些描述:连接在此,这里把原文抄过来,预防链接失效:

当设计在后台播放媒体内容的应用时,设备可能会在您的 Service 运行时进入休眠状态。由于 Android 系统尝试在设备处于休眠状态时节省电量,因此系统会尝试关闭手机上任何不必要的功能,包括 CPU 和 WLAN 硬件。不过,如果您的 Service 正在播放或流式传输音乐,则您需要防止系统干扰播放。

为了确保您的 Service 在这些情况下能继续运行,您必须使用“唤醒锁”。唤醒锁定可以告诉系统:您的应用正在使用一些即使在手机处于闲置状态时也应该可用的功能。

注意:您应始终谨慎使用唤醒锁,并只使其保留必要的时长,因为它们会显著缩短设备的电池续航时间。

为确保 CPU 在 MediaPlayer 播放时继续运行,请在初始化 时调用 setWakeMode() 方法。完成该操作后,MediaPlayer 会在播放时保持我们指定的锁定,并在暂停或停止播放时释放这个锁:

    mediaPlayer = MediaPlayer().apply {
        // ... other initialization here ...
        setWakeMode(applicationContext, PowerManager.PARTIAL_WAKE_LOCK)
    }
    

这个连接是在讲播放器时的注意事项,比如我们在听音乐,不希熄屏后音乐就停止播放吧,这就要保持CPU的运行不能停止,此函数上还有说可以调用setScreenOnWhilePlaying(boolean screenOn)方法,优先用此方法,它会在播放的时候保持屏幕不灭,也实现了相同的功能,而且它不需要权限。如果我们的应用没有播放器,但是我也希望屏幕熄屏后我的程序还能保持运行,则可以查看上面的setWakeMode函数的源码实现,照着使用即可,如下:

private PowerManager.WakeLock mWakeLock = null;

public void setWakeMode(Context context, int mode) {
        boolean washeld = false;

        /* Disable persistant wakelocks in media player based on property */
        /* 根据属性禁用媒体播放器中的持久唤醒锁 */
        if (SystemProperties.getBoolean("audio.offload.ignore_setawake", false) == true) {
            Log.w(TAG, "IGNORING setWakeMode " + mode);
            return;
        }

        if (mWakeLock != null) {
            if (mWakeLock.isHeld()) {
                washeld = true; // 表示曾经持有WakeLock,如果曾经持有,则先释放掉,再创建一个新的并且持有,否则只创建出锁,并不调用acquire()来持有锁。
                mWakeLock.release();
            }
            mWakeLock = null;
        }

        PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
        // 对于ON_AFTER_RELEASE,官方文档描述说不能和PARTIAL_WAKE_LOCK一起使用,但是它的示例代码就是传了这个模式过来一起用的,这是什么情况??
        mWakeLock = pm.newWakeLock(mode|PowerManager.ON_AFTER_RELEASE, MediaPlayer.class.getName());
        mWakeLock.setReferenceCounted(false);
        if (washeld) {
            mWakeLock.acquire();
        }
    }

	/** 在需要保持CPU运行的时候就调用此方法,参数传true,需要释放锁的时候就传false */
	private void stayAwake(boolean awake) {
        if (mWakeLock != null) {
            if (awake && !mWakeLock.isHeld()) {
                mWakeLock.acquire();
            } else if (!awake && mWakeLock.isHeld()) {
                mWakeLock.release();
            }
        }
    }

嗯,此代表来自Android源码,这个使用方式肯定是规范的,可以直接Copy来用哈!!

看到有别的开源项目,在newWakeLock的时候,第一个参数使用的是:PowerManager.PARTIAL_WAKE_LOCK,具体有哪些区别以后有时间再深究。

2、WifiManager.WifiLock

WifiLock:这个很好理解了,就是锁定Wifi的,如用户锁屏后,可能过一会Wifi就断开了,断开Wifi就可省电,但是有时候我们不希望他断开,比如酷狗音乐,播放在线的音乐,熄屏后肯定希望Wifi不要断开,不然就播不了在线音乐了。

需要权限:android.permission.WAKE_LOCK,根PowerManager.WakeLock需要的权限是一样的。

关于Wifi锁,在上面的连接中也有提及:连接在此,原文如下:

如果您使用 WLAN 并通过网络流式传输媒体内容,则您可能也希望保持 WifiLock,该锁定必须手动获取和释放。因此,当您开始使用远程网址准备 MediaPlayer 时,您应创建并获取 WLAN 锁定。例如:

    val wifiManager = getSystemService(Context.WIFI_SERVICE) as WifiManager
    val wifiLock: WifiManager.WifiLock =
        wifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock")
    wifiLock.acquire()

当您暂停或停止媒体内容,或者当您不再需要网络时,应释放该锁定:

    wifiLock.release()

这个使用感觉跟PowerManager.WakeLock是一样的,上面的代码是最简单的使用,要想更好一点,可以参考上面PowerManager.WakeLock的那个实现方式。

我之前在看一个开源的音视频通话项目,发现他是这样使用这两个锁的:通过ConnectivityManager().getActiveNetworkInfo()来获取当前活动的网络,然后判断这个网络如果是Wifi网络的话就使用WifiManager.WifiLock,如果是移动网络的话就使用PowerManager.WakeLock。源码如下:

	private WifiLock mWifiLock;
	private PowerManager.WakeLock mCellularLock;

	/**
	 * Locks the network and start using it. Later, the network must be unlocked using @release.
	 * 锁定网络并开始使用它。之后,必须使用@release()解锁网络。
	 * 这个函数就做两个事情,获取激活的网络,判断网络如果是Wifi,就获取WifiLock,如果是移动网络就获取PowerManager.WakeLock
	 * @return true if succeed, false otherwise.
	 * @sa release
	 */
	public boolean acquire() {
		Log.d(TAG, "acquireNetworkLock()");

		final NetworkInfo networkInfo = NgnApplication.getConnectivityManager().getActiveNetworkInfo();
		if (networkInfo == null) {
			Log.e(TAG, "没有可用网络:ConnectivityManager().getActiveNetworkInfo() == null");
			return false;
		}

		final int netType = networkInfo.getType();
		final int netSubType = networkInfo.getSubtype();
		Log.d(NgnNetworkService.TAG, String.format("netType=%d and netSubType=%d", netType, netSubType));

		if (networkInfo.getType() == ConnectivityManager.TYPE_WIFI && mWifiLock == null) {
			if (mWifiManager != null && mWifiManager.isWifiEnabled()) {
				mWifiLock = mWifiManager.createWifiLock(WifiManager.WIFI_MODE_FULL, NgnNetworkService.TAG + "Lock_Wifi");
				final WifiInfo wifiInfo = mWifiManager.getConnectionInfo();
				if (wifiInfo != null && mWifiLock != null) {
					// 获取Wifi的连接状态,满足指定的Wifi状态时才使用Wifi锁
					final DetailedState detailedState = WifiInfo.getDetailedStateOf(wifiInfo.getSupplicantState());
					if (detailedState == DetailedState.CONNECTED 					// 此状态表示IP通信应该可用
							|| detailedState == DetailedState.CONNECTING			// 此状态表示当前正在建立数据连接
							|| detailedState == DetailedState.OBTAINING_IPADDR) {	// 此状态表示等待DHCP服务器的响应,以便分配IP地址信息。
						mWifiLock.acquire(); // 获取Wifi锁,这样可以保证用户熄屏空闲时Wifi不会断开。
					}
				}
			} else {
				Log.d(NgnNetworkService.TAG, "WiFi not enabled");
			}
		} else if (mCellularLock == null && isMobileNetwork(networkInfo)) {
			PowerManager pm = NgnApplication.getPowerManager();
			mCellularLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, NgnNetworkService.TAG + "Lock_Cellular");
			if (mCellularLock != null) {
				mCellularLock.acquire();
			}
		}
		return true;
	}
	
	private static boolean isMobileNetwork(final NetworkInfo networkInfo) {
		if (networkInfo != null) {
			final int netType = networkInfo.getType();
			/*
			ConnectivityManager.TYPE_WIMAX :即全球微波互联接入。是一项新兴的宽带无线接入技术,能提供面向互联网的高速连接,
			数据传输距离最远可达50km。WiMAX还具有QoS保障、传输速率高、业务丰富多样等优点。WiMAX的技术起点较高,
			采用了代表未来通信技术发展方向的OFDM/OFDMA、AAS、MIMO等先进技术,随着技术标准的发展,WiMAX逐步实现宽带业务的移动化,
			而3G则实现移动业务的宽带化,两种网络的融合程度会越来越高。
			 */
			if ((netType == ConnectivityManager.TYPE_MOBILE || netType == ConnectivityManager.TYPE_WIMAX)) {
				final int netSubType = networkInfo.getSubtype();
				// 下面的判断写得像放屁一样,总结就是netSubType >= 1的都认为是移动网络,这就包含了所有的子网络类型了。所以这里可以直接返回true的,不需要判断子网络类型
				return ((netSubType >= TelephonyManager.NETWORK_TYPE_UMTS)		// 值是3
						|| // HACK
						(netSubType == TelephonyManager.NETWORK_TYPE_GPRS)		// 值是1
						|| (netSubType == TelephonyManager.NETWORK_TYPE_EDGE)); // 值是2
			}
		}
		return false;
	}

	/**
	 * Unlocks the network and stop using it. The network must be locked first using @ref acquire.
	 * @return true is succeed, false otherwise.
	 * @sa acquire
	 */
	@Override
	public boolean release() {
		Log.d(TAG, "releaseNetworkLock()");
		if (mWifiLock != null) {
			if (mWifiLock.isHeld()){
				mWifiLock.release();
			}
			mWifiLock = null;
		}
		if (mCellularLock != null) {
			if (mCellularLock.isHeld()){
				mCellularLock.release();
			}
			mCellularLock = null;
		}
		return true;
	}
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门