最新发布《数智时代的AI人才粮仓模型解读白皮书(2024版)》,立即领取! 了解详情
写点什么

贝壳找房解决全局悬浮窗问题

  • 2019-09-24
  • 本文字数:2521 字

    阅读完需:约 8 分钟

贝壳找房解决全局悬浮窗问题

从百度打开贝壳找房 app 后要在应用每个界面显示个悬浮按钮, 说到悬浮按钮我们首先会想到 WindowManager。


我们在接到这个需求后也是按照惯用方法,使用 WindowManager 添加悬浮窗。



当前问题:必须打开贝壳找房的“悬浮窗”权限后才能显示

问题原因

Android6.0、Android7.1、Android8.0 版本对 WindowManager 的限制越来越多, 不同安卓版本可以使用 SYSTEM_ALERT、TYPE_PHONE、TYPE_TOAST、TYPE_SYSTEM_OVERLAY 类型,但前置条件是用户授权,而悬浮窗权限默认是关闭,在调试悬浮窗功能时可能出现各种坑。


例如:“android.view.WindowManager$BadTokenException: Unable to add window”、不显示悬浮窗等。

技术对标

从“今日头条”的广告展位打开“京东商城”后,打开每个京东商城的界面后都会显示“返回头条”。 这跟我们的需求是一致的,在系统设置里查看“京东商城”的悬浮窗权限是关闭的, 京东商城是如何做到的?



京东商城



京东商城布局


使用 uiautomakeviewer 抓布局后可以看出京东商城没使用 WindowManager(如果是 WindowManager 实现的悬浮窗,在 uiautomakeviewer 里无法选中), 而是在根节点添加个子 View, 悬浮窗是 setContentView()的兄弟 View。


划重点:从上面图片看出根节点是 FrameLayout, 它就是 Activity 的根节点 DecorView。 我们在 Activity 的 onCreate 函数里 setContentView, 其实就是向 PhoneWindow 类的 mDecor 添加子 View。

解决方案

通过对比京东商城 app, 找到了显示悬浮窗的方法。新的问题又来了, 如果在每个界面都添加这个悬浮按钮:


1、在 BaseActivity 里实现是否可行?


原理上没问题, 但贝壳找房使用了插件化,需要所有插件再编译一遍,代价有点大;


2、Activity 的生命周期是被谁触发的, 在 onCreate 或 onStart 里执行不就行了?


划重点:


(1)Activity 的生命周期函数是被 ActivityThread 类的 Instrumentation 类触发的, 而且 Android 在 Application 类里提供了回调接口。


(2)不管应用是否插件化,UI 界面都运行在同一个进程里,即公用一个进程上下文。


(3)必须先执行 setContentView,然后再添加悬浮窗,这样才能保证悬浮窗在上面(根节点 DecorView 是 FrameLayout)。


(4)在 onCreate 或 onStart 函数里执行添加悬浮窗逻辑有什么区别?区别在于 onStart 函数可以向已打开的 Activity 里添加悬浮窗。


核心代码:


在 onStart 函数里判断是否需要显示悬浮窗、悬浮窗是否已添加等条件后, 再添加悬浮窗。


 1 ((Application)mApplicationContext).registerActivityLifecycleCallbacks( 2        new Application.ActivityLifecycleCallbacks() { 3          @Override public void onActivityCreated(final Activity activity, Bundle savedInstanceState) { 4          } 5 6          @Override public void onActivityStarted(final Activity activity) { 7            if (TextUtils.isEmpty(sBackName) 8                || TextUtils.isEmpty(sBackUrl)) { 9              return;10            }1112            FrameLayout root = (FrameLayout) activity.getWindow().getDecorView();13            View linkView = root.findViewById(R.id.ll_deeplink_beike);14            if (linkView == null) {15              //如果已添加则能找到16              View view = UIUtils.inflate(R.layout.layout_baidu_deeplink_window,17                  null);18              TextView tvBackName = (TextView) view.findViewById(R.id.tv_back_name);19              LinearLayout ltBack = (LinearLayout) view.findViewById(R.id.lt_back);20              tvBackName.setText(UIUtils.getString(R.string.back_baidu, sBackName));21              ltBack.setOnClickListener(new View.OnClickListener() {22                @Override public void onClick(View view) {23                  Intent intent = new Intent();24                  intent.setData(Uri.parse(sBackUrl));25                  intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);26                  try {27                    activity.startActivity(intent);28                  } catch (ActivityNotFoundException ex) {29                    ex.printStackTrace();30                  }31                }32              });3334              FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,35                  ViewGroup.LayoutParams.WRAP_CONTENT);36              layoutParams.topMargin = (int)(activity.getResources().getDisplayMetrics().heightPixels * 0.75);37              layoutParams.leftMargin = 0;38              root.addView(view, layoutParams);39            } else {40              //do nothing41            }42          }4344          @Override public void onActivityResumed(Activity activity) {4546          }4748          @Override public void onActivityPaused(Activity activity) {4950          }5152          @Override public void onActivityStopped(Activity activity) {5354          }5556          @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {5758          }5960          @Override public void onActivityDestroyed(Activity activity) {6162          }63        });
复制代码

小结

如果再遇到悬浮窗的需求时,慎重使用 WindowManager,因为 Android 对 WindowManager 有各种权限限制;而在 DecorView 添加 View 的方式不受 Android 各个版本限制。


推荐使用添加 View 的方式替代 WindowManager。


感悟:从技术角度多对标别的产品,找出产品或技术上的亮点,想想自己实现这个功能该怎么做,然后再看看别人怎么做的,取长补短。


作者介绍:


作者大上(企业代号名),目前负责贝壳找房 App 安卓端研发工作。


本文转载自公众号贝壳产品技术(ID:gh_9afeb423f390)。


原文链接:


https://mp.weixin.qq.com/s/3hXyFCgclsuoznNQ2ulC4g


2019-09-24 16:081387

评论

发布
暂无评论
发现更多内容

借势双碳东风:干“实事”的“虚拟”电厂

脑极体

OpenMLDB 实时引擎性能测试报告

第四范式开发者社区

人工智能 机器学习 数据库 性能分析 特征平台

Crypto巨头们纷纷聚焦“Building platonic”,PlatoFarm会是解决方案吗?

西柚子

JAVA SPI机制

源字节1号

云图说丨每个成功的业务系统离不开API网关(APIG)的保驾护航

华为云开发者联盟

云计算 华为云 API网关 业务系统 APIG

带你读顶会论文丨基于溯源图的APT攻击检测

华为云开发者联盟

安全

error: conflicting types for xxx in c

codists

c

对于编程思想和能力有重大提升的书有哪些?

宇宙之一粟

书单推荐 编程思想 5月月更

模块2作业

Geek_701557

架构实战营模块2课后作业

Geek_53787a

高性能 PHP 应用容器之 Workerman

CRMEB

解析数仓lazyagg查询重写优化规则

华为云开发者联盟

数据库

使用 Provider 搞定 Flutter 的局部刷新

岛上码农

flutter ios 前端 安卓开发 5月月更

什么是区块哈希?哈希趣投娱乐竞猜游戏开发逻辑(成熟源码)

开发微hkkf5566

三、KVM管理工具

穿过生命散发芬芳

kvm 5月月更

客观的聊一聊,裁员这件糟心事

互联网 职场 裁员

GitOps自问自答

俞凡

最佳实践 研发效能 gitops

哈希能作弊吗?哈希竞猜游戏防作弊系统开发逻辑(稳定运营)

开发微hkkf5566

带你认识String类

开发微hkkf5566

贪心算法:Dota2 参议院 🏯

空城机

LeetCode 5月月更

为什么人工智能需要可解释性?

博文视点Broadview

全链路压测(十四):生产全链路压测SOP

老张

性能测试 全链路压测 稳定性保障

值得收藏:当向数据库导入大量数据时,mysql主键唯一键重复插入,如何丝滑操作并不导入重复数据呢

迷彩

MySQL 数据库 大数据运维 5月月更

基于云服务MRS构建DolphinScheduler2调度系统

华为云开发者联盟

大数据 MRS 华为云 DolphinScheduler 调度处理

JAVA什么是反射?

源字节1号

软件开发

今天爬,明天没,天津市XX网 详情页加密逻辑拆解,文中关键字已经加密

梦想橡皮擦

5月月更

【LeetCode】检查句子中的数字是否递增Java题解

Albert

LeetCode 5月月更

leetcode 153. Find Minimum in Rotated Sorted Array 寻找旋转排序数组中的最小值(中)

okokabcd

LeetCode 查找

Sa-Token 单点登录 SSO模式二 URL重定向传播会话示例

Java 登录验证 SaToken

助力传统游戏转型GameFi,Web3Games推动游戏发展新航向

One Block Community

区块链 黑客马拉松 gamefi Web3.0

[Day46]-[数组]-三数之和

方勇(gopher)

数组 双指针 LeetCode

贝壳找房解决全局悬浮窗问题_文化 & 方法_大上_InfoQ精选文章