gpt4 book ai didi

android - 省电 + 通话 Intent => 没有互联网?

转载 作者:IT老高 更新时间:2023-10-28 22:03:41 26 4
gpt4 key购买 nike

注意:事实证明,原始问题的假设是不正确的。在底部查看有关其编辑的更多详细信息。

现在是关于节电模式,而不是节电模式和打盹模式。它也不是关于 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());
}

问题是我需要检查为什么有时会返回 false,因此如果失败,我们应该告诉用户(可能通过通知)数据无法访问,因为设备限制了应用程序,并提供给用户从电池优化中将应用程序列入白名单。

我不确定哪些会影响这一点:打瞌睡、省电模式或两者兼而有之,如果总是这样,对于所有设备,在所有情况下。

我试过的

我找到的是如何查询Doze模式和Battery-saver(节电)模式:
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

问题

简而言之,我只是想知道无法访问 Internet 的原因是否确实是因为没有 Internet 连接,或者该应用程序当前是否由于某些电池优化而受到限制。

仅在被限制的情况下,我可以警告用户,如果他没问题,该应用程序将被列入白名单,以便它仍然可以正常工作。

以下是我的问题:
  • 以上哪项阻止了应用程序的后台服务访问互联网?所有这些都会导致它吗?它是特定于设备的吗? “互动”会影响它吗?
  • 什么是“强制空闲”,如果已经有办法进入“轻度”和“深度”打瞌睡状态?还有一种方法可以将打瞌睡模式重置为正常吗?我尝试了多个命令,但只有重新启动设备才能真正将其重置为正常...
  • 我创建的 BroadcastReceiver 是否允许正确检查它?是否会在所有情况下触发因所有特殊情况而拒绝访问 Internet?我不能在 list 中注册它是真的吗?
  • 是否可以检查无法访问 Internet 的原因,确实是因为没有 Internet 连接,还是由于某些电池优化而导致应用程序当前受到限制?
  • Android O 上是否更改了特殊情况下后台服务的 Internet 连接限制?也许我应该检查更多的情况?
  • 假设我将服务更改为在前台运行(带有通知),这是否涵盖所有情况,并且始终可以访问 Internet,无论设备处于什么特殊状态?


  • 编辑:这似乎根本不是服务的错,而且它也发生在省电模式下,没有打盹模式。

    该服务的触发器是一个 BroadcastReceiver,它监听电话事件,即使我在它的 onReceive 上检查互联网连接函数,我看到它返回false。从它启动的服务也是如此,即使它是一个前台服务。查看 NetworkInfo 结果,它是“BLOCKED”,它的状态确实是“DISCONNECTED”。

    现在的问题是,为什么会发生这种情况。

    这是一个新的示例 POC 来检查一下。要重现,您需要打开省电模式(使用 ./adb shell settings put global low_power 1 命令,或作为用户),然后启动它,接受权限,关闭 Activity ,然后从另一部手机调用此手机。您会注意到,在 Activity 中,它显示有 Internet 连接,而在 BroadcastReceiver 上,它表示没有。

    请注意,连接到 USB 电缆时,节电模式可能会自动关闭,因此您可能需要在设备未连接时尝试。使用 adb 命令阻止它,而不是启用它的用户方法。

    示例项目也可以在 here 中找到,即使它最初是关于打盹模式的。只需使用省电模式,即可查看问题是否发生。

    电话广播接收器
    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());
    }
    }

    list
    <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>

    MainActivity.java
    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

    测试 2 的条件:
  • 与测试 1 相同,但有以下变化
  • BroadcastReceiver 启动服务(示例如下)
    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;
    }
    }

  • 这个测试给了我与上面相同的结果。

    测试 3 的条件:
  • 与测试 2 相同,但更改低于
  • 应用已从电池优化中列入白名单

  • 后台应用程序 -
    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.


    因此,除非您当前正在使用该应用程序,或者您将该应用程序列入白名单 运行前台服务,如果您的应用程序处于省电模式或打盹模式,您可以期望没有互联网连接可供您的应用程序使用。

    编辑 #1
    与使用某些计时器重新检查互联网连接相比,这可能是一种不同的解决方法:
    MyService.java
    @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 状态。

    编辑 #2
    所以这就是我推荐的。这是基于您之前在评论中给出的答案( Android check internet connection ):
    @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/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com