0. 前言

本文阅读需要10分钟. 你可能的收获:

  • 学会简单的反编译
  • 一些反编译技巧:实战提取两个知名应用的控件,借鉴学习
  • 学会一种提高工作效率的偷懒方法
  • 希望能给读者正在做的项目有点启发,少走点弯路

每一个android开发程序员很有必要掌握一些逆向工程的知识技巧,其中好处不胜枚举,我细数一二:

  • 参考竞品开发框架,技术选型.比如这样的分析:超千万注册用户APP技术实现简析-android《洋葱数学》
  • 核心功能实现上少走弯路,向竞品学习,取长补短
  • 满足自己的好奇心,比如知乎曾有个创意广告,可以源码级别上大概了解到他们如何实现的仿知乎创意广告

1. 反编译效果展示

1.1 《36氪》下拉刷新(APK下载地址)

各位老铁可以下载体验一下这个app的***下拉刷新***,绝对让你很有收获

1.1.1原生效果:
1.1.2《36氪》下拉刷新控件评价
  • 非常流畅,能极大缓解用户等待数据时的焦灼感. 这点非常牛,市面上的应用多如牛毛,其中很多应用下拉刷新控件的质量却很堪忧.鄙人以为《36氪》的下拉刷新体验在应用市场上绝对名列前茅.他们的UE交互设计师一定花了很多时间精力设计它.
  • 下拉时有缩放效果,等待刷新时填充图标的动效,整个过程行云流水
  • 有一种美感在其中.

那么问题来了,假如你的项目中要实现这样一个控件,你需要多长时间? 1天? 2天? 我的答案是1个小时 反编译提取控件,理解其中绘制逻辑.找UI改改样式,over.

1.1.3反编译提取控件效果:

嘻嘻,下面我会给个关于反编译技巧的教程,市面上大多数的应用脱了裤子给我们看,刺激吧?

1.2 《作业盒子-教师版》批注控件(APK下载地址)
1.2.1《作业盒子》批注作业效果
1.1.3《作业盒子》批注作业控件评价
  • 编辑模式:同时支持双手缩放,以及单手批注
  • 缩放的同时,批注笔头粗细跟随缩放
  • 不可编辑模式:支持双手缩放,单手拖拽
  • 并且支持批注回退 让我老老实实做,估计最少也得3天,就算实现了,估计还有不少bug需要修复.没错,我就是这么不自信.有点自知之明还是好的. 但是,反编译提取控件,估计也就1,2个小时. 我们项目也需要实现类似功能.一直有个用户需要的需求迟迟没有落地---[图片上的文字看不清楚,需要支持放大功能,但是同时还得支持批注功能] 因为没有想到好的交互实现方案,并且有其他功能要做,做这个需求性价比太低了,开发周期这么紧,最后不了了之.而现在,简直是分分钟的事.
1.2.2《作业盒子》反编译提取控件

2. 《36氪》下拉刷新:

2.1正常情况下,不考虑反编译,自己实现的思路分析:
  1. 实现跟随用户上拉下拉 蓝色方框的缩放动画(使用值动画)
  2. 刷新过程中的动画,如果使用自定义view画出来,估计成本会比较高,如果使用lottie会省事很多
  3. 不过做出来说不定还有bug,使用反编译,这样的担忧会减轻很多,毕竟36kr这个下拉控件经过庞大用户无数次的考验.
2.2通过反编译提取
2.2.1目标:

还原实现下拉刷新控件

2.2.2 会遇到的困难:

①控件位置难找; ②资源文件分散; ③代码经过混淆,代码逻辑需要跟着作者实现思路走一遍

2.2.3 提取过程:
  1. 提取jar包以及资源文件 使用apktool 或使用反编译集成工具 这一步没啥难度,建议读者想跟着实践一下的话,首选反编译集成工具. 用命令行工具会很麻烦,光是插件的安装就这么多,更别提安装过程的环境问题.

1.ShakaApkTool 2.Apktool 3.Dex2Jar 4.Zipalign 5.SignApk 6.JDGUI

我是直接github上找到一个mac工具软件:android crack tool

傻瓜化操作后获得如下文件: 2.随便新建一个AS项目,将jar包添加到libs 然后add as library 3.在android studio中使用analyse apk 4.定位下拉刷新控件的代码位置 这一步需要耐心,因为不太好找,需要猜一下. a,找到mainActivity b.在mainActivity中找到使用该控件的fragment

c.关键:在fragment中找到控件(需要一点点小耐心) 一开始没找着,想了一下,这个fragment肯定是使用了下拉刷新的,位置没找错. 那问题出现在哪里呢?

推测36Kr的程序员对这个下拉刷新动作进行了封装.可能在BaseFragment中,然而也没有! 同样的,在Rxfragment中也没找着. 回到HomeFragment2中,看能不能找到点蛛丝马迹 果然找到了一点线索:有关于refresh关键字 原来HomeFragment2是另一个fragment的容器,找错位置了,回到MainActivity中找其他fragment 在SubscribeHomeFragment中马上就找到了

in.srain.cube.views.ptr.PtrFrameLayout //第一反应是网上的开源库,github上一搜索,果然~
复制代码

36Kr使用比较出名的下拉刷新库github地址:android-Ultra-Pull-To-Refresh

d.根据下拉刷新头部KrHeader以及资源R文件定位资源文件

根据header_kr这个id去搜索定位布局文件

e. 根据KrHeader的变量LottieAnimationView b找到lottie动画 根据lottie文档官网,动画文件一般放在asserts文件或res/raw中

至此,这个控件已经被完完全全的抽取出来了

2.2.4 源码展示

PS:对混淆代码进行理解后,进行变量以及类名重命名,添加上一些必要注释

头部刷新代码控件()

/*** 郑重声明:本源码均来自互联网,仅供个人欣赏、学习之用,* 版权归36氪产品发行公司所有,任何组织和个人不得公开传播或用于任何商业盈利用途,* 否则一切后果由该组织或个人承担。* 本人不承担任何法律及连带责任!请自觉于下载后24小时内删除**/
public class KrHeader extends FrameLayout implements PtrUIHandler {private ImageView mScaleImageView;private LottieAnimationView mLoadingLottieView;private TextView mRefreshInfoTextView;private boolean isShowRefreshInfo;public KrHeader(Context context) {this(context, (AttributeSet)null);}public KrHeader(Context context, AttributeSet attributeSet) {super(context, attributeSet);this.init(context);}public KrHeader(Context context, AttributeSet attributeSet, int defStyleRes) {super(context, attributeSet, defStyleRes);this.init(context);}@TargetApi(21)public KrHeader(Context context, AttributeSet attributeSet, int defStyleAttr, int defStyleRes) {super(context, attributeSet, defStyleAttr, defStyleRes);this.init(context);}private void init() {this.mScaleImageView.setVisibility(GONE);this.mLoadingLottieView.setVisibility(VISIBLE);this.mRefreshInfoTextView.setVisibility(GONE);this.mLoadingLottieView.playAnimation();}private void init(Context var1) {View var2 = inflate(var1, R.layout.header_kr, this);this.mScaleImageView = (ImageView)var2.findViewById(R.id.pre);this.mLoadingLottieView = (LottieAnimationView)var2.findViewById(R.id.loading);this.mRefreshInfoTextView = (TextView)var2.findViewById(R.id.tv_refresh_info);}private void onUIRefreshPrepare() {this.mScaleImageView.setVisibility(VISIBLE);this.mLoadingLottieView.setVisibility(GONE);this.mRefreshInfoTextView.setVisibility(GONE);this.mLoadingLottieView.setProgress(0f);this.mLoadingLottieView.cancelAnimation();}private void onUIRefreshComplete() {if (this.isShowRefreshInfo) {this.mScaleImageView.setVisibility(GONE);this.mLoadingLottieView.setVisibility(GONE);this.mRefreshInfoTextView.setVisibility(VISIBLE);}}public TextView getCompleteView() {return this.mRefreshInfoTextView;}/*** 根据手势上下拉缩放imageview* @param frame* @param isUnderTouch* @param status* @param ptrIndicator*/@Overridepublic void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) {int offset = frame.getOffsetToRefresh();int currentPosY = ptrIndicator.getCurrentPosY();if (currentPosY >= offset) {this.mScaleImageView.setScaleX(1.0F);this.mScaleImageView.setScaleY(1.0F);} else if (status == 2) {//根据偏移量计算缩放比例float scale = (float)(offset - currentPosY) / (float)offset;this.mScaleImageView.setScaleX(1.0F - scale);this.mScaleImageView.setScaleY(1.0F - scale);}}@Overridepublic void onUIRefreshBegin(PtrFrameLayout var1) {this.init();}@Overridepublic void onUIRefreshComplete(PtrFrameLayout var1) {this.onUIRefreshComplete();}@Overridepublic void onUIRefreshPrepare(PtrFrameLayout var1) {this.onUIRefreshPrepare();}@Overridepublic void onUIReset(PtrFrameLayout var1) {this.onUIRefreshPrepare();}public void setShowRefreshInfo(boolean showRefreshInfo) {this.isShowRefreshInfo = showRefreshInfo;}
}
复制代码

下拉刷新调用方式


/*** 郑重声明:本源码均来自互联网,仅供个人欣赏、学习之用,* 版权归36氪产品发行公司所有,任何组织和个人不得公开传播或用于任何商业盈利用途,* 否则一切后果由该组织或个人承担。* 本人不承担任何法律及连带责任!请自觉于下载后24小时内删除**/
public class MainActivity extends AppCompatActivity implements PtrHandler {private PtrFrameLayout mPtr;private KrHeader krHeader;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);mPtr = (PtrFrameLayout) findViewById(R.id.ptr);this.krHeader = new KrHeader(this);krHeader.setShowRefreshInfo(true);krHeader.getCompleteView().setText("暂无更新内容");this.mPtr.setHeaderView(this.krHeader);this.mPtr.addPtrUIHandler(this.krHeader);this.mPtr.setPtrHandler(this);this.mPtr.setDurationToCloseHeader(1000);this.mPtr.setDurationToClose(200);this.mPtr.setLoadingMinTime(1000);this.mPtr.setEnabledNextPtrAtOnce(true);ImageView iv_test = findViewById(R.id.iv_test);iv_test.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View view) {startActivity(new Intent(MainActivity.this, TestActivity.class));}});}@Overridepublic boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {return true;}@Overridepublic void onRefreshBegin(PtrFrameLayout frame) {new Handler().postDelayed(new Runnable() {@Overridepublic void run() {mPtr.refreshComplete();}}, 300);}
}
复制代码
2.2.5 源码github地址

希望能得到你的一个star,这是对我写这篇博文给大家带来一些另类思路和收获的鼓励 36KrRefreshDemo

3. 《作业盒子》批改控件提取

基本上与36KrRefresh类似,关键点都在于更多的耐心以及能提取成功的信心 这个控件提取遇到了更多的困难: ①控件涉及多个自定义view,再加上代码混淆的影响下,才确定这个控件涉及3个view,提取难度加大不少 ②控件在app中出现的层级更深,定位的时间耗费更多 ③控件中的绘制逻辑更加复杂,需要更多精力去理解混淆后的代码 PS:过程中发现了这个控件的一个bug:放大倍数过大,OOM,应用闪退.

源码地址: DrawContainerDemo 欢迎star,小小鼓励一下我~

4.授人以鱼不如授人以渔

总结一下反编译参考竞品的技巧:

  • 先看主干,再细看旁枝末节. 什么意思呢?就是先看其大体项目架构,用了什么开源库,浏览一下AndroidManifest,都有什么Activity,通过英文单词去猜测其功能(一个优秀的项目,对类的命名必然是直观易懂的)
  • 实践动手.这个也很重要,因为单纯看来的是不准确的,是不可能深入理解其核心逻辑的,必须尽可能的将其抽取出来做成demo,以此验证自己的猜测.当然,动手是一种冒风险的事情,因为有可能自己的猜测落空.
  • 信心. 心里面的想法:我就有预感自己会成功,蜜汁自信.
  • 耐心. 这事情没那么容易,但真没那么难. ####5. 反编译的实用价值(教你偷懒) 不懂偷懒的程序员,不是好程序员.

5.1功利性价值

核心业务的复杂功能实现,可能需要一个月,但是如果你通过反编译源码级别地了解竞品,借鉴竞品,说得粗俗点,竞品脱了裤子让你观摩,那你完成这个功能可能只需要1个星期,节省了三个星期,开发效率提高300%

5.2 自我价值

只是为了工作敲代码的程序员,就有点shameless了.不应该只看到其功利性价值,更应该去挖掘自我价值,学习一些优秀程序员敲的商用级别代码; 这两个反编译过程,我是带着强烈好奇心去完成的:

  • 卧槽,怎么他们的下拉控件做的这么好?
  • holy shit,怎么他们的批注实现思路这么赞呢? 非常珍惜这种好奇心.好奇心是个好东西,会驱动你去做更多以前没做过的事,让你有更多激动人心的发现,会更加想变得优秀,当然,也会让自己更加开心.

4.28日更新:反编译注意事项

经老上司彼时芒种提醒,新增了两个编译小技巧

  • JD-GUI 可以查看更完备的源代码 比如下图中所示中无法显示的源代码.
  • 寻找目标功能或者目标控件源码时,可通过adb命令查找应用当前界面所对应的activity名称加快寻找效率
adb shell dumpsys activity | grep "Running activities" -A 7
复制代码

最后,感谢大家看完我的文章,觉得不错的话,点个❤️

APK 控件逆向工程(36氪,作业盒子)相关推荐

  1. 【无私分享】修订版干货!!!一个炫酷的自定义日历控件,摆脱日历时间选择烦恼,纯福利~...

    可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多,但也还算将就吧. 没有看的小伙伴如果有兴趣的话可以去看看:http://www.cnblogs.com/liushilin/p/57 ...

  2. Android自定义日历控件,自带农历节假日,已经开源,即取即用~

    关注本人的更多博客:http://www.cnblogs.com/liushilin/ 该自定义日历控件已经开源:github地址 可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多, ...

  3. firefox扩展开发(四) : 更多的窗口控件

    firefox扩展开发(四) : 更多的窗口控件 2008-06-11 17:00 标签盒子 标签盒子是啥?大家都见过,就是分页标签: 对应的代码: <?xml version="1. ...

  4. java自定义日历控件_【无私分享】修订版干货!!!一个炫酷的自定义日历控件,摆脱日历时间选择烦恼,纯福利~...

    可能不少的小伙伴都有看楼主昨天发的自定义日历控件,虽然实现功能不多,但也还算将就吧. 但是看了的小伙伴就很心急了,说楼主上传到gitHub的东西有问题,楼主下载来看了看,基本都没问题吧,没弄好的小伙伴 ...

  5. 在 GUI 控件中使用布局和容器: CBOX 类

    目录 1. 介绍 2. 目标 3. 类 CBox 3.1. 布局样式 3.2. 计算控件之间的间隔 3.3. 对齐 3.4. 部件渲染 3.5. 部件大小调整 3.6. 递归渲染 4. 在对话框窗口里 ...

  6. 背水一战 Windows 10 (36) - 控件(弹出类): ToolTip, Popup, PopupMenu

    原文:背水一战 Windows 10 (36) - 控件(弹出类): ToolTip, Popup, PopupMenu [源码下载] 背水一战 Windows 10 (36) - 控件(弹出类): ...

  7. python tkinter控件_python GUI作业:使用tkinter的重要控件

    题目1:使用tkinter的重要控件 绘制如下菜单: 图片.png 参考代码:#!/usr/bin/env python3# -*- coding: utf-8 -*-# 技术支持:https://w ...

  8. android 环境配置和安装, Android系统包说明,基本控件,常用代码,ADB 命令行,APK文件确解,小技艺,...

    一.             环境配置和安装(Android2.2) 参考文章:这里 1.1     JDK 1.2     SDK 下载地址:http://dl.google.com/android ...

  9. 图片盒子控件 winform 114868210

    图片盒子控件 winform 114868210 引入控件 放图片 控制图片的大小 效果 图片变的与控件一模一样大

最新文章

  1. Build Boost C++ libraries for x32/x64 VC++ compilers on Windows
  2. Linux中以单容器部署Nginx+ASP.NET Core
  3. SpringBoot集成Flowable_Jsite已发任务菜单报500
  4. 怎么把4399小游戏的代码_25行代码带你爬取4399小游戏数据,看下童年的游戏是否还在...
  5. hashmap修改对应key的值_死磕 java集合之HashMap源码分析
  6. 团队作业4——第一次项目冲刺(Alpha版本)
  7. cvs数据导入工具 oracle_oracle数据库的导入导出
  8. DP--POJ1191 棋盘分割
  9. Python网页抓取教程
  10. 北京市海淀区卫星地图离线包下载
  11. ncl 添加点shp文件_NCL绘制中国地图
  12. 地铁一公里造价达7亿元,大部分城市无法回本,为何还抢着建?
  13. 计算多项式的小技巧(Horner法则)
  14. 腾讯互娱2021年游戏客户端开发工程师暑期实习生面试经历
  15. 计算机二级的公共基础课水平测试,计算机二级公共基础知识复习试题含答案
  16. oracle实例由,Oracle 数据库的实例由( )组成
  17. 【重磅】百度智能运维工程架构
  18. JavaScript高级第02天笔记
  19. 4.28 DP练习赛
  20. Chemical Peel Treatment For Acne Scars

热门文章

  1. 计算机硬件系统设计—码表数码管显示驱动设计
  2. 画中画效果自由制作,视频、图片都可制作
  3. vue中使用echarts折现图表(多图表与定时递归刷新图表解决方案在附录1、2中)
  4. linux服务器安装gmt,linux GMT简易安装
  5. 解析几何--对称,平移和旋转
  6. LFS 好辛苦的工作,据说要坚持至少12个小时,而如果不用“风无痕9588”大侠的脚本代码而手工输入的话,据说要200个小时!
  7. 谷歌插件 XssSniper 扩展介绍 xss检测
  8. Netfilter源码分析
  9. 从腰椎间盘突出到坐骨神经痛,这个过程怎么度过与规避!
  10. 不同VLAN下实现网络互相通信(配置port trunk pvid vlan进行数据转发)