APK 控件逆向工程(36氪,作业盒子),网易资深安卓架构师

前言

刚从阿里面试回来,想和大家分享一些我的面试经验,以及面试题目。

这篇文章将会更加聚焦在面试前需要看哪些资料,一些面试技巧以及一些这次的面试考题。

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZdiJD6x1-1649752722719)(https://user-gold-cdn.xitu.io/2018/4/27/16307a0825533f60?imageView2/0/w/1280/h/960/ignore-error/1)]

傻瓜化操作后获得如下文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FQWLY26Z-1649752722720)(https://user-gold-cdn.xitu.io/2018/4/27/16307a0824404d9f?imageView2/0/w/1280/h/960/ignore-error/1)]

2.随便新建一个AS项目,将jar包添加到libs 然后add as library 3.在android studio中使用analyse apk

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6v1XbGD2-1649752722720)(https://user-gold-cdn.xitu.io/2018/4/27/16307a0847f0f97c?imageView2/0/w/1280/h/960/ignore-error/1)]

4.定位下拉刷新控件的代码位置 这一步需要耐心,因为不太好找,需要猜一下. a,找到mainActivity

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-92WQpGAf-1649752722721)(https://user-gold-cdn.xitu.io/2018/4/27/16307a084b46777c?imageView2/0/w/1280/h/960/ignore-error/1)]

b.在mainActivity中找到使用该控件的fragment

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HrONF1V1-1649752722721)(https://user-gold-cdn.xitu.io/2018/4/27/16307a08528a1c66?imageView2/0/w/1280/h/960/ignore-error/1)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6TUeEfYZ-1649752722722)(https://user-gold-cdn.xitu.io/2018/4/27/16307a086a7f4083?imageView2/0/w/1280/h/960/ignore-error/1)]

推测36Kr的程序员对这个下拉刷新动作进行了封装.可能在BaseFragment中,然而也没有! 同样的,在Rxfragment中也没找着.

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QcuGBhdJ-1649752722722)(https://user-gold-cdn.xitu.io/2018/4/27/16307a0881f93efd?imageView2/0/w/1280/h/960/ignore-error/1)]

回到HomeFragment2中,看能不能找到点蛛丝马迹 果然找到了一点线索:有关于refresh关键字

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c7UscgxJ-1649752722722)(https://user-gold-cdn.xitu.io/2018/4/27/16307a088dd41cca?imageView2/0/w/1280/h/960/ignore-error/1)]

原来HomeFragment2是另一个fragment的容器,找错位置了,回到MainActivity中找其他fragment 在SubscribeHomeFragment中马上就找到了

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u3rZ5fFf-1649752722723)(https://user-gold-cdn.xitu.io/2018/4/27/16307a089819ad10?imageView2/0/w/1280/h/960/ignore-error/1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u4evQihk-1649752722723)(https://user-gold-cdn.xitu.io/2018/4/27/16307a089a71d617?imageView2/0/w/1280/h/960/ignore-error/1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YZHqM2pJ-1649752722724)(https://user-gold-cdn.xitu.io/2018/4/27/16307a089ec15580?imageView2/0/w/1280/h/960/ignore-error/1)]

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

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fxbAJS7q-1649752722724)(https://user-gold-cdn.xitu.io/2018/4/27/16307a089f557a4d?imageView2/0/w/1280/h/960/ignore-error/1)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KtaCBg36-1649752722724)(https://user-gold-cdn.xitu.io/2018/4/27/16307a08a6460383?imageView2/0/w/1280/h/960/ignore-error/1)]

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d08S5mEh-1649752722725)(https://user-gold-cdn.xitu.io/2018/4/27/16307a08cd00aadf?imageView2/0/w/1280/h/960/ignore-error/1)]

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-09vM6ZP9-1649752722725)(https://user-gold-cdn.xitu.io/2018/4/27/16307a08ceada8a4?imageView2/0/w/1280/h/960/ignore-error/1)]

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
    */
    @Override
    public 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);
    }

}

@Override
public void onUIRefreshBegin(PtrFrameLayout var1) {
this.init();
}

@Override
public void onUIRefreshComplete(PtrFrameLayout var1) {
this.onUIRefreshComplete();
}

@Override
public void onUIRefreshPrepare(PtrFrameLayout var1) {
this.onUIRefreshPrepare();
}

@Override
public void onUIReset(PtrFrameLayout var1) {
this.onUIRefreshPrepare();
}

最后

感觉现在好多人都在说什么安卓快凉了,工作越来越难找了。又是说什么程序员中年危机啥的,为啥我这年近30的老农根本没有这种感觉,反倒觉得那些贩卖焦虑的都是瞎j8扯谈。当然,职业危机意识确实是要有的,但根本没到那种草木皆兵的地步好吗?

Android凉了都是弱者的借口和说辞。虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

所以,最后这里放上我耗时两个月,将自己8年Android开发的知识笔记整理成的Android开发者必知必会系统学习资料笔记,上述知识点在笔记中都有详细的解读,里面还包含了腾讯、字节跳动、阿里、百度2019-2021面试真题解析,并且把每个技术点整理成了视频和PDF(知识脉络 + 诸多细节)。

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。
脉络 + 诸多细节)。

[外链图片转存中…(img-WM235bSR-1649752722725)]

以上全套学习笔记面试宝典,吃透一半保你可以吊打面试官,只有自己真正强大了,有核心竞争力,你才有拒绝offer的权力,所以,奋斗吧!骚年们!千里之行,始于足下。种下一颗树最好的时间是十年前,其次,就是现在。

最后,赠与大家一句诗,共勉!

不驰于空想,不骛于虚声。不忘初心,方得始终。
Android开发不会这些?如何面试拿高薪!

猜你喜欢

转载自blog.csdn.net/m0_61111814/article/details/124127918
apk
今日推荐