To explain what I want to achieve I wrote what I'm able to do and then - when a problem appears. What works well:
1) I have webView1 containing in loaded HTML
<button onclick="android.buttonPressed();">Click</button>
binded with interface:
private class WebAppInterface1 {
Context context;
WebAppInterface1(Context c) {
context = c;
}
#JavascriptInterface
public void buttonPressed() {
Toast.makeText(context, "buttonPressed", Toast.LENGTH_LONG).show();
}
}
The HTML button works.
2) I have webView2 containing in loaded HTML
<script>
function f() {
document.write("bla bla bla");
}
</script>
Then I call
webView2.loadUrl("javascript:f();");
in android code and it works too.
Now I want to modify webView2's page by pressing button in webView1. Technically it means that I want to call f() inside webView2 when JS interface detected click in webView1. So I add this line
webView2.loadUrl("javascript:f();");
in buttonPressed() function inside JS interface. But it has no effect. What can cause the problem? Are there any limitations in connecting these reciever and sender? Maybe I missed some settings or permissions?
There are my project files, I tried to make them as simple as possible: layout, html, java. Didn't edit other files. Can give any additional information if needed.
I understood that my problem is related to another problem: one can't update UI from non-main thread (was covered on SO here and here). But in my case there isn't any errors for some reason. Maybe because of JavaScript isn't as "strict" as Java.
So solved by adding android.os.Handler in my activity class, assigning it in onCreate function:
handler = new Handler() {
public void handleMessage(Message msg) {
webView2.loadUrl("javascript:f()");
}
}
and accessing it in buttonPressed function:
handler.obtainMessage(1).sendToTarget();
P.S. Another way is to wrap calling webView2.loadUrl("javascript:f()"); by Runnable and calling runOnUiThread(runnable). It looks more neat:
webView1.addJavascriptInterface(new Object() {
#JavascriptInterface
public void buttonPressed() {
runOnUiThread(new Runnable() {
#Override
public void run() {
webView2.loadUrl("javascript:f()");
}
});
}
}, "android");
P.P.S. Enabling Java 8 in app/build.gradle allows to write it even more neat:
runOnUiThread(()->{webView2.loadUrl("javascript:f()");});
Related
I need to use threads in my android application because I am doing image processing w/ native opencv. Here is my code:
void Detector::processBinary(Mat &binary) {
//do stuff
}
void Detector::Detect() {
...
thread t1(processBinary, binary);
t1.join();
}
However, I get the error "invalid use of non-static member function" from thread t1(processBinary, binary) whenever I try to run the app. This line, however, works perfectly in visual studio. Can anyone help me with this? Thanks in advance!
You use member function, that needs this argument (it must be called on some object). There are two alternatives:
Use static class function (or non-class function at all):
void processBinary(Mat &binary) {
//do stuff
}
void Detector::Detect() {
...
thread t1(processBinary, binary);
t1.join();
}
Or pass proper arguments if we want utilize member function:
void Detector::processBinary(Mat &binary) {
//do stuff
}
void Detector::Detect() {
...
thread t1(&Detector::processBinary, *this, binary);
t1.join();
}
In IOS we use blocks when we want to handle an action when a particular situation occur.
Is there a way like this in android to handle situation onCompletion and we can add where ever it is.
Like AsyncTask methods it knows when work is finish . it executes onPostExecute. How we can create this type of method when a particular situation reach and we handle it like .
Today i find a way which behave like Blocks in IOS
BaseAsyncTask(new Callback(){
onPostResult(string result)
{
// do whatever you want to do with the result got from asynctaks
}
});
it is a delegate and it calls when it reaches a particular situation..
Am i right that the above code is block in android as we do in IOS. or there any other way of creating blocks in android.
Behave like iOS Block:
A. Let say, in your class APISingleton (e.g Volley request-API in singleton class):
Define the Interface outside your class:
// Callback Blueprint
interface APICallback {
void onResponse(User user, boolean success, String message); // Params are self-defined and added to suit your needs.
}
In your API-request function
public void requestAPIWithURL:(String url, final APICallback callback) {
// ....
// After you receive your volley response,
// Parse the JSON into your model, e.g User model.
callback.onResponse(user, true, "aloha");
}
B. So, how to call API request and pass your callback function from your activity, or fragment, is like this:
APISingleton.getInstance().requestAPIWithURL("http://yourApiUrl.com", new APICallback() {
#Override
public void onResponse(User user, boolean success, String message) {
// you will get your user model, true, and "aloha" here
mUser = user;
Log.v(TAG, "success:" + success);
Log.v(TAG, "aloha or die?" + message);
// Your adapter work. :D
}
});
It seems that you have two questions in your question so I will answer them both
Is there a way like this in android to handle situation onCompletion and we can add where ever it is?
How we can create this type of method when a particular situation is reached?
Yes there is a way. I have asked the same thing once here.
In this cases, you use Runnable. You use it like so:
//creating a runnable block
Runnable block = new Runnable() {
#Override
public void run() {
//code here
}
};
//passing it on arguments
doSomething(block); //just like a normal variable
//using it on the method
public void doSomething(Runnable block) {
block.run();
}
Your 2nd question:
BaseAsyncTask(new Callback(){
onPostResult(string result)
{
// do whatever you want to do with the result got from asynctaks
}
});
Am i right that the above code is block in android as we do in IOS. or there any other way of creating blocks in android.
Yes in my opinion, the object Callback behaves the same as the blocks in Objective C as I have shown in my previous examples. When the async task is completed (the result is already available) then that Callback is called. Probably like so:
callback.onPostResult();
Just for your information, the Objects Runnable and Callback are java interfaces. More on interfaces here. If you do not need extra parameters for your callback, you can just use the Runnable so as to avoid reinventing the wheel. But if there is something special, i.e. you want to pass paramters to the callback, then you can create your own interface and use it like a Runnable.
I open a dialog when the image of a WebView is pressed and then I get this warning. I assume that the WebView is the different thread he is talking about but is there a way to fix this? I simply execute a function when the image is pressed. The function is inside a fragment.
public View onCreateView(....
...
...
mWebView.addJavascriptInterface(new Object()
{
#JavascriptInterface
public void performClick()
{
enlargeImage();
}
}, "ok");
private void enlargeImage()
{
mImageView = new ImageView(getActivity());
loadImageFromURL(mImage, mImageView);
loadPhoto(mImageView);
}
here is the loadPhoto function which is giving me the problem.
To be more precise the Dialog is giving the problem. If there is anything I can replace it with it will do as well like a window. What I have and what I want is a window to display a image in which I can close.
The callback from javascript will be executed on a background thread, not the UI thread where you can touch the view hierarchy. From your javascript callback (i.e. performClick), you should post a task back to the UI thread.
This documentation should help: http://developer.android.com/training/multiple-threads/communicate-ui.html
I needed to place my image code inside :
context.runOnUiThread(new Runnable()
{
...
...
...
});
long time watcher, first time writer :P
I got this problem:
I can't seem to change anything that has to do with the layout of android from my playSoundThread.
In this example, I use EventListeners. I already tried the simple way. I passed the ScrollView through, so that the thread can change it. But when it's happening, the thread stops immediately. And even when I use EventListeners, the same Problem occurs.
Changing a variable and posting log information works fine, but not layout Objects.
The first thing is, that I want to scroll a HorizontalScrollView from out the Thread's run() method.
the second case is, that, if the thread comes to it's end, I wanna fire an "i'm finished"-Event and change the image and function of an ImageButton
Here's the run()-method of the thread
public void run() {
if(this.playbackPosition < rhythm.tracks.get(0).sounds.size()) {
for (Track t : rhythm.tracks) {
if (t.sounds.get(this.playbackPosition).equals("1")) {
this.sp.play(t.SoundID, 1, 1, 1, 0, 1);
}
}
this.playbackPosition++;
if ( this.playbackPosition >= (this.scrollIndex*(192/this.zoom)) ){
this.scrollIndex++;
//Here I wanna fire the "Scroll" event
for(ScrollListener sl : scrollListeners){
sl.update(scrollPositions[scrollIndex]);
}
}
}
//This is the point where the playback is finished and the event to change a button is fired
else {
tmpListener.update();
}
}
}
The declaration of the OnPlaybackFinishedListener can be found in the class Player, which is the parent of the PlaySoundThread:
public void addOnPlaybackFinishedListener(){
tmpListener = new OnPlaybackFinishedListener() {
#Override
public void update() {
scheduledExecutorService.shutdown();
//this is a seconds Listener, which was implemented to test, if the problem still occurs with a little listener chain
shutdownListener.update();
}
};
}
public void addShutdownListener(OnExecutorShutdown sl){
this.shutdownListener = sl;
}
And here's the part of the MainActivity which is the parent class of Player and adds the shutdown listener and the ScrollListener:
awesomePlayer.addScrollListener(new ScrollListener(){
public void update(int position){
Log.i("ScrollListener update()","Running ScrollTo( "+position+", "+VIEW_rhythmscroll.getScrollY()+")");
VIEW_rhythmscroll.scrollTo(position, VIEW_rhythmscroll.getScrollY());
}
});
awesomePlayer.addOnPlaybackFinishedListener();
awesomePlayer.addShutdownListener(new OnExecutorShutdown() {
#Override
public void update() {
// TODO Auto-generated method stub
//This method changes the Pause Button to a Play Button with a new OnClickListener and a new Picture
BUTTON_STOP.performClick();
}
});
Can anyone help? Is there another way to avoid this problem? I'm developing on Android 2.2
Is it even possible to access UI elements from a thread?
Thanks in advance :)
You can't modify UI elements from a seperate thread, UI elements have to be modified from the main, UI Thread. There are a lot of topics on this, but you can update the UI by using an AsyncTask's onPostExecute(), onPreExecute(), or onProgressUpdate() methods, the Activity class's runOnUiThread(Runnable action), or by sending a Message to a Handler.
I have the following void:
public void load() {
//loading big picture from the Internet
}
And i want it to run in a new Thread.
I can call this procedure like this:
new Thread(new Runnable() {
public void run() {
load();
}
}).start();
or it would be better to modify this void:
public void load() {
new Thread(new Runnable() {
public void run() {
//loading big picture from the Internet
}
}).start();
}
and simply call it:
load();
Or there is no different?
Functionally, they are identical. There are designed details that might make you consider the first way over the other.
You may want the first option if you had a lot of different threads that didn't their own individual things but also called load(). In that case, you already had created the thread, so if they called load() you wouldn't want/need to create another.
The second option is very convenient. You can simply call load() wherever you want. If, in the future, the load() method changes to a point where it's not blocking, then you can change it and no further code changes would be needed.
Alternatively, consider using AsyncTask for this as previously suggested. It was specifically built for exactly what you're trying to do.