博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
部分手机Toast不显示的解决办法
阅读量:7218 次
发布时间:2019-06-29

本文共 11431 字,大约阅读时间需要 38 分钟。

转载请标明地址 QuincySx:

部分手机可能Toast不显示,换其他手机是正常的

这是因为Toast显示需要NotificationManagerService(查看Android源码)

部分手机把通知权限关闭了,所以Toast无法正常弹出

解决办法跳过NotificationManagerService自己维护一个队列

public class CustomToast implements IToast {    private static Handler mHandler = new Handler();    /**     * 维护toast的队列     */    private static BlockingQueue
mQueue = new LinkedBlockingDeque<>(); /** * 原子操作:判断当前是否在读取{@linkplain #mQueue 队列}来显示toast */ private static AtomicInteger mAtomicInteger = new AtomicInteger(0); private WindowManager mWindowManager; private long mDurationMillis; private View mView; private WindowManager.LayoutParams mParams; private Context mContext; public void makeTextShow(String text, long duration) { new CustomToast(mContext) .setText(text) .setDuration(duration) .setGravity(Gravity.BOTTOM, 0, 30).show(); } public static IToast makeText(Context context, String text, long duration) { return new CustomToast(context) .setText(text) .setDuration(duration) .setGravity(Gravity.BOTTOM, 0, 30); } /** * 参照Toast源码TN()写 * * @param context */ public CustomToast(Context context) { mContext = context; mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE); mParams = new WindowManager.LayoutParams(); mParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mParams.format = PixelFormat.TRANSLUCENT; mParams.windowAnimations = android.R.style.Animation_Toast; mParams.type = WindowManager.LayoutParams.TYPE_TOAST; mParams.setTitle("Toast"); mParams.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager .LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE; // 默认居中 mParams.gravity = Gravity.CENTER; } /** * Set the location at which the notification should appear on the screen. * * @param gravity * @param xOffset * @param yOffset */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) @Override public IToast setGravity(int gravity, int xOffset, int yOffset) { // We can resolve the Gravity here by using the Locale for getting // the layout direction final int finalGravity; if (Build.VERSION.SDK_INT >= 14) { final Configuration config = mView.getContext().getResources().getConfiguration(); finalGravity = Gravity.getAbsoluteGravity(gravity, config.getLayoutDirection()); } else { finalGravity = gravity; } mParams.gravity = finalGravity; if ((finalGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) { mParams.horizontalWeight = 1.0f; } if ((finalGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) { mParams.verticalWeight = 1.0f; } mParams.y = yOffset; mParams.x = xOffset; return this; } @Override public IToast setDuration(long durationMillis) { if (durationMillis < 0) { mDurationMillis = 0; } if (durationMillis == Toast.LENGTH_SHORT) { mDurationMillis = 2000; } else if (durationMillis == Toast.LENGTH_LONG) { mDurationMillis = 3500; } else { mDurationMillis = durationMillis; } return this; } /** * 不能和{@link #setText(String)}一起使用,要么{@link #setView(View)} 要么{@link #setView(View)} * * @param view 传入view * @return 自身对象 */ @Override public IToast setView(View view) { mView = view; return this; } @Override public IToast setMargin(float horizontalMargin, float verticalMargin) { mParams.horizontalMargin = horizontalMargin; mParams.verticalMargin = verticalMargin; return this; } /** * 不能和{@link #setView(View)}一起使用,要么{@link #setView(View)} 要么{@link #setView(View)} * * @param text 字符串 * @return 自身对象 */ public IToast setText(String text) { // 模拟Toast的布局文件 com.android.internal.R.layout.transient_notification // 虽然可以手动用java写,但是不同厂商系统,这个布局的设置好像是不同的,因此我们自己获取原生Toast的view进行配置 View view = Toast.makeText(mContext, text, Toast.LENGTH_SHORT).getView(); if (view != null) { TextView tv = (TextView) view.findViewById(android.R.id.message); tv.setText(text); setView(view); } return this; } @Override public void show() { // 1. 将本次需要显示的toast加入到队列中 mQueue.offer(this); // 2. 如果队列还没有激活,就激活队列,依次展示队列中的toast if (0 == mAtomicInteger.get()) { mAtomicInteger.incrementAndGet(); mHandler.post(mActivite); } } @Override public void cancel() { // 1. 如果队列已经处于非激活状态或者队列没有toast了,就表示队列没有toast正在展示了,直接return if (0 == mAtomicInteger.get() && mQueue.isEmpty()) return; // 2. 当前显示的toast是否为本次要取消的toast,如果是的话 // 2.1 先移除之前的队列逻辑 // 2.2 立即暂停当前显示的toast // 2.3 重新激活队列 if (this.equals(mQueue.peek())) { mHandler.removeCallbacks(mActivite); mHandler.post(mHide); mHandler.post(mActivite); } } private void handleShow() { if (mView != null) { if (mView.getParent() != null) { mWindowManager.removeView(mView); } mWindowManager.addView(mView, mParams); } } private void handleHide() { if (mView != null) { // note: checking parent() just to make sure the view has // been added... i have seen cases where we get here when // the view isn't yet added, so let's try not to crash. if (mView.getParent() != null) { mWindowManager.removeView(mView); // 同时从队列中移除这个toast mQueue.poll(); } mView = null; } } private static void activeQueue() { CustomToast toast = mQueue.peek(); if (toast == null) { // 如果不能从队列中获取到toast的话,那么就表示已经暂时完所有的toast了 // 这个时候需要标记队列状态为:非激活读取 mAtomicInteger.decrementAndGet(); } else { // 如果还能从队列中获取到toast的话,那么就表示还有toast没有展示 // 1. 展示队首的toast // 2. 设置一定时间后主动采取toast消失措施 // 3. 设置展示完毕之后再次执行本逻辑,以展示下一个toast mHandler.post(toast.mShow); mHandler.postDelayed(toast.mHide, toast.mDurationMillis); mHandler.postDelayed(mActivite, toast.mDurationMillis); } } private final Runnable mShow = new Runnable() { @Override public void run() { handleShow(); } }; private final Runnable mHide = new Runnable() { @Override public void run() { handleHide(); } }; private final static Runnable mActivite = new Runnable() { @Override public void run() { activeQueue(); } };}
public interface IToast {    void makeTextShow(String text, long duration);    IToast setGravity(int gravity, int xOffset, int yOffset);    IToast setDuration(long durationMillis);    /**     * 不能和{@link #setText(String)}一起使用,要么{@link #setView(View)} 要么{@link #setText(String)}     */    IToast setView(View view);    IToast setMargin(float horizontalMargin, float verticalMargin);    /**     * 不能和{@link #setView(View)}一起使用,要么{@link #setView(View)} 要么{@link #setText(String)}     */    IToast setText(String text);    void show();    void cancel();}

上面是两个是自己实现的Toast队列

我们还写了个工厂 来进行 当 App 初始化的时候判断手机的 Toast 是否能显示,如果不能显示则使用自己维护的 Toast

public class SystemToast implements IToast {    private Toast mToast;    private Context mContext;    public void makeTextShow(String text, long duration) {        new SystemToast(mContext)                .setText(text)                .setDuration(duration).show();    }    public static IToast makeText(Context context, String text, long duration) {        return new CustomToast(context)                .setText(text)                .setDuration(duration);    }    public SystemToast(Context context) {        mContext = context;        mToast = Toast.makeText(context, "", Toast.LENGTH_SHORT);    }    @Override    public IToast setGravity(int gravity, int xOffset, int yOffset) {        mToast.setGravity(gravity, xOffset, yOffset);        return this;    }    @Override    public IToast setDuration(long durationMillis) {        mToast.setDuration((int) durationMillis);        return this;    }    /**     * 不能和{@link #setText(String)}一起使用,要么{@link #setView(View)} 要么{@link #setView(View)}     *     * @param view 传入view     * @return 自身对象     */    @Override    public IToast setView(View view) {        mToast.setView(view);        return this;    }    @Override    public IToast setMargin(float horizontalMargin, float verticalMargin) {        mToast.setMargin(horizontalMargin, verticalMargin);        return this;    }    /**     * 不能和{@link #setView(View)}一起使用,要么{@link #setView(View)} 要么{@link #setView(View)}     *     * @param text 传入字符串     * @return 自身对象     */    @Override    public IToast setText(String text) {        mToast.setText(text);        return this;    }    @Override    public void show() {        if (mToast != null) {            mToast.show();        }    }    @Override    public void cancel() {        if (mToast != null) {            mToast.cancel();        }    }}
public class ToastFactory {    private static final String CHECK_OP_NO_THROW = "checkOpNoThrow";    private static final String OP_POST_NOTIFICATION = "OP_POST_NOTIFICATION";    private int mCheckNotification = -1;    private volatile static ToastFactory sToastFactory;    private IToast mIToast;    private ToastFactory(Context context) {        if (isNotificationEnabled(context)) {            mIToast = new SystemToast(context);        } else {            mIToast = new CustomToast(context);        }    }    public static IToast getInstance(Context context) {        if (sToastFactory == null) {            synchronized (ToastFactory.class) {                if (sToastFactory == null) {                    sToastFactory = new ToastFactory(context);                }            }        }        return sToastFactory.mIToast;    }    private 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;    }}

当用上边这个工厂进行 Toast 显示的时候会在自行维护的 Toast 队列与系统 Toast 之间选择

你可能感兴趣的文章
jetty404web界面服务器信息隐藏
查看>>
22个Photoshop网页设计教程网站推荐
查看>>
如何让程序员更容易的开发Web界面?重构SmartAdmin展示TinyUI
查看>>
centos7 python2和python3共存
查看>>
rhel6.2配置在线yum源
查看>>
分级聚类算法
查看>>
Web Services 入门(之二)
查看>>
随机模拟MCMC和Gibbs Sampling
查看>>
网络安全是一种态度
查看>>
POJ1131 Octal Fractions
查看>>
mysql-ulogd2.sql
查看>>
119. Pascal's Triangle II - Easy
查看>>
349. Intersection of Two Arrays - Easy
查看>>
[算法练习]最长公共子串(LCS)
查看>>
p转c++
查看>>
树(tree)
查看>>
codevs——2645 Spore
查看>>
ssh服务之 远程登录和端口转发
查看>>
java环境配置正确,但是tomcat不能启动的解决办法
查看>>
我就是想找个人聊聊天,说说我这近四年来的经历
查看>>