JNI Callback to Java using Interface - android-ndk

First of all, let me list the best result I could fetch.
jni call java method which take a custom java interface as parameter
This does not answer mine. Let me explain my problem. I want to make a call to NDK as follows.
(1)Java -> (2)CPP -> (3)C (new thread) -> (4)CPP -> (5)Java
Code is below.
(1) Java
public interface Callback<T> {
void success(T result);
}
private native void jniUploadAsync(String imagePath, Callback<String> callback);
jniUploadAsync(file.getAbsolutePath(), new Callback<String>() {
#Override
public void success(final String result) {
Log.v("MyClass: result:: ", result);
}
});
(2) CPP
static JavaVM *jvm;
void imageUploadCallback(char *json, void *completionCallback) {
JNIEnv *env;
jint rs = jvm->AttachCurrentThread(&env, NULL);//create JNIEnv from JavaVM
jclass cbClass = env->FindClass("org/winster/test/Callback");
jmethodID method = env->GetMethodID(cbClass, "success", "(Ljava/lang/String;)V");
env->CallVoidMethod(static_cast<jobject>(completionCallback), method, "abcd");
}
void Java_org_winster_test_MyClass_jniUploadAsync(JNIEnv * env, jobject obj, jstring imagePath, jobject completionCallback) {
jint rs = env->GetJavaVM(&jvm); //Cache JavaVM here
CallMyCMethod((char *)filePath, &imageUploadCallback, &completionCallback);
}
(3) C
CallMyCMethod() //please assume that it works. The reason I need void* as the type for completionCallback is because, in ObjC implementation I use this
(4) CPP
//Call comes back to imageUploadCallback()
(5) Java
//I expect this Log.v("MyClass: result:: ", result); to be executed
Please note that, this is not a basic question about how to call Java from C++. The 2 specific points I want to resolve is, how to call the "callback" and how to invoke a method in a Java Interface implementation. I have done this for Obj-C where it is straight forward.

(2)
First of, you need to store reference to JavaVM so you will be able to obtain JNIEnv later from other thread. Also you need to get new global reference from local variable got from parameter (don't forgot to delete it when it is no longer needed, or it will cause memory leak).
static JavaVM* jvm = 0;
void Java_org_winster_test_MyClass_jniUploadAsync(JNIEnv * env, jobject obj, jstring imagePath, jobject completionCallback) {
env->GetJavaVM(&jvm); //store jvm reference for later
jobject globalref = env->NewGlobalRef(completionCallback);
CallMyCMethod((char *)filePath, &imageUploadCallback, (void *)globalref);
}
(4)
When using generics, native side can't know what type they are of, so everywhere you are using T you should be using Object on the JNI/C/CPP part
you are starting new thread in C. If you are willing to fire callback from that thread, you need to connect it to the java virtual machine and detach it afterwards. From what i think you do, you use the callback object only once. In that case you also need to delete global ref to it.
void imageUploadCallback(char *json, void *completionCallback) {
JNIEnv* env;
jvm->AttachCurrentThread(&env, NULL);
jclass cbClass = env->FindClass("org/winster/test/Callback");
jmethodID method = env->GetMethodID(cbClass, "success", "(Ljava/lang/Object;)V");
jstring abcd = env->NewStringUTF("abcd");
env->CallVoidMethod(completionCallback, method, abcd);
env->DeleteGlobalRef(completionCallback);
jvm->DetachCurrentThread();
}

Related

The method setData(Bundle) in the type Message is not applicable for the arguments (void)

There are many ways to create threads, The most simplest way I know to create a thread is the following:
new Thread(new Runnable(){
public void run(){
//thread work here
}
});
I am looking for the shorthand version for creating a message being sent to a Thread Handler-class;
Bundle bundle = new Bundle();
bundle.putString("KEY", "DONE");
Message msg = (new Message());
msg.setData(bundle);
msg.obtain(handler.obtainMessage());
handler.sendMessage(msg);
I think the Shorthand would be something like the following:
new Message().setData(new Bundle().putString("KEY","DONE")).obtain(handler.obtainMessage());
but for '.setData' I get the error;
The method setData(Bundle) in the type Message is not applicable for
the arguments (void)
What are my options experts?
NOTE: This implementation is for Android OS.

C++ CLI Invoke issues

I have a MainForm class (as you'd expect, it is a form) that has a text box on it. I also have another class called 'Application_Server' That does a load of other stuff (not just form-background related, quite a lot of network based stuff etc.).
The Application_Server class runs in it's own thread, but needs to be able to update the controls on the form, for this question, we will stick with just the textbox.
The problem is that even though I am executing the command to set the text of the textBox control via 'Invoke' I am still getting the following exception during runtime:
Additional information: Cross-thread operation not valid: Control
'DebugTextBox' accessed from a thread other than the thread it was
created on.
What could be causing this? I am definitely invoking a delegate within MainForm.
Here are the relevant code segments (cut down for readability):
MainForm.h:
public ref class MainForm : public System::Windows::Forms::Form {
delegate void del_updateDebugText(String^ msg);
del_updateDebugText^ updateDebugText = gcnew del_updateDebugText(this, &MainForm::postDebugMessage);
private: void postDebugMessage(String^ message);
};
MainForm.cpp:
void EagleEye_Server::MainForm::postDebugMessage(String^ message)
{
Monitor::Enter(DebugTextBox);
if (this->DebugTextBox->InvokeRequired)
{
this->Invoke(updateDebugText, gcnew array<Object^> { message });
}
else
{
this->DebugTextBox->AppendText(message);
}
Monitor::Exit(DebugTextBox);
}
And finally, the code calling it:
void ServerAppManager::postDebugMessage(System::String^ message)
{
mainFormHandle->updateDebugText(message);
}
void ServerAppManager::applicationStep()
{
postDebugMessage("Starting\n");
// This is Run in seperate thread in MainForm.cpp
while (s_appState == ApplicationState::RUN)
{
postDebugMessage("Testing\n");
}
}
Thanks!
From background worker called bwSearch we do the call as following from the DoWork event handler:
private: System::Void bwSearch_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
//... logic
UpdateTxtOutput("Some message");
//... more logic
}
I have a RitchTextBox called txtOutput, also the windows form control containing this code is called frmMain, the UpdateTxtOutput is defined in three parts as follows:
delegate void UpdateTxtOutputDelegate(String^ text);
void UpdateTxtOutput(String^ text)
{
UpdateTxtOutputDelegate^ action = gcnew UpdateTxtOutputDelegate(this, &frmMain::Worker);
this->BeginInvoke(action, text);
}
void Worker(String^ text)
{
txtOutput->AppendText("\t" + text + "\n");
}
I managed to get it working by simplifying the method within the 'MainForm' class to:
void EagleEye_Server::MainForm::postDebugMessage(String^ message)
{
Monitor::Enter(DebugTextBox);
DebugTextBox->AppendText(message);
Monitor::Exit(DebugTextBox);
}
And then moving the 'Invoke' call to the method calling the delegate, not pretty but it works for now. I think the issue may have been caused by the form getting stuck inside an Invoke loop. I say this as I noticed that the form would lock up and stop responding after it hit the recursive Invoke statement.

boost locking mutex deadlocks

I have code that I am trying to guard using boost locking mechanism. The problem is that RecomputeStuff can not only be called from RemoveStuff, but it can also be called from another thread. My question is, using these boost locking mechanisms, what is the correct way to fix RecomputeStuff? The way it is now it deadlocks.
#include <boost/thread.hpp>
boost::shared_mutex values_mutex;
int globaldata;
class A
{
public:
void RecomputeStuff();
void RemoveStuff();
private:
std::vector<std::string> data;
};
//Note, RecomputeStuff just reads from std::vector<std::string> data, but it also writes to other global stuff that RemoveStuff also writes to.
void A::RecomputeStuff()
{
boost::upgrade_lock<boost::shared_mutex> lock(values_mutex);
boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(lock);
// this function reads std::vector<std::string> data
// but also modifies `globaldata` that RemoveStuff also modifies.
}
void A::RemoveStuff()
{
boost::upgrade_lock<boost::shared_mutex> lock(values_mutex);
boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(lock);
//here, remove stuff from std::vector<std::string> data
//...then call RecomputeStuff
RecomputeStuff();
// modify `globaldata`
}
A solution is to move the not locked code of the A::RecomputeStuff method to a separate one and call it from the A::RemoveStuff and A::RecomputeStuff. See the code below
boost::shared_mutex values_mutex;
int globaldata;
class A
{
private:
void RecomputeStuffUnsafe();
public:
void RecomputeStuff();
void RemoveStuff();
private:
std::vector<std::string> data;
};
void A::RecomputeStuffUnsafe()
{
// this function reads std::vector<std::string> data
// but also modifies `globaldata` that RemoveStuff also modifies.
}
//Note, RecomputeStuff just reads from std::vector<std::string> data, but it also writes to other global stuff that RemoveStuff also writes to.
void A::RecomputeStuff()
{
boost::upgrade_lock<boost::shared_mutex> lock(values_mutex);
boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(lock);
RecomputeStuffUnsafe();
}
void A::RemoveStuff()
{
boost::upgrade_lock<boost::shared_mutex> lock(values_mutex);
boost::upgrade_to_unique_lock<boost::shared_mutex> unique_lock(lock);
//here, remove stuff from std::vector<std::string> data
//...then call RecomputeStuff
RecomputeStuffUnsafe();
// modify `globaldata`
}
Edit #00:
Also upgrade_lock has a constructor which accepts the try_to_lock_t tag. It looks like what you are asking for.

MonoTouch Objective C Binding, Right Track?

I am trying to do this with the kiip library:
http://docs.xamarin.com/ios/advanced_topics/binding_objective-c_types
I am getting an error saying my binding project cannot be found, however, I did add a reference to it in the project.
Do I use the code like this:
public override void ViewDidLoad ()
{
var kp = new KiipMonoTouchBinding.IKPManager();
kp.InitWithKey("abc", "123");
}
Am I doing this correctly?
namespace KiipMonoTouchBinding
{
interface IKPManager
{
//kiip code
//KPManager* manager = [[KPManager alloc] initWithKey:#"0b56b49f621ad7f42fd85de7e461f9dd" secret:#"ac3abfdf5cb86ce0febba0c8afd2744e" testFrequency:100];
[Export("initWithKey:")]
void InitWithKey(string key, string secret);
//[[KPManager sharedManager] unlockAchievement:#"_achievement_id_"];
[Export ("unlockAchievement:")]
void UnlockAchievement(string achivementId);
//
//- (IBAction)saveLeaderboard {
// NSLog(#"save leaderboard");
// [[KPManager sharedManager] updateScore:100 onLeaderboard:leaderboard_id.text];
//}
//[[KPManager sharedManager] updateScore:_score_ onLeaderboard:#"_leaderboard_id_"];
[Export("updateScore:")]
void UpdateScore(int score, string leaderboardId);
//- manager:(KPManager*)manager didStartSession:(NSDictionary*)response {
[Export("didStartSession:response")]
void DidStartSession(NSDictionary response);
//updateLatitude:(double)37.7753 longitude:(double)-122.4189];
[Export("updateLatitude:_latitude, longitude")]
void UpdateLatitude(double latitude, double longitude);
[Export("updateUserInfo:info")]
void UpdateUserInfo(NSDictionary info);
// [[KPManager sharedManager] getActivePromos];
[Export("getActivePromos")]
void GetActivePromos();
// Update the user's location
// [manager updateLatitude:_latitude_ longitude:_longitude_];
// Update the User's information
// NSDictionary* info = [[[NSDictionary alloc] initWithObjectsAndKeys:
// _email_, #"email",
// _alias_, #"alias",
// nil]
// autorelease];
// [manager updateUserInfo:info];
}
Your binding has several problems.
Constructors must be declared as "IntPtr Constructor", so change the "void InitWithKey" to be:
[Export ("initWithKey:")]
IntPtr Constructor (string key);
The second problem is that the export you are using "initWithKey:" takes a single parameter (we know this because there is a single instance of the colon), so you might need to find out what the actual name of the constructor is, or use a single parameter (key), like I did in the sample.
Your binding for "DidStartSession" is wrong. Look at the signature which is "manager:didStartSession:" so it should be:
[Export ("manager:didStartSession:")]
void DidStartSession (KPManager manager, NSDictionary sessionREsponse);
Your UpdateLatitude is also wrong, again, the selector you added is incorrect, I can not guess what it is without looking at the code, but if this really gets two parameters (longitude and latitude), it would look like this (I am making the selector name up):
[Export ("updateLatitude:andLongitude:")]
void UpdateLocation (double latitude, double longitude)
UpdateUserInfo is also wrong, most likely it takes a single parameter (guessing again):
[Export ("updateUserInfo:")]
void UpdateUserInfo (NSDictionary info)
Notice that the "info" word, the name of the parameter is never part of the selector name.
The binding for getActivePromos also looks wrong, I suspect it should return a value, but you declared it as returning void.
There might be other problems as well.

Qt Webkit bridge ActiveQt string

See also for part of my issue: String passing from JavaScript to Qt/C++ object
I use this custom WebPage on my WebView (using setPage), the strings are passed in wrongly. When I disable calling setPage, the strings passed correctly.
I have a C++ object (that inherits from QWidget) that displays an activeX on a webpage (using ActiveQt and QWebView). The ActiveX object displays just fine. I've registered the custom type and used the Qt MetaType system to instantiate the HTML object.
If I want to update a property of that C++ object (not the ActiveX) and call from JavaScript, the string gets a wrong translation or so. If I pass in some string, I only get a string of 1 character on my C++ method. Viewing the memory tells me that the whole string is present (formattted as UTF-32).
If I now don't call the custom WebPage that loads the plugin for me (using setPage on QWebView), I don't get the ActiveX - which is expected - and the strings are passed correctly.
I don't quite understand why this doesn't work. Even with setPage called, I'm talking to the C++ object via JavaScript/QtWebKit bridge, not to the ActiveX component.
I'm on Windows 7, 64 bit, using Qt 4.7.4.
Some code (C++):
class Device : public QWidget
{
Q_OBJECT
public:
explicit Device(QWidget* parent = 0);
Device(const Device& other);
Device& operator=(const Device& other);
~Device();
QString name() const;
void setName(QString);
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
signals:
void nameChanged(QString);
private:
QAxWidget* ax;
};
Q_DECLARE_METATYPE(Device);
Device::Device(QWidget* parent/* = 0*/)
: QWidget(parent)
{
ax = new QAxWidget(this);
ax->setControl("{...}");
}
QString Device::name() const
{
return _name;
}
void Device::setName(QString name)
{
if (_name != name) {
_name = name;
emit nameChanged(name);
}
}
// Needed for ActiveQt objects to work
class MapPage : public QWebPage
{
Q_OBJECT
protected:
QObject* createPlugin(const QString& classId, const QUrl& url, const QStringList& paramNames, const QStringList& paramValues);
public:
MapPage(QObject* parent = 0);
};
// ============================================================================
// Needed for ActiveQt objects to work
MapPage::MapPage(QObject* parent/* = 0*/)
: QWebPage(parent)
{
qRegisterMetaType<Device>();
settings()->setAttribute(QWebSettings::PluginsEnabled, true);
}
QObject* MapPage::createPlugin(const QString& classId, const QUrl& url, const QStringList& paramNames, const QStringList& paramValues)
{
// Create widget using QUiLoader, which means widgets don't need to be registered with meta object system (only for gui objects!).
QUiLoader loader;
QObject* result = 0;//loader.createWidget(classId, view());
// If loading failed, use the Qt Metatype system; expect objects derived from QObject
if (!result) {
//result = static_cast<QWidget*>(QMetaType::construct(QMetaType::type(classId.toLatin1())));
int id = QMetaType::type("Device");
void* classPtr = QMetaType::construct(id);
result = static_cast<QWidget*>(classPtr);
}
return result;
}
and then on my window
qRegisterMetaType<Device>();
mapPage = new MapPage(this);
ui.webView->setPage(mapPage); // Needed for ActiveQt objects to work - makes strings broken
...
ui.webView->setHtml(src);
Narrowed it down to an issue probably related to JavaScript/HTML.
//device.name = 'test'; // works
var str = 'test';
previewCameo.view = str; // works
but when I get the name via a HTML drag-n-drop event (using e.dataTransfer), it fails
var name = e.dataTransfer.getData('name');
alert(name); // shows correct name
device.name = name; // fails <------
device.name = e.dataTransfer.getData('name'); // fails <------
So retrieval of the dataTransfer data fails for some reason. Why?
On Linux I do get a string back, but its formatted like t[]e[]s[]t[]. Seems like an encoding issue.

Resources