关闭通知消息权限Toast无法显示的解决方案【android】

问题分析
直接跟踪Toast的源码,发现Toast其实是通过NotificationManagerService维护一个toast队列,然后通知Toast的客户端TN调用WindowManager添加view,所以当关闭消息通知权限时toast是无法显示。

/**
     * Show the view for the specified duration.
     */
    public void show() {
        if (mNextView == null) {
            throw new RuntimeException("setView must have been called");
        }

        INotificationManager service = getService();
        String pkg = mContext.getOpPackageName();
        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的TN,TN其实就是一个aidl的stub实现,相当于client用来接收service发送的消息。看下TN的show方法

 public void handleShow(){
        if (mNextView !=null){
            if (mNextView.getParent() != null){
                windowManager.removeView(mNextView);
            }
            windowManager.addView(mNextView,params);
        }
    }

通过WindowManager添加一个view显示提示消息,如果关闭了消息通知,那么会影响NotificationManagerService队列的逻辑处理过程,导致不能通知TN示出消息。

解决思路
通过对上面的分析,我们可以绕过NotificationManagerService,
仿照系统的Toast,然后用自己的消息队列去维护,使其不受NotificationMnagerService的影响
如下代码内容:参照源码重写了一份,最后再show的时候不进入TN维护队列,而是使用Hander+Queue来维护Toast

package com.hodi.hodi_opencv;

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.PixelFormat;
import android.os.Build;
import android.os.Handler;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by AA on 2016/11/19.
 * 通过windowManager
 */

public class Toast {

    public static final int LENGTH_LONG = 1;
    public static final int LENGTH_SHORT = 0;

    private Context mcontext;
    private WindowManager windowManager;
    private View mNextView;
    private int mDuration;
    private WindowManager.LayoutParams params;
    //维护toast的队列
    private static BlockingQueue<Toast> mQueue = new LinkedBlockingQueue<>();
    //判断是否在读取队列来显示toast
    protected static AtomicInteger mAtomicInteger = new AtomicInteger(0);
    private static Handler mHandler = new Handler();

    public Toast(Context context) {
        mcontext = context.getApplicationContext();
        windowManager = (WindowManager) mcontext.getSystemService(Context.WINDOW_SERVICE);
        params = new WindowManager.LayoutParams();
        params.height = WindowManager.LayoutParams.WRAP_CONTENT;
        params.width = WindowManager.LayoutParams.WRAP_CONTENT;
        params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
        params.format = PixelFormat.TRANSLUCENT;
        params.windowAnimations = android.R.style.Animation_Toast;
        params.y = dip2px(mcontext,64);
        params.type = WindowManager.LayoutParams.TYPE_TOAST;
        params.gravity = Gravity.CENTER | Gravity.CENTER_HORIZONTAL | Gravity.BOTTOM;


    }
    public static Toast makeText(Context context,CharSequence text,int duration){
        Toast result = new Toast(context);
        View view = android.widget.Toast.makeText(context,text, android.widget.Toast.LENGTH_SHORT).getView();
        if (view != null){
            TextView tv = (TextView) view.findViewById(android.R.id.message);
            tv.setText(text);
            Log.e("text",tv.getText().toString());
        }
        result.mNextView = view;
        result.setDuration(duration);
        return result;
    }
    public static Toast makeText(Context context,int resId,int duration) throws Resources.NotFoundException{
        return makeText(context,context.getResources().getText(resId),duration);
    }

    public void show(){
        //将需要显示的加入到队列中
        mQueue.offer(this);
        //激活队列
        if (mAtomicInteger.get() == 0){
            mAtomicInteger.incrementAndGet();
            mHandler.post(mActive);
        }
    }

    /**
     * dip 与px的转换
     * @param context
     * @param dipValue
     * @return
     */
    private int dip2px(Context context,float dipValue){
        final float scale = context.getResources().getDisplayMetrics().density;

        return (int) (dipValue*scale + 0.5f);
    }

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public Toast setGravity(int gravity, int xOffset, int yOffset) {
        final int finalGravity;
        if (Build.VERSION.SDK_INT >= 14){
            final Configuration configuration = mNextView.getContext().getResources().getConfiguration();
            finalGravity = Gravity.getAbsoluteGravity(gravity,configuration.getLayoutDirection());

        }else {
            finalGravity = gravity;
        }
        params.gravity = finalGravity;
        if ((finalGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL){
            params.horizontalWeight = 1.0f;
        }
        if ((finalGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL){
            params.verticalWeight = 1.0f;
        }
        params.y = yOffset;
        params.x = xOffset;
        return this;
    }

    public Toast setDuration(int duration) {
        if (duration<0){
            this.mDuration = 0;
        }
        if (duration == Toast.LENGTH_SHORT){
            this.mDuration = 1000;
        } else if (duration == Toast.LENGTH_LONG){
            this.mDuration = 2000;
        }else {
            this.mDuration = duration;
        }
        return this;
    }

    public Toast setView(View view) {
        this.mNextView = view;
        return this;
    }

    public Toast setMargin(float horizontalMargin,float verticalMargin){
        params.horizontalMargin = horizontalMargin;
        params.verticalMargin = verticalMargin;
        return this;
    }

    private static void activeQueue(){
        final Toast toast = mQueue.peek();
        if (toast == null){
            //非激活
            mAtomicInteger.decrementAndGet();
        }else {
            mHandler.post(toast.mShow);
        }
    }

    private void handleShow(){
        if (mNextView !=null){
            if (mNextView.getParent() != null){
                windowManager.removeView(mNextView);
            }
            windowManager.addView(mNextView,params);
        }
    }

    private  void handleHide(){
        if (mNextView != null){
            if (mNextView.getParent() != null){
                windowManager.removeView(mNextView);
                mQueue.poll();
            }
            mNextView = null;
        }
    }

    private final static Runnable mActive  = new Runnable() {
        @Override
        public void run() {
            activeQueue();
        }
    };
    private final Runnable mShow = new Runnable() {
        @Override
        public void run() {
            handleShow();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    handleHide();
                }
            },mDuration);
            new Handler().postDelayed(mActive,mDuration);
        }
    };
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值