精华内容
下载资源
问答
  • antd的Toast 点击多次会导致Toast关闭
    千次阅读
    2019-07-16 14:12:42

    这个就是antd的bug

    解决办法就是加一个定时器,几秒后强制关闭

    Toast.fail('您输入的账号有误!', 2)
    setTimeout(() => Toast.hide(), 2000) 

     

    更多相关内容
  • 解决通知关闭Toast失效问题

    千次阅读 2018-12-28 12:21:52
    下图是系统INotificationManager的实现源码 当pkg = “android”时不会校验通知关闭逻辑 所以只需要hook INotificationManager 将参数pkg始终传“android” 就行了  实现代码:  /** * Created by linjinfa...

    下图是系统INotificationManager的实现源码 当pkg = “android”时不会校验通知关闭逻辑
    所以只需要hook INotificationManager 将参数pkg始终传“android” 就行了 


    实现代码: 

    /**
     * Created by linjinfa on 2018/2/5.
     * email 331710168@qq.com
     */
    public class ToastUtil {
    
        private static Object iNotificationManagerObj;
    
        /**
         * @param context
         * @param message
         */
        public static void show(Context context, String message) {
            show(context, message, Toast.LENGTH_SHORT, Gravity.BOTTOM);
        }
    
        /**
         *
         * @param context
         * @param message
         */
        public static void showCenter(Context context, String message) {
            show(context, message, Toast.LENGTH_SHORT, Gravity.CENTER);
        }
    
        /**
         * @param context
         * @param message
         */
        public static void show(Context context, String message, int duration, int gravity) {
            if (TextUtils.isEmpty(message)) {
                return;
            }
            //后setText 兼容小米默认会显示app名称的问题
            Toast toast = Toast.makeText(context, null, duration);
            toast.setText(message);
            toast.setGravity(gravity, 0, 0);
            if(isNotificationEnabled(context)){
                toast.show();
            }else{
                showSystemToast(toast);
            }
        }
    
        /**
         * 业务相关的toast
         * @param context
         * @param message
         */
        public static void showCenterForBusiness(final Context context, final String message) {
            if (context != null) {
                if (context instanceof Activity) {
                    ((Activity) context).runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            show(context, message, Toast.LENGTH_SHORT, Gravity.CENTER);
                        }
                    });
                } else {
                    show(context, message, Toast.LENGTH_SHORT, Gravity.CENTER);
                }
            }
        }
    
        /**
         * 显示系统Toast
         */
        private static void showSystemToast(Toast toast){
            try{
                Method getServiceMethod = Toast.class.getDeclaredMethod("getService");
                getServiceMethod.setAccessible(true);
                //hook INotificationManager
                if(iNotificationManagerObj == null){
                    iNotificationManagerObj = getServiceMethod.invoke(null);
    
                    Class iNotificationManagerCls = Class.forName("android.app.INotificationManager");
                    Object iNotificationManagerProxy = Proxy.newProxyInstance(toast.getClass().getClassLoader(), new Class[]{iNotificationManagerCls}, new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //强制使用系统Toast
                            if("enqueueToast".equals(method.getName())
                                    || "enqueueToastEx".equals(method.getName())){  //华为p20 pro上为enqueueToastEx
                                args[0] = "android";
                            }
                            return method.invoke(iNotificationManagerObj, args);
                        }
                    });
                    Field sServiceFiled = Toast.class.getDeclaredField("sService");
                    sServiceFiled.setAccessible(true);
                    sServiceFiled.set(null, iNotificationManagerProxy);
                }
                toast.show();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
        /**
         * 消息通知是否开启
         * @return
         */
        private static boolean isNotificationEnabled(Context context) {
            NotificationManagerCompat notificationManagerCompat = NotificationManagerCompat.from(context);
            boolean areNotificationsEnabled = notificationManagerCompat.areNotificationsEnabled();
            return areNotificationsEnabled;
        }
    
    }
    
    展开全文
  • 相信很多朋友发现了华为等部分手机把通知权限关闭之后会导致Toast无法正常弹出的情况。 原因:谷歌为了让应用的 Toast 能够显示在其他应用上面,所以使用了通知栏相关的 API,但是这个 API 随着用户屏蔽通知栏而变得...

    相信很多朋友发现了华为等部分手机把通知权限关闭之后会导致Toast无法正常弹出的情况。

    原因:谷歌为了让应用的 Toast 能够显示在其他应用上面,所以使用了通知栏相关的 API,但是这个 API 随着用户屏蔽通知栏而变得不可用,系统错误地认为你没有通知栏权限,从而间接导致 Toast 有 show 请求时被系统所拦截。

    解决方法:

     /**
         * 检查通知栏权限有没有开启
         */
        public static boolean isNotificationEnabled(Context context) {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                NotificationManager manager = ((NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE));
                if (manager != null) {
                    return manager.areNotificationsEnabled();
                } else {
                    return false;
                }
            } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                AppOpsManager appOps = (AppOpsManager) context.getSystemService(Context.APP_OPS_SERVICE);
                ApplicationInfo appInfo = context.getApplicationInfo();
                String pkg = context.getApplicationContext().getPackageName();
                int uid = appInfo.uid;
    
                try {
                    Class<?> appOpsClass = Class.forName(AppOpsManager.class.getName());
                    Method checkOpNoThrowMethod = appOpsClass.getMethod("checkOpNoThrow", Integer.TYPE, Integer.TYPE, String.class);
                    Field opPostNotificationValue = appOpsClass.getDeclaredField("OP_POST_NOTIFICATION");
                    int value = (Integer) opPostNotificationValue.get(Integer.class);
                    return (Integer) checkOpNoThrowMethod.invoke(appOps, value, uid, pkg) == 0;
                } catch (NoSuchMethodException | NoSuchFieldException | InvocationTargetException | IllegalAccessException | RuntimeException | ClassNotFoundException ignored) {
                    return true;
                }
            } else {
                return true;
            }
        }
    

    进行判断:

        Toast toast = Toast.makeText(context, info, Toast.LENGTH_SHORT);
    
       //如果没有通知权限 就强制Toast弹框
        if (!isNotificationEnabled(context)) {
            showSystemToast(toast);
            return;
        }
    
        //如果有通知栏权限就正常走
        ......
    

    强制显示方法:

     /**
         * 显示系统Toast
         */
        private static void showSystemToast(Toast toast){
            try{
                Method getServiceMethod = Toast.class.getDeclaredMethod("getService");
                getServiceMethod.setAccessible(true);
    
                final Object iNotificationManager = getServiceMethod.invoke(null);
                Class iNotificationManagerCls = Class.forName("android.app.INotificationManager");
                Object iNotificationManagerProxy = Proxy.newProxyInstance(toast.getClass().getClassLoader(), new Class[]{iNotificationManagerCls}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        // 强制使用系统Toast
                        // 华为p20 pro上为enqueueToastEx
                        if("enqueueToast".equals(method.getName())
                                || "enqueueToastEx".equals(method.getName())){
                            args[0] = "android";
                        }
                        return method.invoke(iNotificationManager, args);
                    }
                });
                Field sServiceFiled = Toast.class.getDeclaredField("sService");
                sServiceFiled.setAccessible(true);
                sServiceFiled.set(null, iNotificationManagerProxy);
                toast.show();
    
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    
    展开全文
  • Toast工作原理依赖于通知关闭应用通知权限后,Toast无法显示。在发布SmartShow1.0.0版的时候,我注意到了这个问题,立即用自己的手机(魅族pro 6 plus)对淘宝、优酷等知名app进行测试,发现关闭通知权限后,它们...

    开源库地址:https://github.com/the-pig-of-jungle/smart-show

    Toast工作原理依赖于通知,关闭应用通知权限后,Toast无法显示。在发布SmartShow1.0.0版的时候,我注意到了这个问题,立即用自己的手机(魅族pro 6 plus)对淘宝、优酷等知名app进行测试,发现关闭通知权限后,它们的“再按一次退出程序”的Toast无法显示。因为Toast的工作机制如此,我并没有把它当做一个问题看待。但是在前两篇文章发布时,关闭通知权限依然能够显示Toast的呼声之高,让我不得不着手解决这个问题。

    有不少Toast开源库解决了该问题,采用独立悬浮窗,也就是另起炉灶,废弃了原生Toast,自行实现一套弹窗提示。我们知道,不同手机品牌设备的Toast外观及弹出动画不尽相同,直接强行统一成一种风格,并不一定符合开发者的意愿,我们应该把是否定制Toast的权利交给开发者。另外,独立悬浮窗本身也需要申请权限,用户会关闭通知权限,难道就没有可能关闭悬浮窗权限么?

    完美的解决方案是什么?当通知权限关闭时,我们需要制造一个VirtualToast来代替,对开发者来说,在使用上和原生Toast毫无差别。对app用户来说,在体验上(弹窗的外观、动画)与原生一致。

    乍一看,这个问题好像很棘手,换一个角度思考问题,则柳暗花明,哈哈。

    先进行技术选型,基于上面提到的原因,我们采用Dialog而不是悬浮窗。

    先分析一下,关闭通知权限后,Toast的工作流程卡在了哪里。Toast的工作流程是一个基于Binder的IPC(进程通信)过程,应用程序作为客户端仅仅发起Toast请求和被动接受回调。系统服务负责管理请求队列、Token等,并通过Toast的内部类Tn来实现与应用程序的交互,即通过回调Tn的show/hide方法来显示/隐藏Toast窗口。关闭了通知权限,导致无法与系统服务“通信”,最终无法添加Toast窗口。

    不过当调用Toast的show方法时,此时添加窗口所需的View及各种窗口参数已全部准备就绪。

    public class Toast{
    
     public void show() {
            ...
            //创建tn,笔者注释
            TN tn = mTN;
            //窗口View,笔者注释
            tn.mNextView = mNextView;
            ...
        }
    
    }
    private static class TN extends ITransientNotification.Stub {
           ...
           TN(String packageName, @Nullable Looper looper) {
                ...
                final WindowManager.LayoutParams params = mParams;
                //窗口宽度,笔者注释
                params.height = WindowManager.LayoutParams.WRAP_CONTENT;
                //窗口高度,笔者注释
                params.width = WindowManager.LayoutParams.WRAP_CONTENT;
                params.format = PixelFormat.TRANSLUCENT;
                //窗口动画,笔者注释
                params.windowAnimations = com.android.internal.R.style.Animation_Toast;
                params.type = WindowManager.LayoutParams.TYPE_TOAST;
                params.setTitle("Toast");
                //不可获取焦点,不接收触碰事件,笔者注释
                params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
                ...
    
            }
          ...
    }

    如此,当通知权限关闭,我们只需在调用Toast的show方法前用Dialog“截胡”,将其View作为Dialog的ContentView。

        public void showToast() {
            applySetting();
            //如果具有通知权限,正常显示Toast
            if (Utils.isNotificationPermitted()) {
                mToast.show();
            } else {
                //无通知权限,用Dialog代替
                VirtualToastManager.get().show(getToastType(), mToast, mWindowParams);
            }
        }

    将Toast窗口的参数设置给Dialog

    public void show(int toastType, Toast toast, WindowManager.LayoutParams windowParams) {
            //获取栈顶activity
            Activity activity = ActivityStack.getTop();
            //若activity生命周期不符合条件,则什么都不做
            if (!Utils.isUpdateActivityUIPermitted(activity)) {
                EasyLogger.d("activity is can not show virtual toast dialog ,so do nothing but return.");
                return;
            }
            ...
            //取出Dialog窗口布局参数,原样复制Toast窗口的布局参数
            WindowManager.LayoutParams lp = virtualToastDialog.getWindow().getAttributes();
            //窗口宽度
            lp.width = windowParams.width;
            //窗口高度
            lp.height = windowParams.height;
            //窗口动画
            lp.windowAnimations = windowParams.windowAnimations;
            //窗口gravity
            lp.gravity = toast.getGravity();
            //窗口x,y坐标
            lp.x = toast.getXOffset();
            lp.y = toast.getYOffset();
    
            ViewGroup content = virtualToastDialog.findViewById(android.R.id.content);
            if (toast.getView().getParent() != content) {
                if (toast.getView().getParent() != null) {
                    ViewGroup parent = (ViewGroup) toast.getView().getParent();
                    parent.removeView(toast.getView());
                }
                content.removeAllViews();
                virtualToastDialog.setContentView(toast.getView());
            }
            try {
                virtualToastDialog.show();
            } catch (WindowManager.BadTokenException e) {
                EasyLogger.e("bad token has happened when show virtual toast!");
                mHostActivity = null;
            }
            ...
    }

    去除Dialog显示时周围变暗的特性

    virtualToastDialog.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);

    Dialog不响应back键

          virtualToastDialog.getWindow().setFlags(
    WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);

    如此这般VirtualToastDialog的外观及动画与目标设备的原生Toast完全一致了。就剩下间隔一段时间自动消失的控制了。

    定义一个Handler,延时特定时间后,执行隐藏VirtualToastDialog的代码。

            Runnable dismissRunnable = toastType == BaseToastManager.PLAIN_TOAST ? mDismissPlainToastRunnable
                    : mDismissTypeToastRunnable;
            mDismissHandler.removeCallbacks(dismissRunnable);
            mDismissHandler.postDelayed(dismissRunnable, toast.getDuration() == Toast.LENGTH_SHORT ?
                    DURATION_SHORT : DURATION_LONG);

    这里,始终复用同一个runnable任务即可,并且在发起一个新的延时隐藏时,我们要清除所有已存在的延时隐藏任务。假如,在上一个隐藏任务还差200毫秒执行的时候,主动调用了隐藏方法使Toast消失,那么再显示一个‘’新的‘’Toast时,200毫秒后就被自动隐藏了。所以mDismissHandler.removeCallbacks(dismissRunnable)起到归位的作用。

    展开全文
  • toast 在应用关闭通知显示时,某些手机上会不在显示toast; 原因简单的说就是toast使用了通知管理器INotificationManager类,而此类因为禁止了通知栏权限而不显示toast; 有兴趣的可以追下源码; 重点说下3种解决方法把: ...
  • Toast使用了INotificationManager类,和通知栏有关,如果关闭通知权限,则Toast不能弹出,如三星、华为等手机在关闭通知栏的情况下就不能使用吐司了。当然国内厂商很多,总会有厂商来自己处理这个东西,比如小米,...
  • Toast 详细
  • 系统原生的Toast是用了INotificationManager类来显示的, Android 5.0以上系统用户只要关闭通知权限,在大部分手机上Toast也将不能显示(有部分国产手机5.0以上的系统禁了通知权限仍能显示Toast)。 /** * ...
  • 显示页面除了Activity,使用最多的可能就是Dialog、PopupWindow、Toast了。这三者有相似之处也有不一样的地方,本篇文章旨在厘清三者关系,阐明各自的优缺点,并探讨哪种场合使用它们。 本篇文章涉及到WindowManager...
  • 首先当然要一个网络去请求我们的服务器,获得仓库中的apk版本信息和下载路径,在和自己当前的版本号进行比较,当自己的版本号小于仓库中的版本号,就提示用户下载,在根据更新等级进行下载强度操作(是否强制下载...
  • Android实现app内部下载更新并通过通知栏展示进度并控制下载状态版本号对比更新弹窗开启apk下载 版本号对比 首先要实现版本更新肯定需要检查当前版本是否是最新版本,这个就需要从服务器获取到最新版本的版本号了,...
  • 通知 消息推送,提醒 基本步骤 练习 1 2响应 消除通知 3声音 4震动 5LED灯 pendingIntent flag取值(一般选择第四个) Intent 与 PendingIntent 的区别 task ...toast ...强制下线
  • 强制下线功能应该算是比较常见的了,很多的应用程序都具备这个...可是这样就存在着一个问题,因为我们被通知需要强制下线时可能正处于任何一个界面,难道需要在每个界面上都编写一个弹出对话框的逻辑?如果你真的...
  • Android 6.0 悬浮窗默认关闭解决方案

    千次阅读 2017-06-02 18:29:12
    在谷歌往Android中加入悬浮窗口功能时就表示希望开发者只用其来做用户通知,修改的悬浮窗功能潜在一定的安全隐患,不过手机厂商可不这么认为,于是本来被用于通知的悬浮窗被改成了其他的功能。我们都知道Androi
  • (5)额外的 PendingIntent(待写) PendingIntent指的是待确定的意图,等待的意图。 Notification(待写) 系统通知
  • "请去设置中打开应用悬浮窗的权限", Toast.LENGTH_SHORT).show() } } else { //Android6.0以上 showDialogTip(context) } } private fun showDialogTip(context: Context) { val dialog = AlertDialog.Builder...
  • 新建BroadcastBestPractice项目,使用广播实现强制下线功能 思路:关闭所有活动,回到登录界面 1、创建ActivityCollector类,用于管理所有的活动 package com.example.broadcastbestpractice; import android.app...
  • 通知与 RemoteViews

    2015-12-25 16:28:14
    有几种类型的场景可能会要求你把应用程序中发生的事件通知给用户,这些事件有的需要用户响应,有的则不需要。  在Android中提供了三种不同的技术来通知用户事件的发生: Toast、Dialog、Notification 第一节 ...
  •  通知栏适用于交互事件的通知,是位于顶层可以展开的通知列表。 二、功能作用  1.显示接收到短消息,及时消息等信息(如QQ、微信、新浪、短信) 2.显示客户端的推送消息(如有新版本发布,广告。推荐新闻等) 3....
  • 类似于微信、QQ等大型app,毫无疑问都有强制下线(即在别处登录时当前登录马上接收到下线通知),一般来说,实现过程是在A手机登录时,记录设备唯一标识,当在B登录时,后台检验上传唯一标识是不是跟后台记录的相同,...
  • 最近研究UWP推送通知,结果因为不懂状态犯了不少错误。状态图如下: 一个完整的应用程序生命周期经历的状态如下1、应用程序第一次被开启,状态由 NotRunning 被激活(Activated)为 Running;2、当用户点击 Start...
  • 可是这样就存在着一个问题,因为当我们被通知需要强制下线时可能正处于任何一个界面,难道需要在每个界面上都编写一个弹出对话框的逻辑?如果你真的这么想,那思维就偏远了,我们完全可以借助所学的广播知识,来非常...
  • 关闭掉所有活动,回到登录界面 一、实现强制下线功能 1.ActivityCollector类: 用于管理所有活动 package com.example.broadcastbestpractice; //用于管理所有的活动 import android.app.Activity; import java...
  • 自从友盟关闭了版本更新功能后,安卓的版本更新只能自己来撸了,结合之前友盟的版本更新,其实实现起来也简单,这里简单说说版本更新实现的思路:第一步,通过接口获取更新信息(版本号、更新内容、apk下载地址、是否...
  • Broadcast实现强制下线功能

    千次阅读 2015-05-11 11:08:00
    现在存在这样一个问题,我们被通知强制下线的时候可能处于任何一个页面,难道我们需要在每个界面上都编写一个弹出对话框的逻辑?我们可以借助广播,来轻松实现这一功能。 首先我们考虑强制下线功能需要先关闭掉所有...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,750
精华内容 700
关键字:

强制关闭toast通知