OVERVIEW:
I am facing an issue while accessing the activity of the on-demand dynamic feature module from the base module due to proguard. (most probably I guess)
DESCRIPTION:
I have implemented an ON-DEMAND dynamic feature module with app bundle and uploaded on play store.
Implemented proguard with custom rules with it.
After downloading the application from the play store and while accessing that module at runtime, the module gets downloaded. Just after downloading it, I have a call for accessing an activity from my base module to that dynamic module.
I am getting error as like below
...
java.lang.RuntimeException: Unable to instantiate activity ComponentInfo{xxx.yyyyyy.zzzzzz.stage/xxx.yyyyyy.zzzzzz.apphub.appview.view.AppHubActivity}:
java.lang.ClassNotFoundException: Didn't find class "xxx.yyyyyy.zzzzzz.apphub.appview.view.AppHubActivity"
on path: DexPathList[[zip file "/system/framework/org.apache.http.legacy.boot.jar", zip file
...
...
FYI:
xxx.yyyyyy.zzzzzz is my changed package name for privacy.
IRONY:
This entire code is working perfectly in debugging while accessing it from the app bundle locally without proguard.
I have tried all the links below to overcome this but could not.
1) https://issuetracker.google.com/issues/120517460
2) https://github.com/android/plaid/issues/764
3) java.lang.NoClassDefFoundError:failed resolution of :Lorg/apache/http/ProtocolVersion
4) https://issuetracker.google.com/issues/79478779
5) https://github.com/android/app-bundle-samples/issues/17
I have also tried all types of proguard files which we can use, but still helpless.
Also kept that both classes in proguard: base and dynamic module activity class but got no success.
Hopefully looking for the solution here.
UPDATE:
not working in android OS 8,9 but working file in android 10.
I started implementing dynamic feature module this month in my app and proguard was given me issues. I also didn't wanted to push my app to playstore without obfuscating the codes. So this is how i solved this with proguard enabled.
Android proguard optimizer already keeps all classes that extends android.view.View. That means any class that extends the View class will not be obfuscated by proguard.
So i created a class in my featured module and extended View and overrided just the first method because it's not for view in my views hierarchy
public class YourCalssName extends View {
public YourClassName(Context context) {
super(context);
}
public static void launchActivity(Activity activity){
activity.startActivity(new Intent(activity,YourMainActivityInYourFeatureModule.class));
}
}
But Android proguard optimizer doesn't keep method names. Only setters and getters are kept.
So i added a proguard keep rule in my main app to not obfuscate static methods to classes that extends android.view.View
-keepclassmembers public class * extends android.view.View {
public static <methods>;
}
Then i used reflection to call the static method to launch my featured module main activity
Class myClass = Class.forName("your_fully_qualified_name");//Without .class
Method method = myClass.getDeclaredMethod("launchActivity",Activity.class);
method.invoke(null, this) ;
This will keep your class name and methods that launches your featured module activity and it static methods.
Also make sure you add this code to all your activities in your featured module
#Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base)
SplitCompat.installActivity(this);
}
Make sure you use SplitCompat in the activities of your on-demand module AND in your Application.
See https://developer.android.com/guide/playcore#access_downloaded_modules which explains in more details how to do so.
Related
Working with an Android project in Android Studio 3.2, having enabled Proguard and some specific rules, I'm not able to figure out the following:
a specific package (and its subpackages) in a library module, used by client code, is preserved through the rule:
-keep public class com.mylib.mypackage.** {
public protected *;
}
Now, within this package there are also a number of package-private classes, which should not be picked by that rule. Some of those classes are effectively obfuscated, both in their own names and their member names, which is fine.
Instead there are some classes, implementing public interfaces, whose class names are not obfuscated, while I'd expect they should. For completeness, their member names, when not part of interface, are effectively obfuscated.
Example:
/* package */ class InternalComponent implements ExternalInterface {
// ExternalInterface is kept: Ok
// InternalComponent is kept: don't like, I'd like it renamed
#Override
public void ExternalMethod() {
// this is kept: Ok
}
public void InternalMethod() {
// this is renamed: Ok
}
}
I'd like to highlight that InternalComponent is created within some other (kept) class and only returned to client code through the ExternalInterface.
How can I also obfuscate their class names as well, if possible?
Edit #1
After #emandt answer on Proguard output files, I double checked and com.mylib.mypackage.InternalComponent is listed in seeds.txt, which according to this blog post lists all items matched by keep rules. So, for some reason, the rule above also picks package-private classes, which still seems wrong to me.
Edit #2
In the meantime, I ended up doing exactly the same approach proposed by #shizhen. For completeness, in order to extend the exclusion to any package named internal, I modified my proguard rule as:
-keep public class !com.mylib.mypackage.**.internal.*, com.mylib.mypackage.** {
public protected *;
}
(note the first part before the comma, prefixed by !)
I'll mark #shizhen answer, though I'd like to be curious as to why the original rule is also picking package-private components.
Are you working on an Android Library project? Probably YES.
In order to achieve your purpose, I am afraid that you need to re-organise your packages into something like below:
Public interfaces
com.my.package.apiforusers
Private/Internal implementations
com.my.package.apiforusers.internal
Then for your obfuscation rules, you can have it like below:
-keep public class com.my.package.apiforusers.** { public *; }
So that only the public classes/interfaces are kept and all those ones inside com.my.package.apiforusers.internal will be obfuscated.
Please note the double-asterisk at the end so that public classes/interface are also kept for the sub-packages.
In "/build/outputs/mapping/release/" folder there are few files ("usage.txt", "seeds.txt", etc..) that contain the REASONS of why and which classes/variables/methods/etc.. are not-processed/not-shrinked/ot-obfuscated via ProGuard utilities.
I'd like to provide an obfuscated AAR library which makes use of Android data binding. When I use the library from a test app, everything works fine as long as the library isn't minified by ProGuard. However, after enabling ProGuard the test app doesn't compile any more since BR fields in the generated data binding classes can't be found.
Since I couldn't find any official documentation on this "specific" subject, I tried to understand the magic behind Android data binding. The mechanism seem to be like this (please correct me, if I'm wrong):
In order to use data binding within an AAR lib, the embedding app must enable data binding, too.
This is because the the layout resources that contain data binding instructions are included without modification in the AAR.
It's therefore the embedding app's responsibility to generate the corresponding data binding classes for the layouts in the lib. (That's why the lib's view model classes must not be obfuscated, BTW.)
The challenge for the Android data binding generator is to tell the package names apart from both library and embedding app: The BR class for the library must be generated in the library's package (e. g. com.example.lib.databinding), since this class is accessed from the library's view model classes. On the other hand, the BR class of the embedding app should normally be generated within the app's package (com.example.app.databinding).
And this is exactly where my problems begin. I don't know Android exactly rises to this challenge, I only know that in my case, it works with an unobfuscated lib, and it doesn't with an obfuscated one. When I look into the generated source of the embedding app, I see:
When using the unobfuscated lib, both the BR and all *Binding.java classes are generated within the library's package, and the app compiles.
When using the obfuscated lib, both the BR and all *Binding.java classes are generated within the apps's package. Even worse, the BR only contains constants for the model variable names in the XML resource, not for the properties in the view model classes. Consequently, the app doesn't compile.
I tried to set the package of the data binding classes explicitely to the lib's package in the XML declaration, but this doesn't solve the problem with the incomplete BR class.
I have no clue where these differences come from and I' already fearing that the only solution could be removing all my nice data binding stuff from the lib ... Does anybody made similar experiences and could give me a hint, please?
These are the ProGuard exceptions I've already added to my lib:
-keep public class **.BR { public *; }
-keep public class **.BR$* { public *; }
-keepclassmembers class **.BR$* {
public static <fields>;
}
-keepclassmembers class **.R$* {
public static <fields>;
}
-keep class android.databinding.** { *; }
-keep class * extends android.databinding.** { *; }
-keep class com.example.lib.databinding.** { *; }
I managed to make it run in the meantime, but the "solution" is so weird that I really don't want to take it over to a productive version ...
When comparing the AAR files of the obfuscated and unobfuscated lib, I noticed that the classes.jar of the unobfuscated one contained these three files:
/com/example/lib/com.example.lib-br.bin
/com/example/lib/com.example.lib-layoutinfo.bin
/com/example/lib/com.example.lib-setter_store.bin
These binary files contain some of my data binding class names and are apparently important for the code generation process. I just tried to copy these files into the corresponding place of my obfuscated AAR and ... it worked!!!
But this cannot be the final solution. At least it would be more reliable than coping if I can convince ProGuard to simply keep these non-class-files within the classes.jar. Any ideas how to do this?
Much more I would appreciate to get some information on the backgrounds of this mechanism and whether it is possible to avoid such ugly low-level operations for solving a problem that actually should be standard.
Thanks in advance for any answer!
Apparently Google has resolved the issue with Gradle plugin 2.3.0, see https://code.google.com/p/android/issues/detail?id=229684.
I have the follow scenario to work on. I was given a shared library (libeffect.so) to use in a Android project i am working for a client. I dont have the shared library source code, i have just the .so file with me. The library is pre-compiled to work on android devices. Along with the shared library I have the method signature
public static native void doEffect(int param1, IntBuffer intBuffer);
So now I have some questiosn on how to make the call to this native method, of source, if this is possible having just the .so file, so there they are:
Do I need to place the native method signature in the same package/class as those defined when the .so was or I can use this signature in any package/class in my project that during runtime the jvm will be able to find the method in the shared library? For example, if this shared library was firstly used in a class mypackage.MyClass, do I need to create the same package, class and then put the method signature there?
Where do I need to place this .so file inside my eclipse android project to get this file deployed inside my apk file?
These question might sound noob, but I have never worked with jndi before so I am a bit concerned if calling the method doEffect without any error can be achieve. Any answer that can guide me is very welcome.
Many Thanks
Thiago
Do I need to place the native method signature in the same package/class as
those defined when the .so was or I
can use this signature in any
package/class in my project that
during runtime the jvm will be able to
find the method in the shared library?
For example, if this shared library
was firstly used in a class
mypackage.MyClass, do I need to create
the same package, class and then put
the method signature there?
No need to create same package/class. You can put the method signature in any package.
public class NativeLib {
static {
System.loadLibrary("so_file");
}
public static native void doEffect(int param1, IntBuffer intBuffer);
}
2.Where do I need to place this .so file inside my eclipse android project
to get this file deployed inside my apk file?
You have put this .so file in lib folder of your application . IF lib folder is not there then you can create a lib folder and put the .so file. you can call it by using System.loadLibrary("so_ file");
1 Do I need to place the native method signature in the same
package/class as those defined when the .so was or I can use this
signature in any package/class in my project that during runtime the
jvm will be able to find the method in the shared library?
According to http://docs.oracle.com/javase/6/docs/technotes/guides/jni/spec/design.html you have to use a matching package and class name.
I've only observed JNI methods where the C side functions are called things like Java_com_company_whatever_SomeClass_someMethod, which means that you have to put the 'native' declarations in a similarly-named Java class.
Use the tool 'nm' or 'nm++' (they're in the precompiled folders in the NDK) to look at the .so file and see what the functions defined in it are called. If you see any starting Java_, those're what you want.
I'm sceptical of the preceding claim that you can call functions which aren't named in the Java_PACKAGE_CLASS_METHOD format; it may be a legacy behaviour if it actually works, but even if you can, it seems dangerous - you might get the wrong one.
2 Where do I need to place this .so file inside my eclipse android
project to get this file deployed inside my apk file?
Your .so lives in libs/armeabi, libs/armeabi-v7a, libs/x86, and/or libs/mips depending on how many platforms you're working with, where 'libs' is a peer of 'src' and 'res'. I don't know whether Android looks in libs/ without the platform qualifier, but there's no evident benefit in that. The situation is slightly complicated by most/all Intel devices including fancy technology allowing them to execute most ARM libraries on x86 hardware.
Further, I like to declare an interface of a JNI class and provide a factory (it's a method here for brevity, but I prefer a factory class) that supplies a no-op implementation of the interface if things go wrong: it facilitates unit testing and also avoids having to mess about testing for null values before calling its methods (assuming you're comfortable that your shipped library will never have missing or changed method signatures - your integration tests should check that):
public interface YourLibI {
#Override
public native yourMethod();
public static final NO_OP = new YourLibI() {
#Override
public void yourMethod(){}
}
}
public class YourLib extends YourLibI {
public newYourLibI() {
try {
return new YourLib();
}
catch (UnsatisfiedLinkError e) {
Log.e("YourLibJNI", "Load failed, returning NO-OP dummy", e);
return YourLibI.NO_OP;
}
}
static {
System.loadLibrary("arbitronSDK");
}
private YourLib() {
}
#Override
public native void yourMethod();
}
I don't normally call interfaces 'xxxI' but I'm assuming your library's JNI class isn't called something nice like UtilityJNI (whereupon I'd call the interface 'Utility').
Suppose I have my Android app's source code packaged this way:
src/
my/
app/
Module.java
ModuleManager.java
module/
ModuleA.java
ModuleB.java
where Module is:
public abstract class Module {
public abstract void run();
}
, ModuleA and ModuleB extends Module:
public class ModuleA extends Module {
#Override
public void run() { /* do something */ }
}
and ModuleManager has some helper methods, among which one to retrieve all the Modules:
public class ModuleManager {
private final static List<Module> modules;
static {
List<Module> tmp= new ArrayList<Module>();
tmp.add(new ModuleA());
tmp.add(new ModuleB());
modules= Collections.unmodifiableList(tmp);
}
public static List<Module> getModules(){
return modules;
}
}
As you can see I manually filled the list of modules in ModuleManager. Instead, I would like it to be automatically filled with an instance of all the Module's subtypes in the module package.
I tried in several ways reflection-based solutions like this, which involve the use of ClassLoader::getResource(String path) to retrieve every .class file inside the module package, but it always returns an empty enumeration. I learnt this is due to the difference between Dalvik's VM and Java SE's one, and its optimized packaging of .class files inside classes.dex. I then tried to import the application's apk from /data/app/ with DexClassLoader or PathClassLoader and unsuccesfully tried their getResource() method again. Anyway I think this is not the right direction, and probably this stuff was already hackish/flawed in Java, and it would be even more in Android.
Could you suggest me a way to do that?
Thank you very much.
One way to do this in a build script, after compilation but before dex is called to create classes.dex, run a small script to get the class names in modules folder and write it to say an application.properties file to read from (or strings.xml in resources).
The disadvantage of this is it is not a pure java method you were asking for, the advantage being it will work for 2 or 10+ subclasses. Hope this helps.
What if you add an XML or a Properties with the module classes and you dynamicly created from it... it might goes something like this
<modules>
<module>
my.app.module.ModuleA
</module>
<module>
my.app.module.ModuleB
</module>
</modules>
Another solution could be to exploit a naming convention on classes' names and retrieve all of them one-by-one with Class.forName(className). In the example I made, if I know that all modules are named sequentially as ModuleA, ModuleB, ModuleC and so on, I can retrieve all of them starting from ModuleA and continuing until Class.forName("ModuleX") fails. If gaps were allowed but the number of modules was bounded (and very low), an exhaustive search on the entire range could be done.
Of course this could be applied in a very few cases, and at the expense of class' name readability.
What's the difference between file, class and activity in android?
File - It is a block of arbitrary information, or resource for storing information. It can be of any type
Class - Its a compiled form of .Java file . Android finally used this .class files to produce an executable apk
Activity - An activity is the equivalent of a Frame/Window in GUI toolkits. It is not a file or a file type it is just a class that can be extended in Android for loading UI elements on view
Class -
A class is a combination of methods, variables and data types. Every Java or Android project must have at least one class.
Example:
public class shape{
public void circle()
{
int A,B,radias;
}
}
Activity -
An Activity is an android class. If we want to use an activity class, we must use extend Activity in your android project.
Example:
public class shape extends Activity{
public void circle()
{
int A,B,radias;
}
}
File is a file on the filesystem. Class is a Java class. Activity is a specific java class commonly used in Android.
1) Class is Blueprint of object and you will create as many object you want from same class. You can create new object by “new” keyword. In example below “ArrayList” is class and “obj” is object.
ArrayList<String> obj=new ArrayList<String>
2) Activity :- Every program has some starting point. In android Activity is starting point of any application you made. It is basically a GUI of the app. In android app every activity have to inherent directly or indirectly from Activity Class which is predefined in the android system. So activity is also a class but a special one. So you can say that “Every activity is a class but every class is not Activity”.
3) File :- file is used to store data so you can reuse again when you app start.
An activity is actually a class (click) and if you want to make your own activity you choose this one as parent class.
And the source code of classes is defined in files, actually every class should be described in its own file.
That's some basic knowledge of object oriented programming - you might want to have a look here to find more information