Whenever I try to use the SeekableInMemoryByteChannel
class from the Apache commons-compress package the application crashes with a java.lang.NoClassDefFoundError: org.apache.commons.compress.utils.SeekableInMemoryByteChannel
exception.
The particularly interesting part of this, for me at least, is that the issue does not occur on devices running Android 7+, but does on all other versions that I have tested. To be more specific I have observed that this issue occurs on Android 6.0.1, 6.0, 5.1.1 but does not occur on Android 7.0 or 7.1.1. Each of these devices comes from a different vendor.
So I'm facing a severe case of ghost class. At first I thought that this may have been related to the fact that my app was using MultiDex, or that the build process was incorrectly configured. But the same happens on a completely new project that does not have these features. I've tried to use different classes from the same package and they are resolved successfully. I added the package to ProGuard but no luck.
This is the code that replicates the issue:
package lt.kaz.compresstest;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry;
import org.apache.commons.compress.archivers.zip.ZipArchiveEntry;
import org.apache.commons.compress.utils.SeekableInMemoryByteChannel;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ZipArchiveEntry zipEntry = new ZipArchiveEntry("hello");
zipEntry.setSize(600);
SevenZArchiveEntry sevenZipEntry = new SevenZArchiveEntry();
sevenZipEntry.setName("sevenZip");
try {
MainActivity.class.getClassLoader().loadClass("org.apache.commons.compress.utils.ChecksumCalculatingInputStream");
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
SeekableInMemoryByteChannel channel = new SeekableInMemoryByteChannel(new byte[]{5, 10, 15, 10});
// App never gets here prior to Android N
Log.d("lt.kaz", channel.toString());
}
}
... and the gradle file ...
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
buildToolsVersion "26.0.1"
defaultConfig {
applicationId 'lt.kaz.compresstest'
minSdkVersion 19
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:26.+'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
compile 'com.android.support:design:26.+'
compile 'org.apache.commons:commons-compress:1.14'
testCompile 'junit:junit:4.12'
}
... and ProGuard ...
-keep public class org.apache.commons.compress
Any help would be really appreciated. What could be the reason for such behaviour? How can it be solved?
Apparently, the SeekableByteChannel
interface form the java.nio.channels
package is only available from Android API level 24 (hence android N).
My final solution, since my original intent was to use the SevenZFile
class, was to downgrade commons-compress to version 1.12, since the implementation there doesn't use the the SeekableByteChannel
class.