Use own imports for imported project in Java - android

after many hours of work, I got jaxb working with android.
I created many own jar-files with own prefixes (instead of javax.xml.bind.* I called it rebuild.javax.xml.bind.*).
It works fine for classes in my project, but I have to make it work for classes in an other imported (Java BuildPath ->Projects ) project, but every class of the imported project references to javax.xml. and so it crashes my app if I run it(because in android there is no javax.xml.bind).
I have no write-access to the other project, how can I make it work?
If I copy every class in the second project and chage the imports von javax to rebuild.javax, it works fine, but I can't reimport it every time something changed.
Sry for my bad english, I hope you guys understand me :)

Maybe you could use a custom ClassLoader that replaces occurences of javax.xml.bind.* by rebuild.javax.xml.bind.* just before loading its classes.
Something like this :
public class JaxbClassLoader extends ClassLoader {
[...]
#Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
if(name.startsWith("javax.xml.bind"))
name = "rebuild." + name;
super.loadClass(name);
}
}
Maybe Class.findClass should be overridden too, but i'm not sure.
Then, you create a custom Application class and replace its ClassLoader like this :
private ClassLoader loader;
#Override
public void onCreate() {
Thread.currentThread().setContextClassLoader(getClassLoader());
}
#Override
public ClassLoader getClassLoader() {
if(loader == null)
loader = new JaxbClassLoader(super.getClassLoader());
return loader;
}
Don't forget to declare your Application in the AndroidManifest.
Setting the context class loader is additional, but may be of use.
Overriding getClassLoader() will cause the returned loader to be used in Activities created inside this app context.
Under some specific conditions, you might have to explicitly set the CCL once again, for example, upon creation of new Thread. A ThreadLocal might be of help in this case.

Related

Dynamic Feature with appbundle and PROGUARD not working

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.

How Do I Set a Breakpoint for Object Instantiation, with No Constructor?

I am trying to make sense of a class from a library. The class has no constructor, and I think it is being instantiated via reflection. It is a confusing library, and I want to figure out what is creating instances of this class... but I cannot figure out where to put a breakpoint, since there is no constructor.
I have tried the following, and Android Studio 3.4.1 blows right past them:
Line breakpoints on fields in the class that have initializers
Field watchpoints, set for both field access and field modification, for fields that have initializers
A breakpoint on the class declaration (class Foo)
Breakpoints in methods work, and field watchpoints work when the field is accessed later on (but not when it is initialized). So the debugger is working with this class in general.
I cannot readily recompile the library to add a constructor, though I do have source code (not just decompiled bytecode).
Is there another spot that I can put a breakpoint that will show me the stack trace of instantiation of this class?
From the InitelliJ IDEA documentation (on which AS is based):
If you want to set a breakpoint in the default class constructor, set it on the first line of this class, since the default constructor is mapped to it.
https://www.jetbrains.com/help/idea/using-breakpoints.html
Simple test-case to determine breakpoint position:
public class ReflectionTest {
static int test = 1;
public static void main(String[] args) throws Exception {
System.out.println(ReflectionTest.class.getDeclaredConstructor().newInstance());
}
}
Placing the breakpoint on public class ReflectionTest seems to trigger for me.

How to configure Android library from an app that imports it?

I'm kinda new to Android development so my question might be weird or not even possible. I wouldn't know!
Anyway, I'm building multiple apps that will have a lot of shared elements, so I decided to build a library with those components and use it in all of the apps, rather than stupid copying and pasting code.
For example, the library handles the welcome screen and login/signup flow activities, among other things. So here are the problems this approach might cause:
While the behavior is the same across the apps, but the logo that I show at the welcome screen is different. Right now I populate it with an image resource from the library resources (R class) which will be the same for all apps and is obviously not correct.
The login/signup process is based on Firebase, which will require the app to have a key to be able to use them. Right now I also populate it with a dummy string resource from the library resources.
So my question really boils down to 3 parts:
Is there anyway I could pass this info from the app to the library? can I somehow modify the R class of the library? Or can I use the app's R class from the library? I can also call this part of the library as a function passing the parameters I need. But the first solution looks maybe more clean to me?
Whatever the answer to Q1 is. Where would I do this and how? The library has the welcome activity itself which is supposed to be the first activity in the app. How and where do I do this once the app starts and before the first activity starts?
If what I'm doing is wrong or impossible, is there any other way to achieve it?
Is there anyway I could pass this info from the app to the library?
can I somehow modify the R class of the library? Or can I use the
app's R class from the library? I can also call this part of the
library as a function passing the parameters I need. But the first
solution looks maybe more clean to me?
You don't need to modify the R class because you can override the resource file by creating a file with the same name. But it's not a clean solution because you constantly need to ensure your project and library resources name are the same.
Whatever the answer to Q1 is. Where would I do this and how? The
library has the welcome activity itself which is supposed to be the
first activity in the app. How and where do I do this once the app
starts and before the first activity starts?
Instead of overriding the resources name, you're better to modify your library to receive a configuration as a contract to use the library. Here the sample:
First, create the class for holding the configuration:
public class Configuration {
private int welcomeImageDrawableId;
private int logoDrawableId;
// constructor
public Configuration(int welcomeImageDrawableId, int logoDrawableId) {
this.welcomeImageDrawableId = welcomeImageDrawableId;
this.logoDrawableId = logoDrawableId;
}
// setter and getter.
public int getLogoDrawableId() {
return logoDrawableId;
}
}
Second, use the configuration class for the library by creating a Singleton class which will be used internally by the library:
public class MyLibrary {
private static MyLibrary myLibrary;
private Configuration configuration;
private MyLibrary(){}
private MyLibrary(Configuration configuration) {
this.configuration = configuration;
}
public static MyLibrary getInstance() {
if(myLibrary == null) {
throw new RuntimeException("Need call createInstanceWith method first!!");
}
return myLibrary;
}
public static MyLibrary createInstanceWith(Configuration configuration) {
if(myLibrary == null) {
synchronized(MyLibrary.class) {
if (myLibrary == null) {
myLibrary = new MyLibrary(configuration);
}
}
}
return test;
}
public Configuration getConfiguration() {
return configuration;
}
}
Third, use the configuration class in your library via the singleton class. something like this:
// assume imvLogo is an existing ImageView
Configuration configuration = MyLibrary.getInstance().getConfiguration();
imvLogo.setImageResource(configuration.getLogoDrawableId());
Last, register the contract when the library is used with:
Configuration configuration = new Configuration(R.drawable.welcome, R.drawable.logo);
MyLibrary.createInstanceWith(configuration);
Note: all the code isn't tested yet, error is to be expected.
Apart from the solution above, I also found another way to achieve this whole thing without having to initialize libraries and whatnot.
I think the correct way to do this is to use productFlavors in the library. This allows the library to share the one main set of source code, one main set of resources, then an extra set of resource per app/flavors. This is very sufficient for my purposes.
For more info about build variants and flavors:
https://developer.android.com/studio/build/build-variants

Android Testing of an Activity not run by JUnit

I'm trying to do Android unit testing for the first tme and I encounter a problem I can't seem to solve : only one of my test classes is ran, I'm not able to run test classes related to Activity testing, and even asserting true=false in them doesn't display an error.
My testing project is composed of three source files :
A test file for a class in my project (subclass of AndroidTestCase)
A test file for my first activity, LoginActivity (subclass of ActivityInstrumentationTestCase2)
A test file for another activity, EditUserActivity (once again subclass of ActivityInstrumentationTestCase2)
I used the following tutorial : http://forum.frandroid.com/topic/13831-traduc-de-tuto-les-tests-unitaires/ (in French but the code is in English)
And first read the following answer on StackOverflow : Trying to run Android JUnit tests in Eclipse fails? however it doesn't seems to be my problem
The code for the last test class is the following :
package com.imci.ica.test;
import com.imci.ica.EditUserActivity;
import android.test.ActivityInstrumentationTestCase2;
public class EditUserActivityTest extends
ActivityInstrumentationTestCase2<EditUserActivity> {
EditUserActivity mActivity;
public EditUserActivityTest() {
super("com.imci.ica", EditUserActivity.class);
}
#Override
protected void setUp() throws Exception {
super.setUp();
mActivity = this.getActivity();
}
public void testTest() {
assertEquals(true, false);
}
}
Thanks in advance for your help!
I don't understand why, but I had to move the Eclipse project's files, so I closed the project, moved them and imported the project back, and now all the tests are checked, so my problem's fixed. If it can help somebody...
For me, I found that one testing class was crashing. I forgot to added non-argument constructor. Fixing that, all tests are run.

Retrieve android:versionName from Library Project

I have created a Library Project which I import into another project.
In that Library Project at some point I retrieve it's android:versionName
To do that you need to supply the package name.
The problem arises when that code is executed when the Library Project is included within another project, then it seems that that code throws an exception :
10-04 10:15:36.987: WARN/System.err(1407): getSoftwareVersion(), Caught Exception : android.content.pm.PackageManager$NameNotFoundException: mobilaria.android.LandenPlayerCodeBase.baseplayer
Thats the package name of the package of the Project Library... it seems it cannot find it even though the same code that is executing that call is part of the Library itself...
Does anyone have experienced something like this or has an idea on how to solve this ?
As far as I know android library project manifest is ignored at the moment, manifest is not merged into end application when you reference a library. Hence you cant extract any data from the library's manifest.
I just tried something similar.
I tried to add a method getLibraryVersion() to my custom Application class. So I would be able to call
MyLibrary.getLibraryVersion()
from within the code that included that library. But it seems that you can not access the String resources via getText() or getString() like this:
public class MyLibrary extends Application {
#Override
public void onCreate() {
super.onCreate();
// provide an instance for our static accessors
MyLibrary.instance = this;
}
private static void checkInstance() {
if (MyLibrary.instance == null) {
throw new IllegalStateException("Application not created yet!");
}
}
/**
* #return the library name
*/
public String getLibraryName() {
MyLibrary.checkInstance();
return MyLibrary.instance.getString(R.string.app_project_name).toString();
}
...
}
Because the onCreate() method seems not to be called, the instance is always null!
As this way was not working out, and as you saw you cannot access the version the way you tried, I just hard coded the version and the library name, into my custom application class like this:
public class MyLibrary extends Application {
/**
* #return the library name
*/
public String getLibraryName() {
return "org.yourCompany.android.lib.YourLibName";
}
/**
* #return the library version
*/
public String getLibraryVersion() {
return "1.0.0";
}
}
I know that this is kind of a dirty solution, and I would prefer a cleaner version of coding, with these Strings stored as String resources in strings.xml but I don't know any better way. So you just have to change the library name and version in your manifest or better the strings.xml AND in the Application class.
But how often do you change the library name or version?
Hope this can help somebody and save time!
PS: some of the above code is based on this:
http://blog.tomgibara.com/post/126377651/global-application-state-in-android

Categories

Resources