I am working on an Android application that will call a .so file created by a different Android NDK application.
I have created the following folder structure in my project and copied over the .so files as seen below:
|--app:
|--|--src:
|--|--|--main
|--|--|--|--jniLibs
|--|--|--|--|--armeabi
|--|--|--|--|--|--libmylib.so
|--|--|--|--|--x86
|--|--|--|--|--|--libmylib.so
I call this library through my application via the following code:
static {
System.loadLibrary("mylib");
}
I then call the method from this shared object via the following code:
String str = stringFromJNI();
This does not work as the program looks for mangled function name as follows:
com.example.androidlibcall.MainActivity.stringFromJNI() where my .so function will be using a different package name and hence a different function name is generated.
I am not really sure what I need to do to call the functions from the external library, I assume I can create my own library and utilize dlopen() to load the external library and make calls to it, but was wondering if there are the other methods to achieve this or not.
My ultimate goal is to be able to create applications that can call pre-existing libraries that are on the mobile device, but since I am new to NDK/Android I am not sure what is the best method for this and have not found good examples to work with.
A lot of the pre-existing similar questions seem to be dealing with older versions of Android Studio that don't seem applicable anymore.
I am using the latest version of Android Studio (3.1.2) with Gradle 4.4 on Windows 7 machine.
Please advise.
Thanks!
Generally speaking, it's not a good idea to have native methods in application's MainActivity, but this should not worry us now that we are forging a workaround.
Assume that your new project has com.example.other.MainActivity.java, and you want to call the native method com.example.androidlibcall.MainActivity.stringFromJNI() from com.example.other.MainActivity.onCreate(). To do this, create a new Java class in your other app:
package com.example.androidlibcall;
public class MainActivity {
public static native String stringFromJNI();
}
and in your existing MainActivity class,
package com.example.other;
import static com.example.androidlibcall.MainActivity.stringFromJNI;
class MainActivity {
static {
System.loadLibrary("mylib");
}
}
public class MainActivity extends Activity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
…
String qq = stringFromJNI();
…
}
}
You see that com/example/androidlibcall/MainActivity.java contains no logic, no data, no code. It is only a wrapper that lets us easily use libmylib.so without rebuilding it.
Related
I am writing an example android app to demonstrate the use of our (university research) C/C++ library.
I know that, using jni, we can call C functions from java.
However, I have not found a step by step set of instructions for how to do this within Android Studio Artic Fox.
I have seen the need to write jni compatible C wrapper functions, but have not found how to do this (correctly formed function signatures) or where to put them.
In addition, what do I need to change in the project setup to correctly build the project (using gradle) ?
Note that I have to use directly the pre-built .so file and the public header file which defines the set of public C functions for the library.
There are plenty of examples which give partial outdated information, but still nothing comprehensive - or have I missed something ?
I put together a quick guide below, but I want to clarify how it all fits together first.
In an Android application, you can bind native methods to specially-named functions that are loaded from a native library.
These specially-named functions receive pointers to a JNIEnv struct to interact with the embedding Java application.
The native library is typically built using CMake. Any external dependencies (such as your prebuilt library) need to be made visible to CMake in its CMakeLists.txt. The weapon of choice here are IMPORTED libraries, which are exactly what you think they are.
the steps
First, create an Android project with Kotlin as language.
Right click the app at the top of the tree and select "Add C++ to module" to generate the necessary build stuff.
Change your MainActivity.kt file to be:
class MainActivity : AppCompatActivity() {
external fun doit();
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val nativeThread = Thread {
doit()
}
nativeThread.start()
}
}
You will get a build error stating "cannot resolve corresponding JNI function". If you select the quick fix for that, Android Studio will generate a .cpp file with the appropriate JNI wrapper code inside it.
The generated function will look like:
extern "C"
JNIEXPORT void JNICALL
Java_com_example_myapplication_MainActivity_doit(JNIEnv *env, jobject thiz) {
// TODO: implement doit()
}
and above that will be instructions on how to load the native library from Kotlin. Copy that code to your MainActivity.kt.
Edit that .cpp file to do whatever you need to do with your native library (eg #include some files and call some functions).
Finally, edit app/src/main/cpp/CMakeLists.txt to point to your headers and precompiled library.
I am working on an automation project based on Appium-cucumber-Java, which will be growing over time.
Currently, I have Step definitions Given,When, Then in one file for iOS & another file for Android.
Both of these, files extend from a common basetest class.
I initialize required Page Objects using new keyword in both of these files.
Now, I would like to modularise it little bit & create a CommonStepDefs file. But I am starting to get nullpointer exception.
Can you please suggest with any method similar to this or sample example to build this
Thanks in advance.
public class AndroidTestsStepDefs_usingFactory extends BaseTestClass {
AndroidChooseCountryPage androidChooseCountryPage;
AndroidCountrySelectionPage androidCountrySelectionPage;
OrderPrints orderPrints;
AndroidHomePage androidHomePage;
TourPage tourPage;
public AndroidTestsStepDefs_usingFactory() throws IOException, AWTException {
}
#Given("^the app has been installed$")
public void the_app_has_been_installed() throws Throwable {
initializeDriver("android");
super.setCoreAppType("Android");
}
You are interested in sharing state between your step definitions files.
The idiomatic to share state in Java is to create a common object that is shared between all steps using dependency injection.
If your project uses a dependency injection framework, use the same for sharing state between the step definition classes. Cucumber-JVM supports many different dependency injection frameworks. Yours is probably supported.
If you don't use a dependency injection, I suggest using PicoContainer.
I have written two blog post on the topic. Sharing state using
PicoContainer:
http://www.thinkcode.se/blog/2017/04/01/sharing-state-between-steps-in-cucumberjvm-using-picocontainer
Spring:
http://www.thinkcode.se/blog/2017/06/24/sharing-state-between-steps-in-cucumberjvm-using-spring
I have created a Visual Studio extension that provider syntax highlighting by implementing IClassifierProvider. I would like to add additional features such as support for the standard Edit.CommentSelection and Edit.FormatDocument commands, but I have no idea how to do that. All the documentation I can find is about adding new commands, but the commands I want to handle already exist.
How can I handle these commands?
I considering the specific Comment Selection and Uncomment Selection commands you refer to as special cases, because I'm working on a Commenter Service specifically intended to support these two actions. The service is being developed on GitHub and will be released via NuGet when it is ready. I'll start with a description of this service, and follow with some general information about implementing support for specific commands, including the Format Document command.
I would like to release the library and its dependencies this week, but the restriction that the Commenter Interfaces assembly be an immutable assembly demands more testing than is generally given to a library prior to its initial release. Fortunately the only thing in this particular assembly is two interfaces is the Tvl.VisualStudio.Text.Commenter.Interfaces namespace.
Using the Commenter Service
Source: Commenter Service (Tunnel Vision Labs' Base Extensions Library for Visual Studio)
This services allows extension developers to easily support the Comment and Uncomment commands for new languages in Visual Studio.
Providing a Standard Commenter
The easiest way to provide commenting features is to use the standard Commenter implementation of the ICommenter interface. The following steps show how to create an instance of Commenter and provide it to the Commenter Service by exporting an instance of ICommenterProvider.
Create a new class derived from ICommenterProvider. This class is exported using the MEF ExportAttribute for one or more specific content types using the ContentTypeAttribute. The commenter in the example supports C++-style line and block comments, for the SimpleC content type.
[Export(typeof(ICommenterProvider))]
[ContentType("SimpleC")]
public sealed class SimpleCCommenterProvider : ICommenterProvider
{
public ICommenter GetCommenter(ITextView textView)
{
// TODO: provide a commenter
throw new NotImplementedException();
}
}
Define the comment format(s) the commenter will support.
private static readonly LineCommentFormat LineCommentFormat =
new LineCommentFormat("//");
private static readonly BlockCommentFormat BlockCommentFormat =
new BlockCommentFormat("/*", "*/");
Implement the GetCommenter(ITextView) method by returning an instance of Commenter. The ITextUndoHistoryRegistry service is imported in order for Commenter to correctly support the Undo and Redo commands. The following code is the complete implementation of ICommenterProvider required to support the Comment and Uncomment commands for a simple language.
[Export(typeof(ICommenterProvider))]
[ContentType("SimpleC")]
public sealed class SimpleCCommenterProvider : ICommenterProvider
{
private static readonly LineCommentFormat LineCommentFormat =
new LineCommentFormat("//");
private static readonly BlockCommentFormat BlockCommentFormat =
new BlockCommentFormat("/*", "*/");
[Import]
private ITextUndoHistoryRegistry TextUndoHistoryRegistry
{
get;
set;
}
public ICommenter GetCommenter(ITextView textView)
{
Func<Commenter> factory =
() => new Commenter(textView, TextUndoHistoryRegistry, LineCommentFormat, BlockCommentFormat);
return textView.Properties.GetOrCreateSingletonProperty<Commenter>(factory);
}
}
Command Handling in Visual Studio
The following are general steps for handling commands in Visual Studio. Keep in mind that the implementation details are quite complicated; I've created some abstract base classes to simplify specific implementations. After this overview, I will point to both those and a concrete example of their use for you to reference.
Create a class which implements IOleCommandTarget. The QueryStatus method should check for the specific commands handled by your command target and return the appropriate status flags. The Exec method should be implemented to execute the commands.
Register the command target with a specific text view by calling IVsTextView.AddCommandFilter. If you are working with an MEF-based extension, you can obtain the IVsTextView by either exporting an instance of IVsTextViewCreationListener, or by importing the IVsEditorAdaptersFactoryService component and using the GetViewAdapter method to obtain an IVsTextView from an instance of ITextView.
Here are some specific implementations of the interfaces described here:
CommandFilter: This class implements the basic requirements for IOleCommandTarget
TextViewCommandFilter: This class implements additional functionality to simplify the attachment of a command filter to a text view
CommenterFilter: This class is a concrete implementation of a command filter used by the Commenter Service implementation to handle the Comment Selection and Uncomment Selection commands
I want to use one native library for a plurality of applications. Library has compiled through android build system and now located in /system/lib/. It can be loaded in application through System.LoadLibrary("libexample"). But method in library which should be declared like
JNIEXPORT jstring JNICALL Java_application1_MainActivity_method1
turning out unusable because library should be used by several applications. And of course this several applications have their own unique names. So I tried to named my method just like
JNIEXPORT jstring JNICALL method1
and call
public native String method1(String string);
But of course my application trying to find it as Java_application1_MainActivity_method1
How to call this method or how it should be named?
Updated:
I tried to use this(see post with green tick) tutorial to complete my project. I wrote
a library for using native method:
package com.example.mylib;
import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;
public class MyClass extends Activity {
public native static String MyMethod(String string);
static {
System.loadLibrary("nativeLibrary");
}
}
Then I trying to use it in my application:
// Some no interesting inclusions
import com.example.mylib.MyClass;
public class MainActivity extends ListActivity {
// Some no interesting code.
MyMethod(file.getAbsolutePath())
//Some no interesting code
}
And it working as I need! But now I confused that my import com.example.mylib.MyClass;
"never used" in eclipse. And if I will create image with this "Is Library" project the latest will no resolving. Any idea?
Yes, you can use the same JNI signature in many applications. Your class may not belong to the default package of the application, as defined in AndroidManifest.xml. So what?
Example:
Start with HelloJni sample from NDK (in Eclipse, use Import -> Android -> existing Android Code, and point to the ${android-ndk-root}/samples/hello-jni).
Build it and run on device or emulator.
Open a new Android Application project, call it TestCrossJni.
The package name for our app will be: test.cross.jni - no relation to com.example.hellojni!
Choose "Create Activity" -> create Blank Activity.
Add new Java class to this project (src/com/example/hellojni/HelloJni.java):
package com.example.hellojni;
public class HelloJni
{
public static String gets() {
return stringFromJNI();
}
/* A native method that is implemented by the
* 'hello-jni' native library, which is packaged
* with this application.
*/
private native String stringFromJNI();
/* this is used to load the 'hello-jni' library on application
* startup. The library has already been unpacked into
* /data/data/com.example.hellojni/lib/libhello-jni.so at
* installation time by the package manager.
*/
static {
System.load("/data/data/com.example.hellojni/lib/libhello-jni.so");
}
}
Edit res/layout/activity_main.xml: replace
line 12 android:text="#string/hello_world" />
with android:id="#+id/hello_world" />
In src/test/cross/jni/MainActivity.java, add the following after
line 12 setContentView(R.layout.activity_main);
((android.widget.TextView)findViewById(R.id.hello_world)).setText(com.example.hellojni.HelloJni.gets());
Profit!
I have a need to load one DLL (Data) using one interface (IDataSender) and another DLL (Message) using another interface (IMessageSender). The code below is generating an error that the DLL being loaded doesn’t support the interface from the other DLL. Looks like each DLL much support all interfaces used by MEF.
Any idea how to load DLLs using different interfaces? I tried using [ImportMany] but that seems to load multiple DLLs using the same interface. Can MEF support multiple Interfaces?
[Import(typeof(IDataSender))]
public IDataSender DataSender;
[Import(typeof(IMessageSender))]
public IMessageSender MessageSender;
catalog_data = new AssemblyCatalog(#".\ABC.Data.dll");
container_data = new CompositionContainer(catalog_data);
container_data.ComposeParts(this);
catalog_message = new AssemblyCatalog(#".\ABC.Message.dll");
container_message = new CompositionContainer(catalog_message);
container_message.ComposeParts(this);
// DLL 1
namespace ABC.Data
{
[Export(typeof(IDataSender))]
public class DataClass : IDataSender
{
}
}
// DLL 2
namespace ABC.Message
{
[Export(typeof(IMessageSender))]
public class MessageClass : IMessageSender
{
}
}
Thank you for any help offered. I am new to MEF and can't figure out how to get that working.
Kamen
You don't need two containers to do this. One is enough. To do so, you need to use an AggregateCatalog that holds both AssemblyCatalogs.
catalog_data = new AssemblyCatalog(#".\ABC.Data.dll");
catalog_message = new AssemblyCatalog(#".\ABC.Message.dll");
container = new CompositionContainer(new AggregateCatalog(catalog_data, catalog_message);
container.ComposeParts(this);
The problem with your code was that none of the two containers, contained both parts needed to satisfy the imports. Each one contained one of the necessary parts. With the AggregateCatalog you can add multiple catalogs to a container, which is what you actually need. In almost any case, a single container is enough.