How to Get Count of Number Methods Used in a Jar File

Is there a way to get count of number methods used in a jar file

You can convert the jar to a dex file, and then pull the number of method references out of the header. It is stored as an unsigned little endian integer, at offset 88 (0x58).

dx --dex --output=temp.dex orig.jar
cat temp.dex | head -c 92 | tail -c 4 | hexdump -e '1/4 "%d\n"'

Keep in mind that this is the number of unique methods referenced, not the number of method references. In other words, if a particular method is referenced twice in the dex file, it will only be counted once in the count in the header. And when you import this jar into your apk, the method references that are common between the two are deduplicated, so the total method reference count of the final merged apk will be <= the sum of the two.

Count the number of files inside an entry of jar file

If you want to do this in Java then it might be easier to use the ZIP File System Provider from the NIO2 API.

import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;

public class Main {

public static void main(String[] args) throws IOException {
Path jarFile = Path.of("path", "to", "file.jar");
String directoryPath = "/myFolder";

long fileCount = countFilesInDir(jarFile, directoryPath);
System.out.println(fileCount);
}

public static long countFilesInDir(Path jarFile, String directory) throws IOException {
try (FileSystem fs = FileSystems.newFileSystem(jarFile)) {
return Files.list(fs.getPath(directory)).count();
}
}
}

If you only want to count regular files then change:

return Files.list(fs.getPath(directory)).count();

To:

return Files.list(directory).filter(Files::isRegularFile).count();

And if you want to count the entries/files recursively then check out the java.nio.file.Files#find(Path,int,BiPredicate,FileVisitOption...) method.


Note: Normally you should close the Stream returned by Files.list or Files.find when done with it, but in this case it should be closed as a consequence of the FileSystem being closed.

Android jar/dex method count on windows

After unsuccessful search for a solution, I wrote two simple batch/shell scripts that do that.

The first one, methodcount.bat, checks if the file is .dex or .jar, and if it's a .jar file, it processes it with dx into dex file and then calls the second one, printhex.ps1, that actually checks the number of methods in the dex file - it reads 2 bytes beginning at 88 (little endian) and converts them into a decimal number.

To use this you to have dx somewhere in your path (it's in the android SDK build-tools/xx.x.x folder) and have PowerShell installed (it should already be installed on Windows 7/8).

Usage is very simple: methodcount.bat filename.dex|filename.jar.

Here are the scripts, but you can also find them on gist: https://gist.github.com/mrsasha/9f24e129ced1b1db791b.

methodcount.bat

@ECHO OFF
IF "%1"=="" GOTO MissingFileNameError
IF EXIST "%1" (GOTO ContinueProcessing) ELSE (GOTO FileDoesntExist)

:ContinueProcessing
set FileNameToProcess=%1
set FileNameForDx=%~n1.dex
IF "%~x1"==".dex" GOTO ProcessWithPowerShell

REM preprocess Jar with dx
IF "%~x1"==".jar" (
ECHO Processing Jar %FileNameToProcess% with DX!
CALL dx --dex --output=%FileNameForDx% %FileNameToProcess%
set FileNameToProcess=%FileNameForDx%
IF ERRORLEVEL 1 GOTO DxProcessingError
)

:ProcessWithPowerShell
ECHO Counting methods in DEX file %FileNameToProcess%
CALL powershell -noexit -executionpolicy bypass "& ".\printhex.ps1" %FileNameToProcess%
GOTO End

:MissingFileNameError
@ECHO Missing filename for processing
GOTO End

:DxProcessingError
@ECHO Error processing file %1% with dx!
GOTO End

:FileDoesntExist
@ECHO File %1% doesn't exist!
GOTO End

:End

printhex.ps1

<#
.SYNOPSIS
Outputs the number of methods in a dex file.

.PARAMETER Path
Specifies the path to a file. Wildcards are not permitted.

#>
param(
[parameter(Position=0,Mandatory=$TRUE)]
[String] $Path
)

if ( -not (test-path -literalpath $Path) ) {
write-error "Path '$Path' not found." -category ObjectNotFound
exit
}

$item = get-item -literalpath $Path -force
if ( -not ($? -and ($item -is [System.IO.FileInfo])) ) {
write-error "'$Path' is not a file in the file system." -category InvalidType
exit
}

if ( $item.Length -gt [UInt32]::MaxValue ) {
write-error "'$Path' is too large." -category OpenError
exit
}

$stream = [System.IO.File]::OpenRead($item.FullName)
$buffer = new-object Byte[] 2
$stream.Position = 88
$bytesread = $stream.Read($buffer, 0, 2)
$output = $buffer[0..1]
#("{1:X2} {0:X2}") -f $output
$outputdec = $buffer[1]*256 + $buffer[0]
"Number of methods is " + $outputdec
$stream.Close()

Count lines of code (LOC) for JAR and AAR

In some cases, you might be able to get a rough idea idea of the number of lines, using the debug information in the dex file.

Using dexlib2, you could do something like:

public static void main(String[] args) throws IOException {
DexFile dexFile = DexFileFactory.loadDexFile(args[0], 15);

long lineCount = 0;

for (ClassDef classDef: dexFile.getClasses()) {
for (Method method: classDef.getMethods()) {
MethodImplementation impl = method.getImplementation();
if (impl != null) {
for (DebugItem debugItem: impl.getDebugItems()) {
if (debugItem.getDebugItemType() == DebugItemType.LINE_NUMBER) {
lineCount++;
}
}
}
}
}

System.out.println(String.format("%d lines", lineCount));
}

An alternative metric for comparing code size might be the number of instructions in a dex file. e.g.

public static void main(String[] args) throws IOException {
DexFile dexFile = DexFileFactory.loadDexFile(args[0], 15);

long instructionCount = 0;

for (ClassDef classDef: dexFile.getClasses()) {
for (Method method: classDef.getMethods()) {
MethodImplementation impl = method.getImplementation();
if (impl != null) {
for (Instruction instruction: impl.getInstructions()) {
instructionCount++;
}
}
}
}

System.out.println(String.format("%d instructions", instructionCount));
}

How to count methods in a Kotlin library

The dexcount-gradle-plugin works with libraries as well. The resulting @aar file should not differ when using Kotlin instead of Java, since Kotlin just compiles to Java byte code as well. So just use it and you'll get all the information you need for your library.

buildscript {
dependencies {
classpath 'com.getkeepsafe.dexcount:dexcount-gradle-plugin:0.6.4'
}
}
apply plugin: 'com.getkeepsafe.dexcount'

How to compile Protobuf in a way to get less number of method count

Have a look at Exclude unused parts of dependencies from jar (Maven) and configure Maven Shade minimizeJar to include class files. They may well be what you need if you are using Maven to build your software.

Count character occurrences in a file

You can use a hashmap:

Map<Character, Integer> charMap = new HashMap<Character, Integer>();
while((ch=reader.read()) != -1) {
char key = (char)ch;
if(charMap.containsKey(key)) {
int value = charMap.get(key);
charMap.put(key, value + 1);
} else {
charMap.put(key,1);
}
}


Related Topics



Leave a reply



Submit