文章目录
- 1 我们的使用痛点是什么?
- 2 我们为什么需要查看class文件中的内容呢?
- 3 写代码把aar拷贝到目标文件夹中
- 4 命令行查看具体的内容
1 我们的使用痛点是什么?
当我们使用Android Studio 搜索 MainActivity 的时候可以查到内容,好像看起来很方便?
但是如果关键字在class文件中,那么就搜索不到了。
2 我们为什么需要查看class文件中的内容呢?
我们App上线之后发现用户使用流量猛增,使用各种统计手段发现程序大量创建一个名为 “xxx” (因为避免涉密不具体贴出来字符,可以看上面图片中的字符串)的线程,这个线程是谁创建的? 我们上来先搜索这个"xxx"发现我们的代码中并没有创建过这个线程,那么是谁创建的呢?
怀疑是什么aar导致的,于是我们要排查到底是哪个aar导致的呢?
3 写代码把aar拷贝到目标文件夹中
注意是使用Android Studio 运行 Java项目,不知道怎么运行java项目可以看Android Studio运行JAVA程序
public class Grep {
public static final String GRADLE_CACHE_PATH = "/Users/liangchaojie/.gradle/caches/transforms-1/files-1.1";
public static final String TARGET_PATH = "/Users/liangchaojie/Desktop/result";
private static int mCopyCount = 0;
private static int mUnZipCount = 0;
private static int mDeleteCount = 0;
public static void main(String[] args) {
File file = new File(GRADLE_CACHE_PATH);
// 1 先把缓存中的文件拷贝到指定的文件夹中
traverseCopy(file);
System.out.println("traverseCopy file number ="+mCopyCount);
// 2 把所有的jar文件解压出来到指定的文件夹中
tarJarFile(TARGET_PATH);
// 3 删除掉之前存在的jar文件
deleteJarFile(TARGET_PATH);
}
/**
* 遍历复制gradle cache 中的classes.jar到 指定目录中去
*
* @param file
*/
private static void traverseCopy(File file) {
File[] fs = file.listFiles();
for (File f : fs) {
if (f.isDirectory()) //若是目录,则递归打印该目录下的文件
{
traverseCopy(f);
} else if (f.isFile() && "classes.jar".equals(f.getName())) //若是文件,直接打印
{
mCopyCount++;
String sourcePath = f.getAbsolutePath();
String aarName = getSimpleAarPathName(f.getAbsolutePath());
String targetPath = getTargetPathName(aarName);
File fileTargetFile = new File(targetPath);
// 如果文件已经存在就不用去拷贝了
if(fileTargetFile.exists()){
continue;
}
copyFile(sourcePath, targetPath);
}
}
}
/**
* 返回类似 homepage-api-2.5.30
* @param path
* @return
*/
private static String getSimpleAarPathName(String path) {
String aarNameFirst = path.substring(path.indexOf("files-1.1/") + 10, path.indexOf(".aar/"));
String aarNameLast = path.substring(path.indexOf(".aar/") + 5, path.indexOf(".aar/") + 10);
return aarNameFirst+"-"+aarNameLast;
}
private static String getTargetPathName(String aarName) {
return TARGET_PATH + "/" + aarName +".jar";
}
public static void copyFile(String source, String target) {
try (FileInputStream fis = new FileInputStream(new File(source));
FileOutputStream fos = new FileOutputStream(new File(target))) {
FileChannel sourceChannel = fis.getChannel();
FileChannel targetChannel = fos.getChannel();
MappedByteBuffer mappedByteBuffer = sourceChannel.map(FileChannel.MapMode.READ_ONLY, 0, sourceChannel.size());
targetChannel.write(mappedByteBuffer);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
private static void deleteJarFile(String targetPath) {
File[] fs = new File(targetPath).listFiles();
for (File f : fs) {
if (f.isFile() && f.getName().contains(".jar")) {
mDeleteCount++;
f.delete();
}
}
System.out.println(" deleteJarFile file number = " + mDeleteCount);
}
/**
* 把指定目录中的 classes.jar 解压到文件中
*/
private static void tarJarFile(String targetPath) {
File[] fs = new File(targetPath).listFiles();
for (File f : fs) {
if (f.isFile() && f.getName().contains(".jar")) {
mUnZipCount++;
String directory = f.getName().substring(0, f.getName().lastIndexOf(".jar"));
String targetPathDirectory = TARGET_PATH + "/" + directory + "/";
File targetPathDirectoryFile = new File(targetPathDirectory);
// 如果已经创建过文件夹就不要去重复创建了
if (targetPathDirectoryFile.exists()) {
continue;
}
targetPathDirectoryFile.mkdirs();
UnZipUtils.unZip(f, targetPathDirectory);
}
}
System.out.println(" tarJarFile file number = " + mUnZipCount);
}
}
/**
* 对文件进行解压操作
*/
public class UnZipUtils {
private static final int BUFFER_SIZE = 2 * 1024;
/**
* zip解压
* @param srcFile zip源文件
* @param destDirPath 解压后的目标文件夹
* @throws RuntimeException 解压失败会抛出运行时异常
*/
public static void unZip(File srcFile, String destDirPath) throws RuntimeException {
long start = System.currentTimeMillis();
// 判断源文件是否存在
if (!srcFile.exists()) {
throw new RuntimeException(srcFile.getPath() + "所指文件不存在");
}
// 开始解压
ZipFile zipFile = null;
try {
zipFile = new ZipFile(srcFile);
Enumeration<?> entries = zipFile.entries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
// 如果是文件夹,就创建个文件夹
if (entry.isDirectory()) {
String dirPath = destDirPath + "/" + entry.getName();
File dir = new File(dirPath);
dir.mkdirs();
} else {
// 如果是文件,就先创建一个文件,然后用io流把内容copy过去
File targetFile = new File(destDirPath + "/" + entry.getName());
// 保证这个文件的父文件夹必须要存在
if(!targetFile.getParentFile().exists()){
targetFile.getParentFile().mkdirs();
}
targetFile.createNewFile();
// 将压缩文件内容写入到这个文件中
InputStream is = zipFile.getInputStream(entry);
FileOutputStream fos = new FileOutputStream(targetFile);
int len;
byte[] buf = new byte[BUFFER_SIZE];
while ((len = is.read(buf)) != -1) {
fos.write(buf, 0, len);
}
// 关流顺序,先打开的后关闭
fos.close();
is.close();
}
}
long end = System.currentTimeMillis();
} catch (Exception e) {
throw new RuntimeException("unzip error from ZipUtils", e);
} finally {
if(zipFile != null){
try {
zipFile.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
4 命令行查看具体的内容
grep "xxx" -r /Users/liangchaojie/Desktop/result
用Android Studio打开这个a.class,确实发现创建了线程
完结撒花~