本文閱讀須要10分鐘. 你可能的收穫:html
每個android開發程序員頗有必要掌握一些逆向工程的知識技巧,其中好處不勝枚舉,我細數一二:android
各位老鐵能夠下載體驗一下這個app的***下拉刷新***,絕對讓你頗有收穫git
那麼問題來了,假如你的項目中要實現這樣一個控件,你須要多長時間? 1天? 2天? 個人答案是1個小時 反編譯提取控件,理解其中繪製邏輯.找UI改改樣式,over.程序員
嘻嘻,下面我會給個關於反編譯技巧的教程,市面上大多數的應用脫了褲子給咱們看,刺激吧?github
還原實現下拉刷新控件shell
①控件位置難找; ②資源文件分散; ③代碼通過混淆,代碼邏輯須要跟着做者實現思路走一遍bash
1.ShakaApkTool 2.Apktool 3.Dex2Jar 4.Zipalign 5.SignApk 6.JDGUI架構
我是直接github上找到一個mac工具軟件:android crack tool app
c.關鍵:在fragment中找到控件(須要一點點小耐心) 一開始沒找着,想了一下,這個fragment確定是使用了下拉刷新的,位置沒找錯. 那問題出如今哪裏呢?框架
in.srain.cube.views.ptr.PtrFrameLayout //第一反應是網上的開源庫,github上一搜索,果真~
複製代碼
36Kr使用比較出名的下拉刷新庫github地址:android-Ultra-Pull-To-Refresh
d.根據下拉刷新頭部KrHeader以及資源R文件定位資源文件
e. 根據KrHeader的變量LottieAnimationView b找到lottie動畫 根據lottie文檔官網,動畫文件通常放在asserts文件或res/raw中
至此,這個控件已經被完徹底全的抽取出來了
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();
}
public void setShowRefreshInfo(boolean showRefreshInfo) {
this.isShowRefreshInfo = showRefreshInfo;
}
}
複製代碼
下拉刷新調用方式
/**
* 鄭重聲明:本源碼均來自互聯網,僅供我的欣賞、學習之用,
* 版權歸36氪產品發行公司全部,任何組織和我的不得公開傳播或用於任何商業盈利用途,
* 不然一切後果由該組織或我的承擔。
* 本人不承擔任何法律及連帶責任!請自覺於下載後24小時內刪除
*
*/
public class MainActivity extends AppCompatActivity implements PtrHandler {
private PtrFrameLayout mPtr;
private KrHeader krHeader;
@Override
protected 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() {
@Override
public void onClick(View view) {
startActivity(new Intent(MainActivity.this, TestActivity.class));
}
});
}
@Override
public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) {
return true;
}
@Override
public void onRefreshBegin(PtrFrameLayout frame) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
mPtr.refreshComplete();
}
}, 300);
}
}
複製代碼
但願能獲得你的一個star,這是對我寫這篇博文給你們帶來一些另類思路和收穫的鼓勵 36KrRefreshDemo
基本上與36KrRefresh相似,關鍵點都在於更多的耐心以及能提取成功的信心 這個控件提取遇到了更多的困難: ①控件涉及多個自定義view,再加上代碼混淆的影響下,才肯定這個控件涉及3個view,提取難度加大很多 ②控件在app中出現的層級更深,定位的時間耗費更多 ③控件中的繪製邏輯更加複雜,須要更多精力去理解混淆後的代碼 PS:過程當中發現了這個控件的一個bug:放大倍數過大,OOM,應用閃退.
源碼地址: DrawContainerDemo 歡迎star,小小鼓勵一下我~
總結一下反編譯參考競品的技巧:
核心業務的複雜功能實現,可能須要一個月,可是若是你經過反編譯源碼級別地瞭解競品,借鑑競品,說得粗俗點,競品脫了褲子讓你觀摩,那你完成這個功能可能只須要1個星期,節省了三個星期,開發效率提升300%
只是爲了工做敲代碼的程序員,就有點shameless了.不該該只看到其功利性價值,更應該去挖掘自我價值,學習一些優秀程序員敲的商用級別代碼; 這兩個反編譯過程,我是帶着強烈好奇心去完成的:
經老上司彼時芒種提醒,新增了兩個編譯小技巧
adb shell dumpsys activity | grep "Running activities" -A 7
複製代碼