仿斗鱼BiliBili 全局悬浮窗直播小窗口 实现详解

本文详细介绍了如何实现仿斗鱼BiliBili的全局悬浮窗直播小窗口功能,包括权限申请、悬浮窗初始化、touch事件监听以及全局单例直播窗口的构造复用。在权限申请中,特别提到6.0及以上版本的用户授权和MIUI系统的特殊处理。悬浮窗的初始化涉及windowManager的使用,touch事件处理则需要区分点击和拖动。最后,文章讲解了如何实现全局单例直播窗口,确保直播的无缝衔接。
摘要由CSDN通过智能技术生成

最近业务需求需要我们直播返回或者退出直播间时,开一个小窗口在全局继续直播视频,先看效果图。

 

更新了一下 现在有demo下载地址了

https://www.jianshu.com/p/a762893c670f
demo下载地址

调研了一下当下主流直播平台,斗鱼BiLiBiLi等app,都是用windowManger 做的即通过windowManger add一个全局的view,可以申请权限悬浮在所有应用之上以此来实现全局悬浮窗

ok,分析完实现原理我们就开始撸代码了

实现悬浮窗难点

1:权限申请:一个是6.0及以后要用户手动授权,因为悬浮窗权限属于高危权限,二是因为MIUI,底层修改了权限,所以在小米手机上需要特殊处理,还有就是8.0以后权限的定义类型变了下面有代码会详解这块

2:对于悬浮窗touch 事件的监听,比如点击事件和touch事件,如果同事监听那么setOnclickListener就没有效果了,需要区别点击和touch,还有就是拖动小窗口移动位置,这里是指针对整个窗体即设置touch事件又设置点击事件会有冲突

3:直播组件的初始化,即全局单例的直播窗口,可以是自己封装一个自定义View,这个因各自的直播SDK而定,我这用的sdk在插件里,所以实现起来比较麻烦,但是一般直播sdk(阿里云或者七牛)都可以用同一个直播组件对象,即在直播页面销毁或者返回时把对象传递到小窗口里,实现无缝衔接开启小窗口直播,不需要重新加载,这里用EventBus发个消息或者广播都可以实现

一:权限申请

首先要在清单文件即AndroidManifest文件声明 悬浮窗权限 

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

然后我们悬浮窗触发的时机是在直播页面返回的时候,那也就是说可以在onDestory()或者finsh()时候去做权限申请

注:因为6.0以后是高危权限,所以代码是拿不到权限的,需要跳到权限申请列表让用户授权

if (isLiveShow) {
                if (Build.VERSION.SDK_INT >= 23) {
                    if (!Settings.canDrawOverlays(getContext())) {
                        //没有悬浮窗权限,跳转申请
                        Toast.makeText(getApplicationContext(), "请开启悬浮窗权限", Toast.LENGTH_LONG).show();
                        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION);
                        startActivity(intent);
                    } else {
                        initLiveWindow();
                    }
                } else {
                    //6.0以下 只有MUI会修改权限
                    if (MIUI.rom()) {
                        if (PermissionUtils.hasPermission(getContext())) {
                            initLiveWindow();
                        } else {
                            MIUI.req(getContext());
                        }
                    } else {
                        initLiveWindow();
                    }
                }
            }

而低版本一般是不需要用户授权的除了MIUI,所以我们需要先判断是否是MIUI系统,然后判断MIUI版本,然后不同的版本对应不同的权限申请姿势,如果你不这么做,那么恭喜你在低版本(低于6.0)的小米手机上不是返回跳转权限崩溃,因为底层改了授权列表类或者是根本不会跳授权没有反应,

 //6.0以下 只有MUI会修改权限
                    if (MIUI.rom()) {
                        if (PermissionUtils.hasPermission(getContext())) {
                            initLiveWindow();
                        } else {
                            MIUI.req(getContext());
                        }
                    } else {
                        initLiveWindow();
                    }

先判断是否是MIUI系统

  public static boolean rom() {
        return Build.MANUFACTURER.equals("Xiaomi");
    }

 然后根据不同版本,不同的授权姿势

/**
 * Description:
 * Created by PangHaHa on 18-7-25.
 * Copyright (c) 2018 PangHaHa All rights reserved.
 *
 *  /**
 * <p>
 * 需要清楚:一个MIUI版本对应小米各种机型,基于不同的安卓版本,但是权限设置页跟MIUI版本有关
 * 测试TYPE_TOAST类型:
 * 7.0:
 * 小米      5        MIUI8         -------------------- 失败
 * 小米   Note2       MIUI9         -------------------- 失败
 * 6.0.1
 * 小米   5                         -------------------- 失败
 * 小米   红米note3                  -------------------- 失败
 * 6.0:
 * 小米   5                         -------------------- 成功
 * 小米   红米4A      MIUI8         -------------------- 成功
 * 小米   红米Pro     MIUI7         -------------------- 成功
 * 小米   红米Note4   MIUI8         -------------------- 失败
 * <p>
 * 经过各种横向纵向测试对比,得出一个结论,就是小米对TYPE_TOAST的处理机制毫无规律可言!
 * 跟Android版本无关,跟MIUI版本无关,addView方法也不报错
 * 所以最后对小米6.0以上的适配方法是:不使用 TYPE_TOAST 类型,统一申请权限
 */

public class MIUI {

    private static final String miui = "ro.miui.ui.version.name";
    private static final String miui5 = "V5";
    private static final String miui6 = "V6";
    private static final String miui7 = "V7";
    private static final String miui8 = "V8";
    private static final String miui9 = "V9";



    public static boolean rom() {
        return Build.MANUFACTURER.equals("Xiaomi");
    }

    private static String getProp() {
        return Rom.getProp(miui);
    }


    public static void req(final Context context) {
        switch (getProp()) {
            case miui5:
                reqForMiui5(context);
                break;
            case miui6:
            case miui7:
                reqForMiui67(context);
                break;
            case miui8:
            case miui9:
                reqForMiui89(context);
                break;
        }

    }


    private static void reqForMiui5(Context context) {
        String packageName = context.getPackageName();
        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
        Uri uri = Uri.fromParts("package", packageName, null);
        intent.setData(uri);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (isIntentAvailable(intent, context)) {
            context.startActivity(intent);
        }
    }

    private static void reqForMiui67(Context context) {
        Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
        intent.setClassName("com.miui.securitycenter",
                "com.miui.permcenter.permissions.AppPermissionsEditorActivity");
        intent.putExtra("extra_pkgname", context.getPackageName());
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (isIntentAvailable(intent, context)) {
            context.startActivity(intent);
        }
    }

    private static void reqForMiui89(Context context) {
        Intent intent = new Intent("miui.intent.action.APP_PERM_EDITOR");
        intent.setClassName("com.miui.securitycenter", "com.miui.permcenter.permissions.PermissionsEditorActivity");
        intent.putExtra("extra_pkgname", context.getPackageName());
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (isInten
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值