类加载器调用dex文件
获取dex文件的方式
直接获取
- 项目运行后 在as工程build\intermediates\project_dex_archive\debug\out\com 这个目录下会生成对应脚本的dex文件
缺点:一个dex文件内只有一个脚本
jar包转dex
新建了两个脚本 loadTest NetTool
task clearJar(type: Delete){
delete 'libs/test.jar'
}
task makeJar(type: org.gradle.api.tasks.bundling.Jar){
baseName('test')
from('build/intermediates/javac/debug/classes/com/test/myapplication')
into('com/test/myapplication/')
exclude('MainActivity.class', 'BuildConfig.class', 'MainActivity$1.class') //屏蔽掉多余的文件
exclude{ it.name.startsWith('R$');}
}
makeJar.dependsOn(clearJar, build)
运行后获得jar包
使用dx工具
jadx查看dex文件的结构
加载dex
将dex文件复制到另一个工程的assets目录下
因为DexClassLoader 参数需要一个文件路径 但是assets 中的文件并不是一个普通的文件路径,而是一个封装在 APK 中的资源。因此,你必须先将 dex 文件从 assets 复制到设备上的一个可访问的目录,然后再使用 DexClassLoader 加载它。
private void LoadDexClass(){
File cache = this.getCacheDir(); // 获取内置缓存目录 dex文件大的话可以换成SD卡
String file = cache.getAbsolutePath() + File.separator + "mpl"; //新建一个目录用于存放复制的dex
FileTool.copyFile(this, "loadTest.dex", file);
DexClassLoader dexClassLoader = new DexClassLoader(file, cache.getAbsolutePath(), null, getClassLoader()); // 第一个参数:dex的位置、第二个参数:存放优化后dex的位置
try{
Class libClazz = dexClassLoader.loadClass("com.test.myapplication.loadTest");
Object obj = libClazz.newInstance(); //类似反射的使用
Method method = libClazz.getDeclaredMethod("Say");
method.invoke(obj);
Field field = libClazz.getField("url");
String url = (String) field.get(obj);
Log.i("main", url);
}catch (Exception e){
e.printStackTrace();
}
}
private void LoadDexClass2(){
File cache = this.getCacheDir();
String file = cache.getAbsolutePath() + File.separator + "mpl";
FileTool.copyFile(this, "NetTool.dex", file);
DexClassLoader dexClassLoader = new DexClassLoader(file, cache.getAbsolutePath(), null, getClassLoader());
try{
Class libClazz = dexClassLoader.loadClass("com.test.myapplication.NetTool");
Object obj = libClazz.newInstance();
Method method = libClazz.getDeclaredMethod("getDizhi");
int dizhi = (int)method.invoke(obj);
Log.i("main", String.valueOf(dizhi));
Method method2 = libClazz.getDeclaredMethod("setDizhi", int.class);
method2.invoke(obj,99);
int dizhi23 = (int)method.invoke(obj);
Log.i("main", String.valueOf(dizhi23));
}catch (Exception e){
e.printStackTrace();
}
}
import android.content.Context;
import android.util.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class FileTool {
//默认dex路径在assests里面
public static void copyFile(Context context, String fileName, String toPath){
try{
InputStream inputStream = context.getAssets().open(fileName);
File to = new File(toPath);
if(!to.exists()){
to.createNewFile();
}
OutputStream outputStream = new FileOutputStream(to.getAbsoluteFile());
byte[] buffer = new byte[1024];
int i;
while ((i = inputStream.read(buffer)) != -1){
outputStream.write(buffer, 0 ,i);
}
if(inputStream != null)
inputStream.close();
else
Log.i("FileTool", "input null");
if(outputStream != null)
outputStream.close();
else
Log.i("FileTool", "output null");
}catch (Exception e){
e.printStackTrace();
}
}
}
package com.test.myapplication;
import android.util.Log;
public class loadTest {
public String url = "https://www.baidu.com";
public void Say(){
Log.i("dex", url);
}
}
---------------------------------------------------
package com.test.myapplication;
import android.util.Log;
public class NetTool {
private int dizhi = 12138;
public int getDizhi(){
return dizhi;
}
public void setDizhi(int a){
dizhi = a;
Log.i("dex", "修改成功?");
}
}
从网上下载dex加载
import android.app.DownloadManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Environment;
import android.widget.Toast;
public class FileDownloader {
private Context context;
public FileDownloader(Context context) {
this.context = context;
}
public void downloadFile(String url, String fileName) {
DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(false)
.setTitle(fileName)
.setDescription("Downloading")
.setDestinationInExternalFilesDir(context, null, fileName); //null 为下载到私有目录
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
if (downloadManager != null) {
long downloadId = downloadManager.enqueue(request);
registerDownloadReceiver(downloadId, fileName);
}
}
private void registerDownloadReceiver(final long downloadId, final String fileName) {
BroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
if (id == downloadId) {
Toast.makeText(context, "Download completed", Toast.LENGTH_SHORT).show();
// You can perform further operations here, such as moving the downloaded file to a different location
}
}
};
context.registerReceiver(receiver, new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
}
}
public class MainActivity extends AppCompatActivity {
private Dynamic dynamic;
private FileDownloader fileDownloader;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.tx).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// LoadDexClass();
// LoadDexClass2();
fileDownloader = new FileDownloader(getApplicationContext());
String url = "---------";
String fileName = "out.dex";
fileDownloader.downloadFile(url, fileName);
loadDownloadedDexFile("out.dex");
}
});
}
private void loadDownloadedDexFile(String fileName) {
File dexFile = new File(this.getExternalFilesDir(null), fileName); // 获取下载的 dex 文件路径
if (!dexFile.exists()) {
Toast.makeText(this, "Dex file not found", Toast.LENGTH_SHORT).show();
return;
}
String dexPath = dexFile.getAbsolutePath();
DexClassLoader dexClassLoader = new DexClassLoader(dexPath, this.getCacheDir().getAbsolutePath(), null, this.getClassLoader());
try {
Class libClazz = dexClassLoader.loadClass("com.test.myapplication.loadTest");
Object obj = libClazz.newInstance();
Method method = libClazz.getDeclaredMethod("Say");
method.invoke(obj);
Field field = libClazz.getField("url");
String url = (String) field.get(obj);
Log.i("main", url);
} catch (Exception e) {
e.printStackTrace();
}
}
}