簡(jiǎn)介
有時(shí)候我們集成第三方SDK時(shí),其中某個(gè)類(lèi)不符合我們的邏輯或者出現(xiàn)某個(gè)bug,而我們又沒(méi)有源碼,這個(gè)時(shí)候本篇博客就能解決你的問(wèn)題下翎。
通常純Java代碼會(huì)這樣操作,比如修改一個(gè)jar中的Test.class文件宝当,首先將jar包內(nèi)容解壓到一個(gè)目錄下面视事,然后使用jd-gui工具,反編譯目錄下Test.class文件庆揩,然后在該目錄下新建Test.java文件俐东,將反編譯的內(nèi)容拷貝入其中,修改完成之后執(zhí)行javac Test.java订晌,會(huì)在同一目錄生成Test.class虏辫,再將新的Test.class替換之前的Test.class,再重新打包成jar包锈拨。但是在Android中jar中就不好這么修改砌庄,因?yàn)槔锩姘珹ndroid SDK內(nèi)容,以下我們來(lái)介紹Android中修改jar中內(nèi)容奕枢。
原理
生成jar包
首先我們生成一個(gè)演示jar包娄昆,在Android Studio項(xiàng)目中新建一個(gè)library,其中添加兩個(gè)類(lèi)缝彬。
JarClassTest.java
public class JarClassTest {
private static JarClassTest jarClassTest;
private JarClassTest() {
}
/**
* 實(shí)例化對(duì)象
*
* @return JarClassTest
*/
public static JarClassTest getInstance() {
if (jarClassTest == null) {
jarClassTest = new JarClassTest();
}
return jarClassTest;
}
/**
* 彈出toast
*/
public void showToast(Context context) {
ToastUtil.showTip(context);
}
}
ToastUtil.java
public class ToastUtil {
/**
* @param context 上下文
*/
public static void showTip(Context context) {
Toast.makeText(context, "替換之前彈出的提示", Toast.LENGTH_LONG).show();
}
}
在library的gradle中添加:
task makeJar(type: Copy) {
delete 'build/libs/TestJar_V1.0.jar' //刪除之前的舊jar包
from('build/intermediates/packaged-classes/release/') //從這個(gè)目錄下取出默認(rèn)jar包
into('build/libs/') //將jar包輸出到指定目錄下
include('classes.jar')
rename('classes.jar', 'TestJar_V1.0.jar') //自定義jar包的名字
}
makeJar.dependsOn(build)
生成jar包:
或者在 Android Studio 終端窗口中輸入 gradlew makeJar 命令萌焰。
在build/libs/文件下找到TestJar_V1.0.jar文件,將jar文件拷貝到項(xiàng)目中使用谷浅。
修改jar
新建一個(gè)項(xiàng)目扒俯,將jar包拷貝到libs中族购,添加jar依賴(lài)。
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnToast = findViewById(R.id.btn_toast);
btnToast.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
JarClassTest.getInstance().showToast(MainActivity.this);
}
});
}
}
這個(gè)時(shí)候我需要修改ToastUtil.class里面的內(nèi)容陵珍,假設(shè)在沒(méi)有源碼的情況下,我們要修改中文提示违施。
新建ToastUtil.java
首先在我們項(xiàng)目的java包文件下創(chuàng)建與需要修改class一樣的包名互纯,重要的事情說(shuō)三遍:
保持包名一致!
保持包名一致磕蒲!
保持包名一致留潦!
在新建的包下新建一個(gè)相同類(lèi)名.java文件,將class文件下的內(nèi)容考入.java文件中辣往,修改不合理的地方兔院,自己還可以添加一下代碼。
執(zhí)行程序站削,報(bào)如下錯(cuò)誤坊萝,這就是由于項(xiàng)目中存在兩個(gè)相同的class文件導(dǎo)致的。
這個(gè)時(shí)候我們把jar中的class文件刪除许起。
在執(zhí)行程序十偶,如下圖:
合成jar包
當(dāng)我們調(diào)試完成之后,可以以這種形式存在項(xiàng)目中园细,這樣隨時(shí)可以修改.java文件里面的代碼惦积,當(dāng)然為了jar的完整性,我們還是考慮將修改完的.class文件拷貝到j(luò)ar包中猛频,替換之前的class文件狮崩,合成一個(gè)完整的jar包。
在Android Studio中執(zhí)行build->make project后在app/build/intermediates/javac/debug/compileDebugJavaWithJavac/classes路徑下
將相關(guān)的class文件拷貝出導(dǎo)入之前的jar包鹿寻,刪除項(xiàng)目中.java文件睦柴,OK 這時(shí)候就可愉快的運(yùn)行了。
注意:以上刪除烈和、添加爱只、覆蓋建議直接在壓縮軟件中進(jìn)行,方便
進(jìn)階jar包混淆
首先在library中proguard-rules.pro添加常規(guī)混淆規(guī)則
-optimizationpasses 5
-dontusemixedcaseclassnames
-dontskipnonpubliclibraryclasses
-dontpreverify
-verbose
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class com.android.vending.licensing.ILicensingService
#忽略警告
-ignorewarnings
#保證是獨(dú)立的jar,沒(méi)有任何項(xiàng)目引用,如果不寫(xiě)就會(huì)認(rèn)為我們所有的代碼是無(wú)用的,從而把所有的代碼壓縮掉,導(dǎo)出一個(gè)空的jar
-dontshrink
#保護(hù)泛型
-keepattributes Signature
-keepclasseswithmembernames class * {
native <methods>;
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet);
}
-keepclasseswithmembernames class * {
public <init>(android.content.Context, android.util.AttributeSet, int);
}
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
需要注意的問(wèn)題招刹,因?yàn)槲覀冃枰猨ar包被別人使用恬试,因此我們需要對(duì)外提供接口需要暴露出來(lái),不需要混淆疯暑,可在配置文件加上添加如下過(guò)濾規(guī)則训柴。
-keep public class com.zhj.jartest.JarClassTest{
#保持了類(lèi)mylibrary里面public 修飾的成員變量和public修飾的方法。
public <fields>;
public <methods>;
}
將混淆打開(kāi)
buildTypes {
release {
//開(kāi)啟關(guān)閉混淆
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
這個(gè)時(shí)候生成的jar包有點(diǎn)那么羞澀了
這個(gè)時(shí)候我們修改與之前步驟一樣妇拯,里面添加代碼直接寫(xiě)邏輯就行了幻馁,但是修改混淆的代碼還是有點(diǎn)難度洗鸵,需要了解這種a、b仗嗦、c代表什么才能寫(xiě)邏輯膘滨。
總結(jié)
可見(jiàn)修改jar包內(nèi)容是非常簡(jiǎn)單,aar包中的jar的修改道理相似稀拐,所以提供給別人jar包時(shí)火邓,為了保護(hù)好jar包價(jià)值,需要對(duì)代碼進(jìn)行混淆德撬,或者添加一些不相關(guān)的代碼铲咨,增加修改、破解代碼邏輯的難度蜓洪。