解决Android关闭通知消息权限无法弹出Toast的问题

在Android开发中,当用户关闭通知消息权限时,可能会导致Toast无法正常显示。本文分析了Toast的显示原理,并提出通过自建消息队列来绕过权限限制,详细介绍了实现方法,包括关键代码和注意事项,如避免使用ScrollView和处理键盘遮挡SnackBar的情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近在做项目时,遇到一个问题,有部手机就是Toast弹不出来。仔细想想可能权限的问题。后面网上搜索给出如下答案:

跟踪Toast的源代码, make方法省略,做了一些初始化的工作, show方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public void show() {
  if (mNextView == null) {
    throw new RuntimeException("setView must have been called");
  }

  INotificationManager service = getService();
  String pkg = mContext.getPackageName();
  TN tn = mTN;
  tn.mNextView = mNextView;

  try {
    service.enqueueToast(pkg, tn, mDuration);
  } catch (RemoteException e) {
    // Empty
  }
}

static private INotificationManager getService() {
    if (sService != null) {
      return sService;
    }
    sService = INotificationManager.Stub.asInterface(ServiceManager.getService("notification"));
     return sService;
}

熟悉binder通讯的同学应该都知道,其实调用了NotificationManagerService.service.enqueueToast方法进入toast队列,进行相应的逻辑处理后回调给Toast中的TNTN其实就是一个aidlstub实现,相当于Client端,用来接收Service端发来的消息。看下TN中的show方法

1
2
3
4
5
6
7
8
9
public void handleShow() { 
			... 
            mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);  
            mWM.removeView(mView);  
            mWM.addView(mView, mParams);  
            trySendAccessibilityEvent();  
            ...
            }  
        }

通过WinwodManager添加一个view显示提示消息。
总结来说就是toast的显示过程通过IPC通讯由NotificationManagerService维护一个toast队列,然后通知给Toast中的客户端TN调用WindowManager添加view。
那么,如果关闭通知栏消息权限,会影响NotificationManagerService队列的逻辑处理过程,导致不能通知TN显示出视图。

通过上面的分析,我们可以绕过NotificationManagerService,我们自己维护一个toast队列,处理相关的逻辑,进行显示,定时取消。关键代码

1
2
3
4
5
6
7
8
9
10
private static void activeQueue() {
    BooheeToast toast = mQueue.peek();
    if (toast == null) {
        mAtomicInteger.decrementAndGet();
    } else {
        mHanlder.post(toast.mShow);
        mHanlder.postDelayed(toast.mHide, toast.mDuration);
        mHanlder.postDelayed(mActivite, toast.mDuration);
    }
}

mQueue维护了Toast的队列,队列采用FIFO调度,每次调用show()方法触发activeQueue()方法,从队列中取出toast进行显示,然后定时取消。

我本想用SnackBar来替代,可SnackBar有下面几个问题要注意:

1. make()方法的第一个参数的view,不能是有一个ScrollView.

因为SnackBar的实现逻辑是往这个View去addView.而ScrollView我们知道,是只能有一个Child的.否则会Exception.


2. 如果大家在想把Toast替换成SnackBar.需要注意的是,Toast和SnackBar的区别是,前者是悬浮在所有布局之上的包括键盘,而SnackBar是在View上直接addView的.


所以SnackBar.show()的时候,要注意先把Keyboard.hide()了.不然,键盘就会遮住SnackBar.

想想还是自己维护了Toast的队列,那么首先要判断用户是否关闭了消息通知权限,如果没有关闭我还是想用系统提供的队列,网上了很多没有发现可用代码,只能自己看源码了。

com.android.server.notification.NotificationManagerService


private boolean noteNotificationOp(String pkgint uid) {
     Slog.v("notifications are disabled by AppOps for " + pkg);
return  false;
}
return  true;
}

看到这基本上知道 通过
.noteOpNoThrow(.uidpkg) != .
来判断了
AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
mAppOps.noteOpNoThrow()
AppOpsManager.OP_POST_NOTIFICATION ??? is hide,不可用。这咱办。只能反射,来调用了。

private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";
private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";

public static boolean isNotificationEnabled(Context context){

    AppOpsManager mAppOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
    ApplicationInfo appInfo = context.getApplicationInfo();

    String pkg = context.getApplicationContext().getPackageName();

    int uid = appInfo.uid;

    Class appOpsClass = null; /* Context.APP_OPS_MANAGER */

    try {

        appOpsClass = Class.forName(AppOpsManager.class.getName());

        Method checkOpNoThrowMethod = appOpsClass.getMethod(CHECK_OP_NO_THROW, Integer.TYPE, Integer.TYPE, String.class);

        Field opPostNotificationValue = appOpsClass.getDeclaredField(OP_POST_NOTIFICATION);
        int value = (int)opPostNotificationValue.get(Integer.class);
        return ((int)checkOpNoThrowMethod.invoke(mAppOps,value, uid, pkg) == AppOpsManager.MODE_ALLOWED);

    } catch (Exception e) {
        e.printStackTrace();
    }
    return true;
}


听说到了android24 可以 Use NotificationManagerCompat.areNotificationsEnabled()
来判断了。

github 下载。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a3676212

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值