硬核干货——《中小企业 AI 实战指南》免费下载! 了解详情
写点什么

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

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

评论

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

代码质量保证的利器:Git 预提交钩子

俞凡

最佳实践

AI Agent:一场智能革命的开始

TechubNews

AI Agent

MIAOYUN信创云原生项目亮相“科创天府·智汇蓉城”西部“中试”生态对接活动

MIAOYUN

云计算 云原生 创新 信创 信创产业

谷歌地图 | 利用 Google 地图平台重新定义叫车体验

Cloud Ace 云一

实时洞察、智能运营——新技术重塑企业绩效管理应用(下)

用友智能财务

绩效 企业 会计

SpringBoot的两种启动方式原理

不在线第一只蜗牛

Java Spring Boot 后端

浅尝semantic kernel

为自己带盐

人工智能 semantic kernel

奇点云发布DataSimba敏捷版 | StartDT Day全回顾

奇点云

大数据 AI

天润融通智能运营工具:让AI客服机器人回答更精准

天润融通

唯品会API接口详解:商品详情与关键词搜索功能

代码忍者

唯品会商品API接口

天润融通AI Agent,让智能客服对话更自然、理解更准确

天润融通

某政务行业基于 SeaTunnel 探索数据集成平台的架构实践

白鲸开源

解析与构建:基于语法树的代码规则定义

测试人

软件测试

百度HelixFold 3重磅升级 打造生命科学领域“超强引擎”

新消费日报

百度HelixFold 3重磅升级 打造生命科学领域“超强引擎”

极客天地

学习Java的路线、资料整理

追随哆咪

如何在自己电脑上一键部署 AI 大模型

JustYan

人工智能 大模型 生成式AI

Oracle 可观测最佳实践

观测云

oracle

智谱发布端到端多模态模型 GLM-Realtime,2 分钟记忆能力;讯飞星火同传语音大模型发布,5 秒时延

声网

低代码平台:技术复杂性的系统简化

JeeLowCode低代码平台

低代码 低代码开发 低代码前端 低代码缺点

《CPython Internals》阅读笔记:p177-p220

codists

CPython Internals

解锁微店商品数据:深度探索item_get API接口及其应用

代码忍者

微店商品详情API接口

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