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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章