Android APP息屏状态下收到通知解决方案

1.问题

       最近负责的Android APP,用户反馈无法收到通知,尤其是息屏状态下无法收到通知。

       这些APP,笔者以前都测试过,可以收到推送的。但测试以后,发现在新的Android上,确实收不到通知和推送。

       根本原因在于:在最新的Android系统里,基于省电考虑,如果相关应用没有在系统白名单里,则会在锁屏之后,会被优化掉而不再运行;这样,APP将不再收到通知和推送。

       用户还抱怨:微信能收到消息,为啥你们不能?笔者查阅资料后得知:在几乎所有Android系统里,微信、QQ、企业微信都被默认在系统白名单里,可以开机运行,锁屏后后台运行。

2.解决方案整体思路

       但问题还得解决,解决思路为:

      1)允许APP收到通知;

      2)允许APP自启动;

      3)打开APP的悬浮窗;

      4)在电量管理里,允许APP在耗电状态下长期运行。

      值得注意的是:这一系列操作,只能让使用者自己来完成;这样的APP才能才能被认为是合规的。

      对于用户来说,可以做的,其实是提供引导程序,让用户来操作。

3.引导程序

       提供的引导程序如下:

开启通知工具类:

/**
 * Created by Administrator 
 * 开启通知
 */

public class NotificationUtil {
    /**
     * 跳转到app的设置界面--开启通知
     * @param context
     */
    public static void goToNotificationSetting(Context context) {
        Intent intent = new Intent();

        if (Build.VERSION.SDK_INT >= 26) {
            // android 8.0引导
            intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
            intent.putExtra("android.provider.extra.APP_PACKAGE", context.getPackageName());
        } else if (Build.VERSION.SDK_INT >= 21) {
            // android 5.0-7.0
            intent.setAction("android.settings.APP_NOTIFICATION_SETTINGS");
            intent.putExtra("app_package", context.getPackageName());
            intent.putExtra("app_uid", context.getApplicationInfo().uid);
        } else {
            // 其他
            intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
            intent.setData(Uri.fromParts("package", context.getPackageName(), null));
        }

        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);
    }

    /**
     * 判断是否需要打开通知
     * @param context
     */
    public static boolean isNotificationEnabled(Context context) {
        boolean isOpened = false;

        try {
            isOpened = NotificationManagerCompat.from(context).areNotificationsEnabled();
        } catch (Exception e) {
            e.printStackTrace();
            isOpened = false;
        }

        return isOpened;
    }
}

权限检查工具类:

/**
 * Created by Administrator
 * 权限检查
 */
public class PermissionCheckUtil {
    //判断是否开启悬浮窗权限   context可以用你的Activity.或者tiis
    public static boolean checkFloatPermission(Context context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
            return true;
        }

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) {
            try {
                Class cls = Class.forName("android.content.Context");
                Field declaredField = cls.getDeclaredField("APP_OPS_SERVICE");
                declaredField.setAccessible(true);
                Object obj = declaredField.get(cls);

                if (!(obj instanceof String)) {
                    return false;
                }

                String str2 = (String) obj;
                obj = cls.getMethod("getSystemService", String.class).invoke(context, str2);
                cls = Class.forName("android.app.AppOpsManager");
                Field declaredField2 = cls.getDeclaredField("MODE_ALLOWED");
                declaredField2.setAccessible(true);
                Method checkOp = cls.getMethod("checkOp", Integer.TYPE, Integer.TYPE, String.class);
                int result = (Integer) checkOp.invoke(obj, 24, Binder.getCallingUid(), context.getPackageName());
                return result == declaredField2.getInt(cls);
            } catch (Exception e) {
                return false;
            }
        } else {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                AppOpsManager appOpsMgr = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
                if (null == appOpsMgr) {
                    return false;
                }

                int mode = appOpsMgr.checkOpNoThrow(
                        "android:system_alert_window",
                        android.os.Process.myUid(), context
                        .getPackageName()
                );

                return mode == AppOpsManager.MODE_ALLOWED || mode == AppOpsManager.MODE_IGNORED;
            } else {
                return Settings.canDrawOverlays(context);
            }
        }
    }

    //权限打开
    public static void requestSettingCanDrawOverlays(Context context) {
        int sdkInt = Build.VERSION.SDK_INT;

        if (sdkInt >= Build.VERSION_CODES.O) {
            //8.0以上
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
//            startActivityForResult(intent, REQUEST_DIALOG_PERMISSION);
            context.startActivity(intent);
        } else if (sdkInt >= Build.VERSION_CODES.M) {
            //6.0-8.0
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
            intent.setData(Uri.parse("package:" + context.getPackageName()));
//            startActivityForResult(intent, REQUEST_DIALOG_PERMISSION);
            context.startActivity(intent);
        } else {
            //4.4-6.0以下
            //无需处理了
        }
    }
}

自启动工具类

/**
 * Created by Administrator
 * 自启动设置
 */

public class AutoStartUtil {
    /**
     * 获取自启动管理页面的Intent
     * @param context context
     * @return 返回自启动管理页面的Intent
     * */
    public static void getAutostartSettingIntent(Context context) {
        ComponentName componentName = null;
        String brand = Build.MANUFACTURER;
        Intent intent = new Intent();
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        try {
            switch (brand.toLowerCase()) {
                case "samsung"://三星
                    componentName = new ComponentName("com.samsung.android.sm", "com.samsung.android.sm.app.dashboard.SmartManagerDashBoardActivity");
                    break;

                case "huawei"://华为
                    //荣耀V8,EMUI 8.0.0,Android 8.0上,以下两者效果一样
                    componentName = new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.appcontrol.activity.StartupAppControlActivity");
//            componentName = new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");//目前看是通用的
                    break;

                case "xiaomi"://小米
                    componentName = new ComponentName("com.miui.securitycenter", "com.miui.permcenter.autostart.AutoStartManagementActivity");
                    break;

                case "vivo"://VIVO
//            componentName = new ComponentName("com.iqoo.secure", "com.iqoo.secure.safaguard.PurviewTabActivity");
                    componentName = new ComponentName("com.iqoo.secure", "com.iqoo.secure.ui.phoneoptimize.AddWhiteListActivity");
                    break;

                case "oppo"://OPPO
//            componentName = new ComponentName("com.oppo.safe", "com.oppo.safe.permission.startup.StartupAppListActivity");
                    componentName = new ComponentName("com.coloros.oppoguardelf", "com.coloros.powermanager.fuelgaue.PowerUsageModelActivity");
                    break;

                case "yulong":
                case "360"://360
                    componentName = new ComponentName("com.yulong.android.coolsafe", "com.yulong.android.coolsafe.ui.activity.autorun.AutoRunListActivity");
                    break;

                case "meizu"://魅族
                    componentName = new ComponentName("com.meizu.safe", "com.meizu.safe.permission.SmartBGActivity");
                    break;

                case "oneplus"://一加
                    componentName = new ComponentName("com.oneplus.security", "com.oneplus.security.chainlaunch.view.ChainLaunchAppListActivity");
                    break;

                case "letv"://乐视
                    intent.setAction("com.letv.android.permissionautoboot");

                default://其他
                    intent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS");
                    intent.setData(Uri.fromParts("package", context.getPackageName(), null));
                    break;
            }

            intent.setComponent(componentName);
            context.startActivity(intent);
        } catch (Exception e){
            Log.e("HLQ_Struggle", e.getLocalizedMessage());

            AlertDialog.Builder builder = new AlertDialog.Builder(context);

            //设置标题
            builder.setTitle("请用户设置自启动");

            //设置对话框内容
            builder.setMessage("请用户别忘记设置自启动!!!");

            //设置图标
            builder.setIcon(android.R.drawable.ic_dialog_alert);

            //设置是否可以点击屏幕其他地方或者返回键取消显示
            builder.setCancelable(false);

            //确定按钮
            builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    //该按钮的点击事件,这里设置按钮返回
                    dialog.dismiss();
                }
            });

            //很多朋友都会忘了show
            builder.show();

            intent = new Intent(Settings.ACTION_SETTINGS);
            context.startActivity(intent);
        }
    }
}

电池优化工具类:

/**
 * Created by Administrator 
 * 对电池进行管理,使APP避免被电池优化
 */

public class BatteryManagementUtil {
    /**
     * 忽略电池优化
     */
    public static void ignoreBatteryOptimization(Context context) {
        PowerManager powerManager = (PowerManager) context.getSystemService(POWER_SERVICE);
        boolean hasIgnored = false;

        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            hasIgnored = powerManager.isIgnoringBatteryOptimizations(context.getPackageName());
            //  判断当前APP是否有加入电池优化的白名单,如果没有,弹出加入电池优化的白名单的设置对话框。
            if (!hasIgnored) {
                try {//先调用系统显示 电池优化权限
                    Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                    intent.setData(Uri.parse("package:" + context.getPackageName()));
                    context.startActivity(intent);
                } catch (Exception e) {//如果失败了则引导用户到电池优化界面
                    try {
                        Intent intent = new Intent(Intent.ACTION_MAIN);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        intent.addCategory(Intent.CATEGORY_LAUNCHER);

                        ComponentName cn = ComponentName.unflattenFromString("com.android.settings/.Settings$HighPowerApplicationsActivity");

                        intent.setComponent(cn);
                        context.startActivity(intent);
                    }catch (Exception ex){//如果全部失败则说明没有电池优化功能
                        ex.printStackTrace();
                        Intent intent = new Intent(Settings.ACTION_SETTINGS);
                        context.startActivity(intent);
                    }
                }
            }
        }
    }
}

整个程序放在MainActivity的OnCreate()函数里:

NotificationUtil.goToNotificationSetting(MainActivity.this);
//打开悬浮窗
PermissionCheckUtil.requestSettingCanDrawOverlays(MainActivity.this);

//打开自启动窗口
AutoStartUtil.getAutostartSettingIntent(MainActivity.this);

//打开电池优化
BatteryManagementUtil.ignoreBatteryOptimization(MainActivity.this);

4.目前结果

         引导之后,部分机型可以直接设置。当然,部分机型还有如下问题:

         1)小米需要更改通知过滤规则,避免消息被过滤掉;

         2)华为需要手动在电池管理里进行操作,才能进行自启动和后台运行。

        没办法,提供操作说明和解决方案吧。提供解决方案以后,目前还正常。

要在 Android 应用程序中保持应用在息屏状态下继续运行,可以使用以下两种方法: 1. 使用 WakeLock:可以使用 WakeLock 来保持设备处于唤醒状态。WakeLock 可以防止设备在息屏时进入休眠模式。以下是使用 WakeLock 的示例代码: ```java PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE); WakeLock wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyApp:MyWakeLockTag"); wakeLock.acquire(); // 在这里执行需要在息屏状态下继续运行的操作 wakeLock.release(); ``` 请确保在使用完 WakeLock 后调用 `release()` 方法来释放 WakeLock。 2. 使用 Foreground Service:可以将应用程序设置为前台服务,这样即使在息屏状态下,应用程序也可以持续运行。以下是使用前台服务的示例代码: ```java public class MyForegroundService extends Service { private static final int NOTIFICATION_ID = 1; private static final String CHANNEL_ID = "MyForegroundServiceChannel"; @Override public void onCreate() { super.onCreate(); // 创建通知渠道(仅适用于 Android 8.0 及更高版本) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(CHANNEL_ID, "Foreground Service Channel", NotificationManager.IMPORTANCE_DEFAULT); NotificationManager notificationManager = getSystemService(NotificationManager.class); notificationManager.createNotificationChannel(channel); } // 创建通知 Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID) .setContentTitle("My App") .setContentText("App is running in background") .setSmallIcon(R.drawable.ic_notification) .build(); // 开始前台服务 startForeground(NOTIFICATION_ID, notification); } @Override public int onStartCommand(Intent intent, int flags, int startId) { // 在这里执行需要在息屏状态下继续运行的操作 return START_STICKY; } @Override public void onDestroy() { super.onDestroy(); // 停止前台服务 stopForeground(true); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } } ``` 要使用前台服务,需要在 AndroidManifest.xml 文件中声明服务,并请求必要的权限。确保在启动服务时调用 `startService()` 方法。 这些方法可以让您的应用程序在 Android 设备的息屏状态下继续运行。但是,请注意这可能会对设备的电池寿命产生一定影响,因为应用程序会持续占用设备资源。
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值