NVIDIA 初创加速计划,免费加速您的创业启动 了解详情
写点什么

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

  • 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:081391

评论

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

项目管理系统选择有哪些需要注意的点?

PingCode

项目管理

包装类型

7月月更

Codeforces Round #787 (Div. 3)

KEY.L

7月月更

Python已有列表和字典,为什么还需要元组?

迷彩

Python Python基础知识 元组 7月月更

jQuery 的事件绑定

Jason199

jquery js 7月月更

渲染与云渲染:一部电影的制作25%的时间是在“等”

Finovy Cloud

GPU服务器

自动化生成Javascript调用后台代码v0.5.3版本

百家饭隐私计算平台创业者

JavaScript API

Flutter 模拟火箭发射动画

岛上码农

flutter ios 移动端开发 安卓开发 7月月更

小程序媒体组件-1

小恺

7月月更

【刷题记录】11. 盛最多水的容器

WangNing

7月月更

Istio组件Mixer介绍

阿泽🧸

istio 7月月更

长安链中的加密算法

长安链

国内外知名的待办事项app有哪些

PingCode

待办事项 todolist

九联科技开发板正式合入OpenHarmony主干

科技汇

SQL也能做AI ?没错!MLOps Meetup V3 回顾|OpenMLBD+SQLFlow+Byzer

星策开源社区

人工智能 机器学习 sql 特征平台

17张图带你深度剖析 ArrayDeque(JDK双端队列)源码

程序员小毕

Java 源码 程序员 jdk 队列

异步 API 设计之扇入扇出模式

宇宙之一粟

API 7月月更

行业首个「视频直播技术最佳实践图」发布!

阿里云视频云

阿里云 音视频 直播

Docker(二)Docker-Compose、网络、数据卷

神农写代码

Allure测试报告怎么设置

和牛

测试

手动上传表单数据+图片文件功能

猪痞恶霸

前端 7月月更

电商平台数据可视化监控系统-Echarts-vue项目综合练习

武师叔

7月月更

谈Java Record类

ES_her0

7月月更

JSON 和JavaScript 介绍与区别

devpoint

JavaScript json 7月月更

金融行业开放平台

穿过生命散发芬芳

7月月更 开放平台

鸿湖万联致远开发板正式合入OpenHarmony主干

科技汇

【Docker 那些事儿】容器数据卷的妙手

Albert Edison

Docker Kubernetes 容器 云原生 7月月更

新书上市 | 图解、幽默、有趣、简单的 Java 书

图灵教育

Java 程序员 计算机

计算机组成原理之计算机最基本的工作原理

未见花闻

7月月更

在线SQL转YAML工具

入门小站

工具

AWS Config

冯亮

云计算 DevOps 架构师 AWS 产品解决方案

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