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)华为需要手动在电池管理里进行操作,才能进行自启动和后台运行。

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

<think>好的,我现在需要帮助用户寻找关于Android相机XML配置或适配的相关信和示例解决方案。首先,我需要回顾用户提供的引用内容,看看是否有直接相关的信。 用户提供的三个引用中: - 引用[1]提到了Android项目配置文件AndroidManifest.xml的结构,包括res文件夹下的子目录,如layout和values,这可能涉及到权限声明或资源管理,但并没有直接提到相机配置。 - 引用[2]讨论了Android 11拍照适配,涉及权限管理和图形处理流程,这可能与相机的权限配置有关,比如在Manifest中声明摄像头权限。 - 引用[3]关于分辨率适配,可能涉及不同幕尺寸的布局配置,但用户的问题是关于相机的XML配置,所以可能需要更具体的布局文件或资源文件配置。 接下来,用户的问题是关于“camera xml 配置 适配 示例 解决方案”。我需要考虑Android中相机的配置通常涉及哪些XML文件。首先,AndroidManifest.xml中需要声明摄像头权限和必要的特性,例如: ```xml <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> ``` 这部分可能在引用[1]提到的AndroidManifest.xml配置中有涉及,但用户可能还需要在布局文件中配置预览界面,比如使用TextureView或SurfaceView。例如,在res/layout/activity_camera.xml中定义预览的视图。 另外,适配不同设备可能需要不同的布局或资源文件,比如在res/layout-land(横向布局)和res/layout-port(纵向布局)中分别定义界面,这可能与引用[3]中的分辨率适配有关,但需要具体到相机预览的布局调整。 此外,Android 11的适配可能需要在Manifest中添加权限请求的方式,比如使用<queries>元素来声明与其他应用的交互,特别是如果应用需要启动系统的相机应用。引用[2]中提到权限管理和图形处理流程,可能涉及到这些配置。 总结可能的解决方案步骤: 1. 在AndroidManifest.xml中声明相机权限和特性。 2. 创建相机预览的布局文件,使用合适的视图组件。 3. 根据设备方向或分辨率创建不同的布局文件进行适配。 4. 处理Android 11的存储权限变更,可能需要使用MediaStore API。 5. 适配不同的摄像头特性,比如前置和后置摄像头,可能需要不同的配置。 然后,我需要确保提供的示例代码和XML配置正确,并符合用户提到的使用$...$和$$...$$格式的要求。例如,在解释布局文件时,使用代码块展示XML结构。同时,生成相关问题,帮助用户进一步了解相关内容。 最后,检查是否有遗漏点,比如不同Android版本的适配差异,或者使用Camera2 API时的配置需求,确保回答准确且覆盖用户的需求。</think>### 相机XML配置与适配方案 在Android中,相机功能的配置和适配通常涉及`AndroidManifest.xml`权限声明、布局文件定义、资源适配以及新版本API的兼容性处理。以下是具体实现步骤和示例: --- #### 1. **权限与特性声明(AndroidManifest.xml)** 在`AndroidManifest.xml`中添加相机权限和硬件特性声明[^2]: ```xml <uses-permission android:name="android.permission.CAMERA" /> <uses-feature android:name="android.hardware.camera" /> <!-- 适配Android 11的文件访问权限 --> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="29" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" android:maxSdkVersion="29" /> <!-- 适配Android 11的媒体位置权限(若需要地理位置) --> <uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" /> ``` --- #### 2. **相机预览布局(XML配置)** 在`res/layout/activity_camera.xml`中定义相机预览界面,使用`TextureView`或`SurfaceView`: ```xml <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <TextureView android:id="@+id/texture_view" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" /> <Button android:id="@+id/btn_capture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="拍照" /> </LinearLayout> ``` --- #### 3. **多分辨率适配** 针对不同幕方向或分辨率,创建对应的布局文件夹: - `res/layout-land/activity_camera.xml`(横向布局) - `res/layout-port/activity_camera.xml`(纵向布局)[^3] 示例横向布局优化: ```xml <TextureView ... android:scaleType="centerCrop" <!-- 保持预览画面比例 --> android:layout_width="match_parent" android:layout_height="match_parent" /> ``` --- #### 4. **Android 11适配关键点** - **文件访问权限**:使用`MediaStore` API保存照片,避免直接访问文件路径: ```java ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DISPLAY_NAME, "photo.jpg"); values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg"); Uri uri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); ``` - **相机权限动态请求**:在代码中检查`CAMERA`权限并动态申请。 --- #### 5. **相机配置扩展(Camera2 API适配)** 若使用Camera2 API,需在`res/values/styles.xml`中声明兼容特性: ```xml <style name="AppTheme" parent="Theme.AppCompat.Light"> <item name="android:windowKeepScreenOn">true</item> <!-- 防止预览时 --> </style> ``` ---
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值