I am diving into the ndk stuff and I've managed to make a simple library and call it from my activity.
In order to test it, in my Activity I have used:
static {
System.loadLibrary("my-lib");
}
public native static String callNativeFunction(Context context);
This means that calling MyActivity.callNativeFunction(context) does return the String value from my Cpp function.
I have 2 questions:
The loadLibrary is made in my main activity, however I want to be able for instance, to call the callNativeFunction function from an IntentService when the activity may be closed or from other places of my app. How can I properly load the library and have it available from all places of the app?
Since this is a simple function that I'll use in my project, is there anything else specific to do on release? From https://developer.android.com/studio/projects/add-native-code.html it seems that Gradle builds for all supported abis and adds them into the apk.
You need to load the library once. This can also be done in your Application class. Once the library is loaded, you can call the defined native methods from the appropriate classes defined in your cpp class
Application class :
public class SRApplication extends Application {
static {
System.loadLibrary("my-lib");
}
#Override
public void onCreate() {
super.onCreate();
}
}
CPP :
extern "C"
jstring
Java_com_example_service_ExampleService_getNativeString (
JNIEnv* env,
jobject /* this */) {
return env->NewStringUTF("yo");
}
Your Service :
package com.example.service.ExampleService;
public class ExampleService extends Service {
#Nullable
#Override
public IBinder onBind(Intent intent) {
String nativeString = getNativeString();
return null;
}
public native String getNativeString();
}
If you don't have specific code for different cpu variants, you don't need to explicitly handle anything, the default
externalNativeBuild {
cmake {
cppFlags ""
}
}
is sufficient.
Related
I am not sure of the terminology for what I'm looking to do, so sorry in advance!
I've found a FilePicker plugin for Xamarin.Forms (https://github.com/Studyxnet/FilePicker-Plugin-for-Xamarin-and-Windows) that implements device-specific functionality for selecting files via the CrossFilePicker class.
The way to use leverage this functionality would be something like
CrossFilePicker.Current.OpenFile("Filename.txt");
The most important part of this for me is that CrossFilePicker.Current is static and can be accessible from anywhere in the shared layer of my Xamarin.Forms app.
I need to implement a class with the same characteristics. I want to leverage device Accessibility functionality (i.e. determining if a screen reader is enabled) and I need to be able to do so with a static class.
My eventual plan is to then wrap this static class so that I can use it for unit tests too.
I don't want to import device libraries into my shared project.
TLDR: I need a static class that implements device-specific functionality.
Any help would be greatly appreciated! Thank you :)
EDIT:
Here are the files I have currently implemented in my project
IAccessibilityService Located in the shared .NET project
namespace Bitspace.Services
{
public interface IAccessibilityService
{
public bool IsScreenReaderEnabled();
public void Announcement(string message);
public void NavigationAnnouncement(string message);
}
}
DeviceAccessibility.cs Located in the shared .NET project
using System;
namespace Bitspace.Services
{
public class DeviceAccessibility
{
private static Lazy<IAccessibilityService> Implementation = new Lazy<IAccessibilityService>(() => CreateAccessibilityService(), System.Threading.LazyThreadSafetyMode.PublicationOnly);
public static IAccessibilityService Current
{
get
{
var curr = Implementation.Value;
if (curr == null)
{
throw new Exception();
}
return curr;
}
}
private static IAccessibilityService CreateAccessibilityService()
{
return new DeviceAccessibilityImplementation();
}
}
}
DeviceAccessibilityImplementation.cs Located in the Android project
using Android.Runtime;
namespace Bitspace.Services
{
[Preserve (AllMembers = true)]
public class DeviceAccessibilityImplementation : IAccessibilityService
{
public bool IsScreenReaderEnabled()
{
return true;
}
public void Announcement(string message)
{
}
public void NavigationAnnouncement(string message)
{
}
}
}
If I try to build the project, I get an error on the return new DeviceAccessibilityImplementation(); line in DeviceAccessibility.cs that says DeviceAccessibility.cs(25, 24): [CS0246] The type or namespace name 'DeviceAccessibilityImplementation' could not be found (are you missing a using directive or an assembly reference?)
However, CTRL Clicking on that line takes me to the DeviceAccessibilityImplementation.cs
As the title suggested, How can I call Java function from C++ if the function is from a different java activity class?
All of the sample and tutorials calls C++ function and java back and forth but the caller is the class and the JNIEnv and jobject are passed from java thru JNI. But what if the function that needed to be called is from a different java activity class? How to do this? passing the "this" of the activity did not work
Here is sample layout of classes
Activity class
public class MainActivity extends Activity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
JNIAdapter.launch();
}
private void DisplayLoginDialog()
{
//...
}
}
JNIAdapter.class
public class JNIAdapter {
static {
System.loadLibrary("jnisample-lib");
}
public static native void launch();
}
jnisample.cpp
extern "C"
JNIEXPORT void JNICALL
Java_com_JNIAdapter_launch(JNIEnv *env,jobject object)
{
jclass dataClass = env->FindClass("com/game/ramo/MainActivity");
jmethodID javaMethodRef = env->GetMethodID(dataClass, "DisplayLoginDialog", "()V");
env->CallVoidMethod(object, javaMethodRef);
}
In the above code, using the jobject, refers to the JNIAdapter class and not the Activity hence the DisplayLoginDialog() is not called.
How to do this?
Your small example (I understand that you reduced all details not relevant to the specific problem, that's very nice!) could run without native method. JNIAdaptor.launch() could be pure Java. So, to begin with, rewrite it in Java and make sure it works.
The issues could be that MainActivity.DisplayLoginDialog() may expect its parent activity to be in the foreground, or in some specific state. This is easier to fix in pure Java.
After that, the JNI code you wrote should run without problems.
Java compiler does not preserve parameter names for any interface, unless newer compiler option -parameter is used (I am not sure how to use it with android studio) - refer example below.
Since java compiler does not preserve parameters names, Android Studio "code -> implement methods" is not able to generate code with original parameter names.
The question is, how to implement a library module so that Android Studio Menu, Code->Implement Methods correctly generates code with all the original parameter names.
For example, following is a simple class and an interface. This class is in a separate aar module. When application uses this AAR, implements TablaListener and asks AndroidStudio to generate interface methods stubs, the parameter names are not preserved.
Please note that proguard is NOT used.
Any ideas?
public class TablaCore {
public interface TablaListener {
/**
* #param params
* #param data
* #return
*/
boolean TablaCore_onAction(String params, byte[] data);
}
private static TablaListener mListener = null;
public static void setListener(TablaListener myListener) {
mListener = myListener;
}
public TablaListener getListener() {
return mListener;
}
}
It is easy to demonstrate by compiling and decompiling above class. This is decompiled version
public class TablaCore
{
private static TablaListener mListener = null;
public static void setListener(TablaListener myListener)
{
mListener = myListener;
}
public TablaListener getListener()
{
return mListener;
}
public static abstract interface TablaListener
{
public abstract boolean TablaCore_onAction(String paramMessageParams, byte[] paramArrayOfByte);
}
}
You have to include Android SDK source code.
Go to: File > Settings... > Apperance & Behavior > System Settings > Android SDK
On the tab SDK Platforms select Show Package Details and find and select appropriate Sources for Android XX. Unfortunately, currently for Android API 27, there is no sources, but there is for Sources for Android 26. Apply changes - the download window should appear automatically.
After downloading and restarting implementing methods should use proper names for method parameters.
I have an Android app with an activity derived from NativeActivity like this:
public class MyNativeActivity extends android.app.NativeActivity
{
public native void TellNativeSide(int info);
static {
System.loadLibrary("MyNatAct"); // <--- is this necessary?
}
public int OtherMethods(...) ...
}
On the C/C++ side, I have
extern "C" void
Java_mycom_nativity_MyNativeActivity_TellNativeSide(JNIEnv *env,
jobjectactivityobj, jint info)
{
... do something
} // java native TellNativeSide() method //
extern "C" jint JNI_OnLoad(JavaVM *vm, void *)
{
LOGI("***JNI_OnLoad called...");
}
The libMyNatAct.so library is loaded automatically by the NativeActivity class and indeed android_main() and everything runs correctly with or without the system.loadLibrary() line. However, JNI_OnLoad() would never be called and the TellNativeSide() method is also not available on the Java side unless the
system.loadLibrary("MyNatAct");
call is there in the static class init block.
So it seems that the native .so has to be loaded twice. Once in the init block to make available all the native methods and get JNI_OnLoad() called, and another time by the NativeActivity class but not through system.loadLibrary()?
Is this the correct behaviour?
That's right. You must explicitly call system.loadLibrary() to have the native Java methods bound to exported functions of the .so file
I'm currently trying to develop an application under Android using Mono.
I'd like to add support for plugins to my application so additional features could be brought to it.
I was able to load simple .dll at runtime in my program, however whenever I try creating a dll implementing both my interface and a custom activity, an exception of type Java.Lang.NoClassDefFoundError is thrown.
There is the class inside the dll code:
[Activity (Label = "Vestiaire")]
public class Vestiaire : Activity, IModule
{
public string Name { get; set; }
public string Version { get; set; }
void OnClickVestiaireButton(object sender, System.EventArgs e)
{
;
}
public void InitVestiaireModule()
{
Run();
}
public Type LaunchActivity ()
{
return typeof(Vestiaire);
}
public void Init()
{
Name = "Vestiaire Module";
Version = "0.1";
}
public void Run()
{
}
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
}
}
The line responsible for the exception: (from the program core)
LoadedPlugin.Add((IModule)(Activator.CreateInstance(Plugin)));
Things I'm actually wonderring are:
- Is it possible to actually achieve what i'm trying to ?
If yes, help would be apreciated on that problem :P
Otherwise what would be the best alternative ?
Global point is to be able to load a custom menu at runtime loaded from a dll.
Thanks.
i think the key to your problem is that the Activity needs to be registered in you Manifest.xml file.
For Activities in you main app, MonoDroid does this for you - but I don't think this will work for your plugin.
Things you could try are:
putting the Activity in the Manifest yourself (MonoDroid does seem very capable at merging these files)
if that doesn't work, then you could try using a Fragment instead - and loading the Fragment into a custom FragmentActivity in your main app.