Soul app是我司的竞品,对它的语音音乐播放同步联动的逻辑很感兴趣,于是就开启了一波逆向分析。

下面看代码,以及技术分析,直接步入正轨,哈哈。

我们根据https://github.com/xingstarx/ActivityTracker 这个工具,找到某一个页面,比如cn.soulapp.android/.ui.post.detail.PostDetailActivity 这个页面,然后我们用反编译工具AndroidToolPlus反编译soul 的Android apk, 然后搜索下PostDetailActivity这个类。然后找到这个类之后,我们在根据代码经验猜测,这个语音音乐封装的控件可能在哪,肯定是在PostDetailActivity里面或者是他内容的某个成员变量里面,一不小心,我们就找到了PostDetailHeaderProvider。在这个类里面找到了MusicStoryPlayView, AudioPostView这两个view类,他们就是封装好的音频view,音乐view。(就不截图了。有人感兴趣可以按照我说的实践一番就能得到结论了)

关键代码找到了。那就看看他们内部实现吧。

public class MusicStoryPlayView
extends FrameLayout
implements SoulMusicPlayer.MusicPlayListener

类结构上,实现了核心播放器的listener逻辑,那就说明,他的刷新逻辑,都是通过播放器自身的播放状态回调到view自身上,然后view自身实现了对应的刷新机制就可以更改view的状态了

我们选取几个回调的逻辑看看。不做仔细分析。

 public void onPause(cn.soulapp.android.lib.common.c.i parami)
{
d();
} public void onPlay(cn.soulapp.android.lib.common.c.i parami)
{
LoveBellingManager.e().d();
} public void onPrepare(cn.soulapp.android.lib.common.c.i parami)
{
if (this.e == null) {
return;
}
if (parami.b().equals(this.e.songMId)) {
e();
}
}

那么我们还得思考一个问题,这个listener是什么时候被添加进来的呢。关键点在于view自身的两个方法

  protected void onAttachedToWindow()
{
super.onAttachedToWindow();
SoulMusicPlayer.k().a(this);
} protected void onDetachedFromWindow()
{
super.onDetachedFromWindow();
SoulMusicPlayer.k().b(this);
}

所以很明显,在view被添加到window上(也就是在页面上显示出来)的时候,添加入listener里面,从页面消失,就移除出去。

接着我们在看看核心播放器的逻辑里面,是怎么调度的?

根据代码相关联的逻辑,我们很容易找到核心播放器类SoulMusicPlayer

 public void a(cn.soulapp.android.lib.common.c.i parami)
{
y0.d().a();
LoveBellingManager.e().d();
MusicPlayer.i().f();
if (TextUtils.isEmpty(parami.f())) {
return;
}
Object localObject1 = this.d;
if (localObject1 != null) {
if (!((cn.soulapp.android.lib.common.c.i)localObject1).equals(parami))
{
i();
}
else
{
if (!f())
{
this.a.setLooping(parami.g());
h();
}
return;
}
}
if (this.a == null)
{
this.a = new IjkMediaPlayer();
this.a.setOnErrorListener(this);
this.a.setOnCompletionListener(this);
this.a.setOnPreparedListener(this);
}
this.a.setLooping(parami.g());
try
{
if (l0.e(parami.f()))
{
SoulApp localSoulApp;
Object localObject2;
if (parami.a() != null)
{
localObject1 = this.a;
localSoulApp = SoulApp.e();
localObject2 = new java/io/File;
((File)localObject2).<init>(parami.f());
((IjkMediaPlayer)localObject1).setDataSource(localSoulApp, Uri.fromFile((File)localObject2), parami.a());
}
else
{
localObject2 = this.a;
localSoulApp = SoulApp.e();
localObject1 = new java/io/File;
((File)localObject1).<init>(parami.f());
((IjkMediaPlayer)localObject2).setDataSource(localSoulApp, Uri.fromFile((File)localObject1));
}
}
else
{
localObject1 = parami.a();
if (localObject1 != null) {
this.a.setDataSource(SoulApp.e(), Uri.parse(parami.f().replace("https", "http")), parami.a());
} else {
this.a.setDataSource(SoulApp.e(), Uri.parse(parami.f().replace("https", "http")));
}
}
this.a.prepareAsync();
this.d = parami;
this.b = true;
}
catch (IOException parami)
{
parami.printStackTrace();
}
}
 public void g()
{
if (f())
{
Object localObject = this.a;
if (localObject != null)
{
this.b = false;
((IjkMediaPlayer)localObject).pause();
localObject = this.e.iterator();
while (((Iterator)localObject).hasNext()) {
((MusicPlayListener)((Iterator)localObject).next()).onPause(this.d);
}
this.c.removeCallbacksAndMessages(null);
}
}
}

仔细观察分析这两个方法体,大致可以猜测出,他们是start逻辑,以及暂停播放的逻辑。可以分析出,核心播放器执行完播放,暂停,停止等逻辑后,都会调用List里面的listener,遍历listener,然后触发对应的回调逻辑。

恩,大体的思路有了,就是这么搞,哈哈。

那么我用于我自己项目中,是这么用的么,还是有一些细微差异的,整体方案是参考的soul。细微不同之处在于我是将MusicStoryPlayView放在xml里面,不是像soul那样,直接new的。所以MusicStoryPlayView会被添加很多次,比如在列表中有很多个的话,后面需要判断播放的媒体资源,跟MusicStoryPlayView存放的媒体资源的主键是否一致。

此外出了view类,我对于一些特殊的逻辑,比如Activity或者是悬浮view等等,都实现了PlayListener。通过他们可以实现一些棘手的问题。

好了,本篇到此结束,如果大家有疑问,欢迎留言交流。

Soul Android app 悬浮view以及帖子中view的联动刷新逆向分析的更多相关文章

  1. 通过Hack方式实现SDC中Stage配置联动刷新

    目录 问题描述 如何从外部获取下拉列表参数 如何实现根据下拉列表选项动态刷新 总结 问题描述 最近项目组准备开发一个IoT平台项目,需要使用到StreamSets DataCollector组件进行数 ...

  2. 写给Android App开发人员看的Android底层知识(2)

    (五)AMS 如果站在四大组件的角度来看,AMS就是Binder中的Server. AMS全称是ActivityManagerService,看字面意思是管理Activity的,但其实四大组件都归它管 ...

  3. MAC安裝《Genymotion Android模擬器》大玩Android APP (神魔之塔)

    链接地址:http://www.minwt.com/mac/10083.html/comment-page-2 MAC» 智慧型裝罝» Android | 2014/02/12 Android是一個開 ...

  4. Android app启动耗时分析

    前言 app启动耗时过长的话,无论你的app里面的内容多么丰富有趣,作为一个用户,首先是没有耐心去等待的,如果我是一个用户,我会这样想:这是什么垃圾公司出的什么烂app,再等2s不进来就卸载,黑人问号 ...

  5. Android悬浮框,在Service中打开悬浮窗;在Service中打开Dialog;

    文章介绍了如何在Service中显示悬浮框,在Service中弹出Dialog,在Service中做耗时的轮询操作: 背景需求: 公司的项目现在的逻辑是这样的:发送一个指令,然后3秒一次轮询去查询这个 ...

  6. Android无需权限显示悬浮窗, 兼谈逆向分析app

    前言 最近UC浏览器中文版出了一个快速搜索的功能, 在使用其他app的时候, 如果复制了一些内容, 屏幕顶部会弹一个窗口, 提示一些操作, 点击后跳转到UC, 显示这个悬浮窗不需要申请android. ...

  7. Android中View的绘制过程 onMeasure方法简述 附有自定义View例子

    Android中View的绘制过程 onMeasure方法简述 附有自定义View例子 Android中View的绘制过程 当Activity获得焦点时,它将被要求绘制自己的布局,Android fr ...

  8. Android中 View not attached to window manager错误的解决办法

    前几日出现这样一个Bug是一个RuntimeException,详细信息是这样子的:java.lang.IllegalArgumentException: View not attached to w ...

  9. 源码解析Android中View的measure量算过程

    Android中的Veiw从内存中到呈现在UI界面上需要依次经历三个阶段:量算 -> 布局 -> 绘图,关于View的量算.布局.绘图的总体机制可参见博文< Android中View ...

随机推荐

  1. ML Lecture 0-2: Why we need to learn machine learning?

    在Github上也po了这个系列学习笔记(MachineLearningCourseNote),觉得写的不错的小伙伴欢迎来给项目点个赞哦~~ ML Lecture 0-2: Why we need t ...

  2. effective-java学习笔记---注解优于命名模式39

    命名模式的缺点有以下三点:(在第 4 版之前,JUnit 测试框架要求其用户通过以 test[Beck04] 开始名称来指定测试方法) 1.拼写错误导致失败,但不会提示. 2.无法确保它们仅用于适当的 ...

  3. 一篇文章让你了解动态数组的数据结构的实现过程(Java 实现)

    目录 数组基础简单回顾 二次封装数组类设计 基本设计 向数组中添加元素 在数组中查询元素和修改元素 数组中的包含.搜索和删除元素 使用泛型使该类更加通用(能够存放 "任意" 数据类 ...

  4. Ubuntu 18 安装MySQL 5.7

    1.首先把系统换到阿里云的镜像源,需要等待一会 2.系统更新完毕后执行MySQL安装命令:sudo apt install mysql-server 3.查看MySQL服务状态:sudo servic ...

  5. elasticesearch搜索返回高亮关键字

    pre_tags 前缀标签 post_tags 后缀标签 tags_schema 设置为styled可以使用内置高亮样式 require_field_match 多字段高亮需要设置为false 使用h ...

  6. Java 判断 循环

    一.优先级 1.1 先判断5>3,true 6>4 true;然后true==true ,最后是true; 1.2 6>5,true;而true和4无法比较.所以该判断出错: 1.3 ...

  7. python函数的传参模式

    python里的变量更像是一个名字.标签.而Python中一切又皆为对象. 当函数传参时,函数参数作为一个标签,指向某个对象,因此更贴切的说是"call by object". 但 ...

  8. LeetCode 题解 | 面试题57 - II. 和为s的连续正数序列

    题目描述 面试题57 - II. 和为s的连续正数序列 难度简单37收藏分享切换为英文关注反馈 输入一个正整数 target ,输出所有和为 target 的连续正整数序列(至少含有两个数). 序列内 ...

  9. 【php】面向对象(一)

    1. 学习面向对象的目标: a) 语法的学习: b) 编程思想的学习: i. 过程化: ii. 面向对象:2. 比较(有对象和没对象的区别) a) 没对象: i. 我饿了 自己做饭 ii. 我渴了 自 ...

  10. 关于wget下载jdk问题解决

    问题: 直接从jdk官网下载会出现: 正在解析主机 login.oracle.com (login.oracle.com)... 156.151.58.18正在连接 login.oracle.com ...