- mongodb - 在 MongoDB mapreduce 中,如何展平值对象?
- javascript - 对象传播与 Object.assign
- html - 输入类型 ="submit"Vs 按钮标签它们可以互换吗?
- sql - 使用 MongoDB 而不是 MS SQL Server 的优缺点
注意:事实证明,原始问题的假设是不正确的。在底部查看有关其编辑的更多详细信息。
现在是关于节电模式,而不是节电模式和打盹模式。它也不是关于 Service&BroadcastReceiver,而只是 BroadcastReceiver。
背景
从 Android Lollipop 开始,Google 引入了新的手动和自动方式来帮助节省电池电量:
“打瞌睡”模式和“节电模式”。
在某些情况下,由于这些技术,应用程序可能无法访问 Internet。
问题
我正在开发一个需要使用后台服务访问互联网的应用程序,该服务在特定情况下触发,如果收到重要信息,它会显示一些 UI。
作为用户,我注意到在某些情况下,它无法访问 Internet。
APP是否可以上网的检查是这样的:
public static boolean isInternetOn(Context context) {
final NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
return !(info == null || !info.isConnectedOrConnecting());
}
public class PowerSaverHelper {
public enum PowerSaveState {
ON, OFF, ERROR_GETTING_STATE, IRRELEVANT_OLD_ANDROID_API
}
public enum WhiteListedInBatteryOptimizations {
WHITE_LISTED, NOT_WHITE_LISTED, ERROR_GETTING_STATE, IRRELEVANT_OLD_ANDROID_API
}
public enum DozeState {
NORMAL_INTERACTIVE, DOZE_TURNED_ON_IDLE, NORMAL_NON_INTERACTIVE, ERROR_GETTING_STATE, IRRELEVANT_OLD_ANDROID_API
}
@NonNull
public static DozeState getDozeState(@NonNull Context context) {
if (VERSION.SDK_INT < VERSION_CODES.M)
return DozeState.IRRELEVANT_OLD_ANDROID_API;
final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm == null)
return DozeState.ERROR_GETTING_STATE;
return pm.isDeviceIdleMode() ? DozeState.DOZE_TURNED_ON_IDLE : pm.isInteractive() ? DozeState.NORMAL_INTERACTIVE : DozeState.NORMAL_NON_INTERACTIVE;
}
@NonNull
public static PowerSaveState getPowerSaveState(@NonNull Context context) {
if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP)
return PowerSaveState.IRRELEVANT_OLD_ANDROID_API;
final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm == null)
return PowerSaveState.ERROR_GETTING_STATE;
return pm.isPowerSaveMode() ? PowerSaveState.ON : PowerSaveState.OFF;
}
@NonNull
public static WhiteListedInBatteryOptimizations getIfAppIsWhiteListedFromBatteryOptimizations(@NonNull Context context, @NonNull String packageName) {
if (VERSION.SDK_INT < VERSION_CODES.M)
return WhiteListedInBatteryOptimizations.IRRELEVANT_OLD_ANDROID_API;
final PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
if (pm == null)
return WhiteListedInBatteryOptimizations.ERROR_GETTING_STATE;
return pm.isIgnoringBatteryOptimizations(packageName) ? WhiteListedInBatteryOptimizations.WHITE_LISTED : WhiteListedInBatteryOptimizations.NOT_WHITE_LISTED;
}
//@TargetApi(VERSION_CODES.M)
@SuppressLint("BatteryLife")
@RequiresPermission(permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS)
@Nullable
public static Intent prepareIntentForWhiteListingOfBatteryOptimization(@NonNull Context context, @NonNull String packageName, boolean alsoWhenWhiteListed) {
if (VERSION.SDK_INT < VERSION_CODES.M)
return null;
if (ContextCompat.checkSelfPermission(context, permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) == PackageManager.PERMISSION_DENIED)
return null;
final WhiteListedInBatteryOptimizations appIsWhiteListedFromPowerSave = getIfAppIsWhiteListedFromBatteryOptimizations(context, packageName);
Intent intent = null;
switch (appIsWhiteListedFromPowerSave) {
case WHITE_LISTED:
if (alsoWhenWhiteListed)
intent = new Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS);
break;
case NOT_WHITE_LISTED:
intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).setData(Uri.parse("package:" + packageName));
break;
case ERROR_GETTING_STATE:
case IRRELEVANT_OLD_ANDROID_API:
default:
break;
}
return intent;
}
/**
* registers a receiver to listen to power-save events. returns true iff succeeded to register the broadcastReceiver.
*/
@TargetApi(VERSION_CODES.M)
public static boolean registerPowerSaveReceiver(@NonNull Context context, @NonNull BroadcastReceiver receiver) {
if (VERSION.SDK_INT < VERSION_CODES.M)
return false;
IntentFilter filter = new IntentFilter();
filter.addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED);
context.registerReceiver(receiver, filter);
return true;
}
}
./adb shell settings put global low_power [1|0]
./adb shell dumpsys deviceidle step [light|deep]
./adb shell dumpsys deviceidle force-idle
onReceive
上检查互联网连接函数,我看到它返回false。从它启动的服务也是如此,即使它是一个前台服务。查看 NetworkInfo 结果,它是“BLOCKED”,它的状态确实是“DISCONNECTED”。
./adb shell settings put global low_power 1
命令,或作为用户),然后启动它,接受权限,关闭 Activity ,然后从另一部手机调用此手机。您会注意到,在 Activity 中,它显示有 Internet 连接,而在 BroadcastReceiver 上,它表示没有。
public class PhoneBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
Log.d("AppLog", "PhoneBroadcastReceiver:isInternetOn:" + isInternetOn(context));
}
public static boolean isInternetOn(Context context) {
final NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
return !(info == null || !info.isConnectedOrConnecting());
}
}
<manifest package="com.example.user.myapplication" xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
<application
android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<receiver android:name=".PhoneBroadcastReceiver">
<intent-filter >
<action android:name="android.intent.action.PHONE_STATE"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.NEW_OUTGOING_CALL"/>
</intent-filter>
</receiver>
</application>
</manifest>
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.d("AppLog", "MainActivity: isInternetOn:" + PhoneBroadcastReceiver.isInternetOn(this));
if (VERSION.SDK_INT >= VERSION_CODES.M) {
requestPermissions(new String[]{permission.READ_PHONE_STATE, permission.PROCESS_OUTGOING_CALLS}, 1);
}
}
}
最佳答案
所以我从 issue tracker 下载了你的示例应用程序,按照您描述的方式对其进行了测试,并在运行 Android 6.0.1 的 Nexus 5 上发现了这些结果:
测试 1 的条件:
adb shell settings put global low_power 1
设置的省电模式adb tcpip <port>
通过无线连接的设备和 adb connect <ip>:<port>
D/AppLog: PhoneBroadcastReceiver:isInternetOn:false
D/AppLog: PhoneBroadcastReceiver:isInternetOn:false
public class PhoneService extends Service {
public void onCreate() {
super.onCreate();
startForeground(1, new Notification.Builder(this)
.setSmallIcon(R.mipmap.ic_launcher_foreground)
.setContentTitle("Test title")
.setContentText("Test text")
.getNotification());
}
public int onStartCommand(Intent intent, int flags, int startId) {
final String msg = "PhoneService:isInternetOn:" + isInternetOn(this);
Log.d("AppLog", msg);
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
return START_STICKY;
}
public static boolean isInternetOn(Context context) {
final NetworkInfo info = ((ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE)).getActiveNetworkInfo();
return !(info == null || !info.isConnectedOrConnecting());
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
D/AppLog: PhoneService:isInternetOn:false
D/AppLog: PhoneService:isInternetOn:true
这个测试很有趣,第一个日志没有给我互联网连接,但是第二个日志给了我互联网连接,在第一次之后大约 4 秒,并且在它建立前台服务之后。当我第二次运行测试时,两个日志都是真实的。这似乎表明
startForeground
之间的延迟函数被调用,系统将应用程序置于前台。
adb shell dumpsys deviceidle force-idle
运行了测试 2 和 3 ,并得到与测试 3 类似的结果,其中第一个日志未连接,但所有后续日志都显示 Internet 连接。
To help improve battery life, battery saver reduces your device's performance and limits vibration, location services, and most background data. Email, messaging, and other apps that rely on syncing may not update unless you open them.
@Override
public void onCreate() {
super.onCreate();
Log.d("AppLog", "MyService:onCreate isInternetOn:" + PhoneBroadcastReceiver.isInternetOn(this));
if (!PhoneBroadcastReceiver.isInternetOn(this)) {
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
final ConnectivityManager connectivityManager = (ConnectivityManager) getApplicationContext().getSystemService(CONNECTIVITY_SERVICE);
connectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(), new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
//Use this network object to perform network operations
connectivityManager.unregisterNetworkCallback(this);
}
});
}
}
}
如果有您可以使用的网络连接,这将立即返回,或者等到有连接。从这里开始,您可能只需使用
Handler
如果一段时间后您没有收到任何结果,请取消注册,尽管我可能只是让它保持 Activity 状态。
@Override
public void onCreate() {
super.onCreate();
Log.d("AppLog", "MyService:onCreate isInternetOn:" + PhoneBroadcastReceiver.isInternetOn(this));
if (!PhoneBroadcastReceiver.isInternetOn(this)) {
if (VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
final ConnectivityManager connectivityManager = (ConnectivityManager) getApplicationContext().getSystemService(CONNECTIVITY_SERVICE);
connectivityManager.registerNetworkCallback(new NetworkRequest.Builder()
.addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
.build(), new ConnectivityManager.NetworkCallback() {
@Override
public void onAvailable(Network network) {
isConnected(network); //Probably add this to a log output to verify this actually works for you
connectivityManager.unregisterNetworkCallback(this);
}
});
}
}
}
public static boolean isConnected(Network network) {
if (network != null) {
try {
URL url = new URL("http://www.google.com/");
HttpURLConnection urlc = (HttpURLConnection)network.openConnection(url);
urlc.setRequestProperty("User-Agent", "test");
urlc.setRequestProperty("Connection", "close");
urlc.setConnectTimeout(1000); // mTimeout is in seconds
urlc.connect();
if (urlc.getResponseCode() == 200) {
return true;
} else {
return false;
}
} catch (IOException e) {
Log.i("warning", "Error checking internet connection", e);
return false;
}
}
return false;
}
关于android - 省电 + 通话 Intent => 没有互联网?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45976482/
我在 C# 中有一个轮询循环,需要在 上每 100 微秒轮询一次。平均 (当然,考虑到没有因内核短缺而由Windows进行过多的抢占式线程上下文切换)。 由于没有时间重新安排,Sleep(1) 不会这
注意:事实证明,原始问题的假设是不正确的。在底部查看有关其编辑的更多详细信息。 现在是关于节电模式,而不是节电模式和打盹模式。它也不是关于 Service&BroadcastReceiver,而只是
我是一名优秀的程序员,十分优秀!