Is there a way to bind to the ExceptionManagerModule similar to RCTExceptionManager? - android

In React Native you can bind a delegate to RCTExceptionManager on the iOS side to catch errors and handle them yourself (like this).
However, looking into Android the ExceptionManagerModule only takes in a DevSupportManager and seems to be instantiated inside the CoreModulesPackage. I tried instantiating the module a second time using a DevSupportManager subclass, but you cannot instantiate the same module more than once.
Is there any way to add a custom error handler on the native Android side?

It is possible to override core modules if the getName() return the same as the core module and if canOverrideExistingModule() returns true.
You can see an example of that on the library that I did to support Crashlytics https://github.com/aoriani/ReactNative-StackTracer/blob/master/rnstacktracer/src/main/java/com/github/aoriani/rnstacktracer/StackTraceManagerModule.java

Related

Is it possible to load a native library in a Jetpack Compose #preview function?

I've begun implementing a feature on my application with Jetpack Compose.
The application uses Swig to generate code that allows me to interact with a C++ layer from Kotlin/Java. In order to use the C++ layer, I load the library like this in the app's main activity:
static {
System.loadLibrary("{swig library name}");
}
When I attempt to use Jetpack Compose previews, I get the following render problem:
java.lang.UnsatisfiedLinkError: 'void {package containing swig's generated code}.swigWrappersJNI.swig_module_init()'
The function does use a class generated by Swig, so I'm assuming that's why this is happening.
I tried getting around this by adding the following to the preview function:
System.loadLibrary("{swig library name}")
but that just results in the following render problem:
java.lang.UnsatisfiedLinkError: no {swig library name} in java.library.path:
Does anybody know how/if I can properly link the library so that I can use Compose's preview feature as I work on this app?
Edit:
For a little more info, I've tried the following methods for loading my library:
#Composable
#Preview
fun ___Preview() {
System.loadLibrary("{swig library name}");
// Actual preview code here
}
object LibraryLoader {
fun load() {
System.loadLibrary("{swig library name}")
}
}
#Composable
#Preview
fun ___Preview() {
LibraryLoader.load()
// Actual preview code here
}
(This one was a bit of a shot in the dark. I thought the key difference could've been loading it within a static block, but this had no effect.)
The previews can be successfully deployed to a device/emulator.
I think the issue here is that the library lives in lib/{abi}/{swig library name}.so, so it exists when running on a device/emulator (with a supported ABI) but not for Compose previews.
What I've done to work around it is create a copy of my model classes that don't use any of Swig's generated code so I can take advantage of previews when creating the layout, then replacing them with the actual model classes for testing functionality. Definitely not a solution I'm particularly happy with but it may be the only way to use Compose previews in this type of situation.

Error when binding an .AAR in Xamarin.Android

i'm trying to bind the core library: https://github.com/gabrielemariotti/cardslib
I downloaded the .AAR file from Maven Central and did everything in this tutorial Binding an .AAR, but when i'm building the Binding Library i'm getting this errors:
'CardExpandableListAdapter' does not implement inherited abstract member 'BaseExpandableListAdapter.GetGroup(int)'
Inconsistent accessibility: return type 'CardWithList.LinearListAdapter' is less accessible than method 'CardWithList.GetLinearListAdapter()'
Inconsistent accessibility: parameter type 'CardWithList.LinearListAdapter' is less accessible than method 'CardWithList.SetLinearListAdapter(CardWithList.LinearListAdapter)'
Inconsistent accessibility: property type 'CardWithList.LinearListAdapter' is less accessible than property 'LinearListView.Adapter'
'CardView' does not implement interface member 'ICardViewWrapper.SetExpanded(bool)'
'CardView' does not implement interface member 'ICardViewWrapper.SetForceReplaceInnerLayout(bool)'
'CardView' does not implement interface member 'ICardViewWrapper.SetLongClickable(bool)'
'CardView' does not implement interface member 'ICardViewWrapper.SetOnExpandListAnimatorListener(ICardViewWrapperOnExpandListAnimatorListener)'
'CardView' does not implement interface member 'ICardViewWrapper.SetRecycle(bool)'
It looks like the binding generator has some bugs, is there any fix for this?
Thanks in advance.
From Xamarin's page,
When you are generating Java Bindings, there are several common error scenarios that you may run into.
Here is the link to possible errors you might run into and how to resolve them.
Link for how to fix "Interface does not implement method"
Inconsistent accessibility issue can be resolved by changing the visibility accordingly. Here is an example of how to modify visibility
<attr path="/api/package[#name='com.somepackage']/class[#name='SomeClass']" name="visibility">public</attr>

How does Dagger 2 make testing easier on Android?

One of the best advantages of using DI is it makes testing a lot easier (What is dependency injection? backs it too). Most of DI frameworks I've worked with on other programming languages (MEF on .NET, Typhoon on Obj-C/Swift, Laravel's IoC Container on PHP, and some others) allows the developer do register dependencies on a single entry point for each component, thus preventing the "creation" of dependency on the object itself.
After I read Dagger 2 documentation, it sounds great the whole "no reflection" business, but I fail to see how it makes testing easier as objects are still kind of creating their own dependencies.
For instance, in the CoffeMaker example:
public class CoffeeApp {
public static void main(String[] args) {
// THIS LINE
CoffeeShop coffeeShop = DaggerCoffeeShop.create();
coffeeShop.maker().brew();
}
}
Even though you're not explicitly calling new, you still have to create your dependency.
Now for a more detailed example, let's go to an Android Example.
If you open up DemoActivity class, you will notice the onCreate implementation goes like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Perform injection so that when this call returns all dependencies will be available for use.
((DemoApplication) getApplication()).component().inject(this);
}
You can clearly see there is no decoupling from the DI component, to the actual code. In summary, you'd need to mock/stub ((DemoApplication) getApplication()).component().inject(this); on a test case (if that's even possible).
Up to this point, I am aware Dagger 2 is pretty popular, so there is got to be something I am not seeing. So how does Dagger 2 makes testing classes easier? How would I mock, let's say a network service class that my Activity depends on? I would like the answer to be as simple as possible as I'm only interested in testing.
Dagger 2 doesn't make testing easier
...beyond encouraging you to inject dependencies in the first place, which naturally makes individual classes more testable.
The last I heard, the Dagger 2 team were still considering potential approaches to improving support for testing - though whatever discussions are going on, they don't seem to be very public.
So how do I test now?
You're correct to point out that classes which want to make explicit use of a Component have a dependency on it. So... inject that dependency! You'll have to inject the Component 'by hand', but that shouldn't be too much trouble.
The official way
Currently, the officially-recommended approach to swapping dependencies for testing is to create a test Component which extends your production one, then have that use custom modules where necessary. Something like this:
public class CoffeeApp {
public static CoffeeShop sCoffeeShop;
public static void main(String[] args) {
if (sCoffeeShop == null) {
sCoffeeShop = DaggerCoffeeShop.create();
}
coffeeShop.maker().brew();
}
}
// Then, in your test code you inject your test Component.
CoffeeApp.sCoffeeShop = DaggerTestCoffeeShop.create();
This approach works well for the things you always want to replace when you are running tests - e.g. Networking code where you want to run against a mock server instead, or IdlingResource implementations of things for running Espresso tests.
The unofficial way
Unfortunately, it the official way can involve a lot of boilerplate code - fine as a one-off, but a real pain if you only want to swap out a single dependency for one particular set of tests.
My favourite hack for this is to simply extend whichever Module has the dependency you want to replace, then override the #Provides method. Like so:
CoffeeApp.sCoffeeShop = DaggerCoffeeShop.builder()
.networkModule(new NetworkModule() {
// Do not add any #Provides or #Scope annotations here or you'll get an error from Dagger at compile time.
#Override
public RequestFactory provideRequestFactory() {
return new MockRequestFactory();
}
})
.build();
Check this gist for a full example.
"allows the developer do register dependencies on a single entry point for
each component" - analogues in Dagger 2 are the Modules and Components where you define the dependencies. The advantage is that you don't define the dependencies directly in your component thus decoupling it so later when writing unit tests you may switch the Dagger 2 component with a test one.
"it sounds great the whole "no reflection" business" - the "no reflection" thing is not the "big deal" about dagger. The "big deal" is the full dependency graph validation at compile time. Others DI frameworks don't have this feature and if you fail to define how some dependency is satisfied you will get an error late at runtime. If the error is located in some rarely used codepath your program may look like it is correct but it will fail at some point in the future.
"Even though you're not explicitly calling new, you still have to create your dependency." - well, you always have to somehow initiate dependency injection. Other DI may "hide"/automate this activity but at the end somewhere building of the graph is performed. For dagger 1&2 this is done at app start. For "normal" apps (as you shown in the example) in the main(), For android apps - in the Application class.
"You can clearly see there is no decoupling from the DI component, to the actual code" - Yes, you are 100% correct. That arises from the fact that you don't control directly the lifecycle of the activities, fragments and services in Android, i.e. the OS creates these objects for you and the OS is not aware that you are using DI. You need manually to inject your activities, fragments and services. At first this seem seems awkward but in real life the only problem is that sometimes you may forget to inject your activity in onCreate() and get NPE at runtime.

Instantiating multiple copies of a JNI library from Java

I have a JNI library that I've written to capture sensor (mostly accelerometer and gyro) data and do some feature detection algorithms on said data. The features detected are configurable via a few configuration files. When the features are detected, the JNI uses a callback to notify the java side of the application. All this works great.
Now I want to be able to have multiple instances of the same JNI library running simultaneously (so I can recognize features from multiple configuration files at once). To do this, I wrote a 'wrapper' class that implements the callbacks for the JNI library and takes care of all the initialization of the library as well. I was planning on simply instantiating this class and using each instance separately. What I've found is that while each wrapper instance is distinct, the library is reused across instances almost like it was statically declared. When I try to initialize the library from the second instance of the Wrapper class, I find it has already been initialized.
Here is a wrapper class similar to the code I've written:
public class JNIWrapper {
public native int initializeJNI(String configPath);
public native void endProcessing();
public native int getInstanceIdFromJNI();
public JNIWrapper(){
try {
System.loadLibrary("libjnicode.so");
}
catch (Exception e) {
Log.e("JNI", "WARNING: Could not load libjnicode.so: " + e.getMessage());
}
}
public int initialize(String configPath){
return initializeJNI(configPath);
}
public void stop(){
endProcessing();
}
public void callbackFromJNI(int output, int instanceId){
//notify the subscribed application(s) of the feature detection
//via message passing.
}
}
Does anyone know how I can instantiate multiple copies of a JNI library?
Thanks!
You can't do that. The dynamic linker will only load a given .so file into a process once.
Ideally you would modify the library to give it a light object-oriented style, allowing you to create instances and initialize those (rather than process-level static state) from your configuration files or calls. This isn't necessarily as complicated as it seems - basically put all your state in a struct and pass the pointer to it through all your calls. You'll have one marathon editor session resulting in a tired "paste" finger, and then some mistake cleanup. Fortunately once you remove the static variables you'll get compile errors on all remaining attempts to use them.
A very hacky alternative might be to declare some remote-process services in your AndroidManifest.xml and load the library into each of those. Or, really breaking the android model (and at theoretical risk of random killing), load the library into multiple created-on-demand native executables.

Using a Library Project

I have a android application, say 'OldApp', which uses some native methods within itself, and I have to use it as a library for a new app, say 'NewApp'.
This is the first time i work with this request and i have a doubt:
the OldApp has some classes, and one of these is the JNI Wrapper and contains the native methods, for example:
public class LibOldApp {
private static native int method1();
....
....
}
Can i use all the classes/methods of the OldApp or only the wrapper, and native methods?
thank you..
Yes sure you can use.
just use your old app as library for your new project.

Categories

Resources