package com.ypp.apk.sdk.parse;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.ypp.apk.sdk.parse.model.ApkInfo;
import com.ypp.apk.sdk.parse.model.LaunchActivityIconParser;
import com.ypp.apk.sdk.parse.util.AdaptiveIconUtils;
import com.ypp.util.size.StorageUnitUtils;
import lombok.extern.slf4j.Slf4j;
import net.dongliu.apk.parser.ApkFile;
import net.dongliu.apk.parser.bean.AdaptiveIcon;
import net.dongliu.apk.parser.bean.ApkMeta;
import net.dongliu.apk.parser.bean.Icon;
import net.dongliu.apk.parser.bean.IconFace;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
/**
* APK解析工具
*
* @author ypp
* @date 2021-12-07 13:50
**/
@Slf4j
public class ApkParseUtils {
/**
* 解析apk
*
* @param apkFilePath apk文件路径
* @return
* @throws Exception
*/
public static ApkInfo parse(String apkFilePath) throws Exception {
ApkFile apkFile = new ApkFile(apkFilePath);
ApkMeta apkMeta = apkFile.getApkMeta();
ApkInfo apkInfo = new ApkInfo();
// 基本信息
apkInfo.setAppName(apkMeta.getLabel());
if (StrUtil.isEmpty(apkInfo.getAppName())) {
apkInfo.setAppName(apkMeta.getName());
}
// 进一步从xml中截取app名称
if (StrUtil.isEmpty(apkInfo.getAppName())) {
String name = getNameFromXml(apkFile.getManifestXml());
apkInfo.setAppName(name);
}
apkInfo.setVersionName(apkMeta.getVersionName());
apkInfo.setVersionCode(apkMeta.getVersionCode());
apkInfo.setAppPackage(apkMeta.getPackageName());
// 解析图标
parseIcon(apkInfo, apkFile, apkFilePath);
// apk大小
apkInfo.setAppSize(new File(apkFilePath).length());
apkInfo.setAppSizeUnit(StorageUnitUtils.withUnit(apkInfo.getAppSize()));
// 获取app基本信息
log.debug("获取到应用基本信息:versionName: {}, versionCode: {}, appName: {}, packageName: {}, label: {}",
apkInfo.getVersionName(), apkInfo.getVersionCode(), apkInfo.getAppName(), apkInfo.getAppPackage(), apkMeta.getLabel());
// 关闭文件流,否则后续不会删除
apkFile.close();
return apkInfo;
}
/**
* 解析图标
*
* @param apkInfo
* @param apkFile
* @param apkFilePath
* @throws Exception
*/
private static void parseIcon(ApkInfo apkInfo, ApkFile apkFile, String apkFilePath) throws Exception {
// 从icon列表中取一个比较适中的icon(尺寸适中)
List<IconFace> iconFaceList = apkFile.getAllIcons();
// 如果图标列表为空,那么可能是通过 启动activity 设置的图标
if (CollectionUtil.isEmpty(iconFaceList)) {
iconFaceList = parseLaunchActivityIcon(apkFile);
}
IconFace iconFace = null;
// 优先取出Icon类型的图标,如果存在则取中间的某个Icon
List<IconFace> iconList = iconFaceList.stream().filter(tmpIconFace -> {
return tmpIconFace instanceof Icon;
}).collect(Collectors.toList());
if (CollectionUtil.isNotEmpty(iconList)) {
// 取中间Icon
int middle = iconList.size() / 2;
iconFace = iconList.get(middle);
}
// 如果为空,则从尾开始遍历直到找到一个不为空的图标为止
if (isNull(iconFace)) {
for (int i = iconFaceList.size() - 1; i >= 0; i--) {
iconFace = iconFaceList.get(i);
if (!isNull(iconFace)) {
break;
}
}
}
// 如果最终没有找到可用图标,则返回null
if (isNull(iconFace)) {
apkInfo.setIcon(null);
apkInfo.setIconName("icon.png");
} else {
apkInfo.setIcon(iconFace.getData());
apkInfo.setIconName(iconFace.getPath());
// 自适应图标处理
if (iconFace instanceof AdaptiveIcon) {
AdaptiveIcon adaptiveIcon = (AdaptiveIcon) iconFace;
parseAdaptiveIcon(apkFile, apkFilePath, adaptiveIcon, apkInfo);
}
}
}
/**
* 解析AdaptiveIcon
*
* @param apkFile
* @param apkFilePath
* @param adaptiveIcon
* @param apkInfo
* @throws Exception
*/
private static void parseAdaptiveIcon(ApkFile apkFile, String apkFilePath, AdaptiveIcon adaptiveIcon, ApkInfo apkInfo) throws Exception {
String iconName = "";
byte[] foregroundBytes = null;
byte[] backgroundBytes = null;
// 兼容vector格式的图标
// 前景
if (adaptiveIcon.getForeground().getPath().endsWith(".xml")){
String xml = apkFile.transBinaryXml(adaptiveIcon.getForeground().getPath());
String parentPath = new File(apkFilePath).getParent();
foregroundBytes = AdaptiveIconUtils.getIconByXml(xml, parentPath);
iconName = adaptiveIcon.getPath().replaceAll("\\.xml", "\\.png");
} else {
foregroundBytes = adaptiveIcon.getForeground().getData();
iconName = adaptiveIcon.getForeground().getPath().replaceAll("\\.xml", "\\.png");
}
// 背景
if (adaptiveIcon.getBackground() != null && StrUtil.isNotEmpty(adaptiveIcon.getBackground().getPath())
&& adaptiveIcon.getBackground().getPath().endsWith(".xml")) {
String xml = apkFile.transBinaryXml(adaptiveIcon.getBackground().getPath());
String parentPath = new File(apkFilePath).getParent();
backgroundBytes = AdaptiveIconUtils.getIconByXml(xml, parentPath);
} else {
backgroundBytes = adaptiveIcon.getBackground().getData();
}
if (backgroundBytes != null) {
// 则合并两个图片数据
BufferedImage foregroundImage = ImageIO.read(new ByteArrayInputStream(foregroundBytes));
BufferedImage backgroundImage = ImageIO.read(new ByteArrayInputStream(backgroundBytes));
int width = foregroundImage.getWidth();
int height = foregroundImage.getHeight();
BufferedImage descBufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D g2d = descBufferedImage.createGraphics();
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.drawImage(backgroundImage, 0, 0, width, height, null);
g2d.drawImage(foregroundImage, 0, 0, width, height, null);
g2d.dispose();
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(descBufferedImage, "png", byteArrayOutputStream);
apkInfo.setIcon(byteArrayOutputStream.toByteArray());
} else {
apkInfo.setIcon(foregroundBytes);
}
apkInfo.setIconName(iconName);
}
/**
* 判断图标是否为空
*
* @param iconFace
* @return
*/
private static boolean isNull(IconFace iconFace) {
if (iconFace == null) {
return true;
}
if (iconFace instanceof Icon && StrUtil.isNotEmpty(iconFace.getPath())) {
return false;
}
if (iconFace instanceof AdaptiveIcon && ((AdaptiveIcon) iconFace).getForeground() != null) {
return false;
}
return true;
}
/**
* 从
没有合适的资源?快使用搜索试试~ 我知道了~
资源推荐
资源详情
资源评论
收起资源包目录
AndroidApk包解析SDK.zip (8个子文件)
pom.xml 3KB
src
main
java
com
ypp
apk
sdk
parse
ApkParseUtils.java 9KB
model
ApkInfo.java 807B
LaunchActivityIconParser.java 4KB
LaunchActivityIconTranslator.java 4KB
Vector2Svg.java 8KB
util
Svg2PngUtils.java 936B
AdaptiveIconUtils.java 7KB
共 8 条
- 1
资源评论
- 阿玫小酱当当囧2023-07-27这个Android Apk包解析SDK让我在开发过程中省去了很多繁琐的工作,非常实用。
- MurcielagoS2023-07-27使用这个Android Apk包解析SDK,我能够轻松地获取APK文件中的信息,对我的开发工作帮助很大。
- 航知道2023-07-27这个Android Apk包解析SDK真是帮了我大忙,功能强大且易于使用!
- 挽挽深铃2023-07-27使用这个Android Apk包解析SDK后,我对APK文件的解析更加得心应手了。
- 又可乐2023-07-27我觉得这个Android Apk包解析SDK是个不可或缺的工具,对于想要深入了解APK文件的人来说,简直是福音。
来了就走下去
- 粉丝: 111
- 资源: 19
上传资源 快速赚钱
- 我的内容管理 展开
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功