Mac通过aapt获取apk文件的基本信息

通过appt获取apk文件的基本信息

1.主要过程

1.1 解析AndroidManifest.xml文件,获取E: application节点下的android:icon信息

./aapt dump xmltree /Users/mac123/Desktop/携程旅行.apk --file AndroidManifest.xml

部分输出结果如下:

E: application (line=128)
        A: http://schemas.android.com/apk/res/android:theme(0x01010000)=@0x7f0c01c5
        A: http://schemas.android.com/apk/res/android:label(0x01010001)="携程旅行" (Raw: "携程旅行")
        A: http://schemas.android.com/apk/res/android:icon(0x01010002)=@0x7f030000
        A: http://schemas.android.com/apk/res/android:name(0x01010003)="ctrip.base.component.CtripBaseApplication" (Raw: "ctrip.base.component.CtripBaseApplication")
        A: http://schemas.android.com/apk/res/android:allowBackup(0x01010280)=false
        A: http://schemas.android.com/apk/res/android:largeHeap(0x0101035a)=true
        A: http://schemas.android.com/apk/res/android:supportsRtl(0x010103af)=false
        A: http://schemas.android.com/apk/res/android:resizeableActivity(0x010104f6)=false
        A: http://schemas.android.com/apk/res/android:networkSecurityConfig(0x01010527)=@0x7f070006

上述结果中0x7f030000就是我们需要的Id信息,根据此Id在resource资源文件总获取缩略图。

1.2 打印apk文件的resource table内容

Print the contents of the resource table from the APK.

./aapt dump resources /Users/mac123/Desktop/携程旅行.apk 

部分输出结果如下:

type mipmap id=03 entryCount=3
    resource 0x7f030000 mipmap/common_ic_launcher
      (ldpi-v4) (file) res/mipmap-ldpi-v4/common_ic_launcher.png type=PNG
      (mdpi-v4) (file) res/mipmap-mdpi-v4/common_ic_launcher.png type=PNG
      (hdpi-v4) (file) res/mipmap-hdpi-v4/common_ic_launcher.png type=PNG
      (xhdpi-v4) (file) res/mipmap-xhdpi-v4/common_ic_launcher.png type=PNG
      (xxhdpi-v4) (file) res/mipmap-xxhdpi-v4/common_ic_launcher.png type=PNG
      (anydpi-v26) (file) res/mipmap-anydpi-v26/common_ic_launcher.xml type=XML
    resource 0x7f030001 mipmap/ic_launcher
      (mdpi-v4) (file) res/mipmap-mdpi-v4/ic_launcher.png type=PNG
      (hdpi-v4) (file) res/mipmap-hdpi-v4/ic_launcher.png type=PNG
      (xhdpi-v4) (file) res/mipmap-xhdpi-v4/ic_launcher.png type=PNG
      (xxhdpi-v4) (file) res/mipmap-xxhdpi-v4/ic_launcher.png type=PNG
      (xxxhdpi-v4) (file) res/mipmap-xxxhdpi-v4/ic_launcher.png type=PNG
    resource 0x7f030002 mipmap/leak_canary_icon
      (mdpi-v4) (file) res/mipmap-mdpi-v4/leak_canary_icon.png type=PNG
      (hdpi-v4) (file) res/mipmap-hdpi-v4/leak_canary_icon.png type=PNG
      (xhdpi-v4) (file) res/mipmap-xhdpi-v4/leak_canary_icon.png type=PNG
      (xxhdpi-v4) (file) res/mipmap-xxhdpi-v4/leak_canary_icon.png type=PNG
      (xxxhdpi-v4) (file) res/mipmap-xxxhdpi-v4/leak_canary_icon.png type=PNG
      (anydpi-v26) (file) res/mipmap-anydpi-v26/leak_canary_icon.xml type=XML

我们需要的信息就在type mipmap 或者type drawable中,一般情况下,上述输出结果中只有其一,因此代码中需要分支判断,单独处理。

根据Id信息,定位到resource 0x7f030000之后,解析其内部信息,即可得到各个分辨率的缩略图路径。

res/mipmap-ldpi-v4/common_ic_launcher.png
res/mipmap-mdpi-v4/common_ic_launcher.png
res/mipmap-hdpi-v4/common_ic_launcher.png
res/mipmap-xhdpi-v4/common_ic_launcher.png
res/mipmap-xxhdpi-v4/common_ic_launcher.png

1.3 通过命令从.apk文件中解压出icon

由于考虑性能及效率,只拉取分辨率最高的一张(xxhdpi-v4),然后通过命令从.apk文件中解压出即可。

解压命令如下:

unzip -j /Users/mac123/Desktop/携程旅行.apk "res/mipmap-xxhdpi-v4/common_ic_launcher.png" -d /Users/mac123/Desktop/getAppIconDecode 

2. 其他命令

//获取bundleID
./aapt dump packagename  蘑菇街.apk

//获取版本号:
./aapt dump badging my.apk | grep “VersionName” | sed -n "s/.*versionName='\([^']*\).*/\1/p"

//获取versionCode
./aapt dump badging /path/test.apk | grep -o ‘versionCode=[^,]*’ | cut -d’=’ -f 2 | cut -d ‘ ‘ -f 1 | tr -d "'"

//获取icon
./aapt dump badging /Users/mac123/Desktop/美团.apk | sed -n "/^application: /s/.*icon='\([^']*\).*/\1/p"

3. Objective-C代码

//获取apk文件的信息
- (NSDictionary *)getApkBaseInfoWithPath:(NSString *)apkPath {
    NSMutableDictionary *baseInfoDic = [[NSMutableDictionary alloc] init];
    //获取包名
    NSArray *packageNameArgArr = @[@"dump", @"packagename", apkPath];
    NSString *packageName = [self runAAPTCommondWithArguments:packageNameArgArr];
    if (packageName != nil && packageName.length > 0) {
        packageName = [[packageName componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsJoinedByString:@""];
        [baseInfoDic setValue:packageName forKey:@"name"];
    }
    
    
    NSArray *allPropertyArr = [NSArray arrayWithObjects:@"dump",@"badging",apkPath, nil];
    NSString *allProperty = [self runAAPTCommondWithArguments:allPropertyArr];
    NSArray *outputArr = [allProperty componentsSeparatedByString:@"\n"];
    
    //获取版本号
    NSArray *packageArr = [outputArr filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @"SELF contains[c] 'versionName'"]];
    NSArray *packageStrArr = [[packageArr firstObject] componentsSeparatedByString:@" "];
    for (NSString *propertyStr in packageStrArr) {
        if ([propertyStr rangeOfString:@"="].location == NSNotFound) {
            continue;
        }
        NSArray *inlineArr = [propertyStr componentsSeparatedByString:@"="];
        NSString *keyName = [inlineArr firstObject];
        NSString *value = [inlineArr lastObject];
        value = [value stringByReplacingOccurrencesOfString:@"'" withString:@""];
        if (keyName.length > 0 && value.length > 0) {
            [baseInfoDic setValue:value forKey:keyName];
        }
    }
    
    //缩略图路径
    NSArray *containsApplicationArr = [outputArr filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @"SELF contains[c] 'application'"]];
    NSArray *applicationArr = [[containsApplicationArr lastObject] componentsSeparatedByString:@" "];
    for (NSString *propertyStr in applicationArr) {
        if ([propertyStr rangeOfString:@"="].location == NSNotFound) {
            continue;
        }
        
        NSArray *inlineArr = [propertyStr componentsSeparatedByString:@"="];
        NSString *keyName = [inlineArr firstObject];
        NSString *value = [inlineArr lastObject];
        value = [value stringByReplacingOccurrencesOfString:@"'" withString:@""];
        if (keyName.length > 0 && value.length > 0) {
            if ([keyName isEqualToString:@"icon"]) {
                if ([Help isImageType:value.pathExtension]) {
                    [baseInfoDic setValue:value forKey:keyName];
                }else {
                    NSString *iconPath = [self getApkIconFromMinmapFileWithApkPath:apkPath];
                    [baseInfoDic setValue:iconPath forKey:keyName];
                }
            }else {
                [baseInfoDic setValue:value forKey:keyName];
            }
        }
    }

    NSLog(@"app baseInfoDic = %@", baseInfoDic);
    return baseInfoDic;
}

//从minmap文件中获取apk缩略图路径
- (NSString *)getApkIconFromMinmapFileWithApkPath:(NSString *)apkPath {
    NSString *resultString = @"";
    NSArray *manifesetXMLParams = [NSArray arrayWithObjects:@"dump", @"xmltree", apkPath, @"--file", @"AndroidManifest.xml", nil];
    NSString *manifestAll = [self runAAPTCommondWithArguments:manifesetXMLParams];
    NSArray *outputArr = [manifestAll componentsSeparatedByString:@"\n"];
    NSArray *applicationArr = [outputArr filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @"SELF contains[c] 'E: application'"]];
    NSInteger applicationIndex = [outputArr indexOfObject:[applicationArr firstObject]];
    outputArr = [outputArr subarrayWithRange:NSMakeRange(applicationIndex+1, 10)];
    NSArray *containIconArr = [outputArr filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @"SELF contains[c] 'android:icon'"]];
    if (containIconArr.count > 0) {
        NSString *propertyStr = containIconArr.firstObject;
        if ([propertyStr rangeOfString:@"="].location != NSNotFound) {
            NSArray *inlineArr = [propertyStr componentsSeparatedByString:@"="];
            NSString *value = [inlineArr lastObject];
            value = [value stringByReplacingOccurrencesOfString:@"'" withString:@""];
            value = [value stringByReplacingOccurrencesOfString:@"@" withString:@""];
            if (value.length > 0) {
                resultString = [[self getAppIconPathsArrWithId:value apkPath:apkPath] lastObject];
            }
        }
    }
    return resultString;
}

//根据id查找appicon,从mimap或者drawable中截取路径
- (NSArray *)getAppIconPathsArrWithId:(NSString *)IdStr apkPath:(NSString *)apkPath {
    NSMutableArray *resultArr = [[NSMutableArray alloc] init];
    NSArray *minmapParams = [NSArray arrayWithObjects:@"dump", @"resources", apkPath, nil];
    NSString *resourcesArr = [self runAAPTCommondWithArguments:minmapParams];
    NSArray *outputArr = [resourcesArr componentsSeparatedByString:@"\n"];
    
    NSPredicate *mipmapPredicate = [NSPredicate predicateWithFormat: @"SELF contains[c] 'type mipmap'"];
    NSPredicate *drawablePredicate = [NSPredicate predicateWithFormat: @"SELF contains[c] 'type drawable'"];
    if ([outputArr filteredArrayUsingPredicate:mipmapPredicate].count > 0) {
        NSArray * containMipmapArr = [outputArr filteredArrayUsingPredicate:mipmapPredicate];
        NSString *typeOfMinmap = [containMipmapArr firstObject];
        NSInteger index = [outputArr indexOfObject:typeOfMinmap];
        if (index != NSNotFound) {
            NSArray *minmapNodeArr = [outputArr subarrayWithRange:NSMakeRange(index+1, 50)];
            NSArray *iconsArr = [minmapNodeArr filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @"SELF contains[c] %@", IdStr]];
            if (iconsArr.count > 0) {
                NSInteger iconArrIndex = [minmapNodeArr indexOfObject:[iconsArr firstObject]];
                if (iconArrIndex != NSNotFound) {
                    NSArray *images = [minmapNodeArr subarrayWithRange:NSMakeRange(iconArrIndex+1, 10)];
                    for (NSString *subStr in images) {
                        if ([subStr rangeOfString:@"resource"].location != NSNotFound) {
                            break;
                        }
                        
                        NSArray *allIconsArr = [subStr componentsSeparatedByString:@" "];
                        if([subStr rangeOfString:@"type="].location != NSNotFound) {
                            NSString *iconPath = [allIconsArr objectAtIndex:allIconsArr.count-2];
                            iconPath = [iconPath stringByReplacingOccurrencesOfString:@"\"" withString:@""];
                            iconPath = [[iconPath componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsJoinedByString:@""];
                            if ([Help isImageType:iconPath.pathExtension]) {
                                [resultArr addObject:iconPath];
                            }
                        }else {
                            NSString *iconPath = [allIconsArr lastObject];
                            iconPath = [iconPath stringByReplacingOccurrencesOfString:@"\"" withString:@""];
                            iconPath = [[iconPath componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsJoinedByString:@""];
                            if ([Help isImageType:iconPath.pathExtension]) {
                                [resultArr addObject:iconPath];
                            }
                        }
                    }
                }
            }
        }
    }else if([outputArr filteredArrayUsingPredicate:drawablePredicate].count > 0) {
        NSArray * containDrawableArr = [outputArr filteredArrayUsingPredicate:drawablePredicate];
        NSString *typeOfDrawble = [containDrawableArr firstObject];
        NSInteger index = [outputArr indexOfObject:typeOfDrawble];
        if (index != NSNotFound) {
            NSArray *drawableNodeArr = [outputArr subarrayWithRange:NSMakeRange(index+1, 50)];
            NSArray *iconsArr = [drawableNodeArr filteredArrayUsingPredicate: [NSPredicate predicateWithFormat: @"SELF contains[c] %@", IdStr]];
            if (iconsArr.count > 0) {
                NSInteger iconArrIndex = [drawableNodeArr indexOfObject:[iconsArr firstObject]];
                if (iconArrIndex != NSNotFound) {
                    NSArray *images = [drawableNodeArr subarrayWithRange:NSMakeRange(iconArrIndex+1, 10)];
                    for (NSString *subStr in images) {
                        if ([subStr rangeOfString:@"resource"].location != NSNotFound) {
                            break;
                        }
                        
                        NSArray *tempStrArr = [subStr componentsSeparatedByString:@" "];
                        NSString *typeStr = [tempStrArr lastObject];
                        NSArray *typeArr = [typeStr componentsSeparatedByString:@"="];
                        if (typeArr.count != 2 || ![typeArr.firstObject isEqualToString:@"type"]) {
                            continue;
                        }
                        if (![Help isImageType:[typeArr lastObject]]) {
                            continue;
                        }
                        
                        NSString *iconPath = [tempStrArr objectAtIndex:tempStrArr.count - 2];
                        iconPath = [iconPath stringByReplacingOccurrencesOfString:@"\"" withString:@""];
                        iconPath = [[iconPath componentsSeparatedByCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]] componentsJoinedByString:@""];
                        if ([Help isImageType:iconPath.pathExtension]) {
                            [resultArr addObject:iconPath];
                        }
                    }
                }
            }
        }
    }
    
    return resultArr;
}

//运行appt
- (NSString *)runAAPTCommondWithArguments:(NSArray *)arguments {
    NSTask *zipTask = [[NSTask alloc] init];
    NSString *launchPath = [[NSBundle mainBundle] pathForResource:@"aapt" ofType:@""];
    if (![[DMFileManager defaultManager] fileExistsAtPath:launchPath]) {
        return nil;
    }
    zipTask.launchPath = launchPath;
    zipTask.arguments = arguments;

     
    NSPipe *pipe;
    pipe = [NSPipe pipe];
    [zipTask setStandardOutput: pipe];
    
    NSFileHandle *file;
    file = [pipe fileHandleForReading];
    
    [zipTask launch];
    
    NSData *data;
    data = [file readDataToEndOfFile];
    NSString *output;
    output = [[NSString alloc] initWithData: data
                                   encoding: NSUTF8StringEncoding];
    [file closeFile];
    [zipTask waitUntilExit];
    
//    NSLog(@"output = %@", output);
    if (output.length <= 0) {
        return nil;
    }
    
    return output;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jarlen John

谢谢你给我一杯咖啡的温暖

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值