I have an implemented method in my class which assign instance variable. I want to create a method which waits until interface method runned and then returns instance variable which is assigned in there. If I couldn't find any solution,I need to re-code my class to handle this problem. As an example :
public class MyClass {
private String /*or AnyObject*/ string;
@Override
public void onData(String value) {
this.string=value;
}
public void callOnData(/*some param*/){
//does some work and calls onData();
}
public String whatIwantTo(){
//if onData called
//return this.string;
//else wait until it recevied.
}
}
After that I can call whatIwantTo()
method from myMain class.
If you wonder What I tried, It looks like:
public class MyClass {
private String /*or AnyObject*/ string;
private static final class Lock {}
private final Object lock = new Lock();
void lock() {
synchronized (lock) {
while (string == null) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void onData(String value) {
this.string = value;
synchronized (lock) {
lock.notify();
}
}
public void callOnData(/*some param*/) {
//does some work and calls onData();
}
public String whatIwantTo() {
callOnData();//No need to check if it is null at this time.
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
lock();
}
});
thread.start();
try {
/*If I use this it freezes and never notifyed from onData*/
/*Because thread is locked so it doesn't receive any data*/
/*If I don't use this it returns null*/
/*And then calling getString() from myMain class returns right value.*/
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
return this.string;
}
public String getString() {
return this.string;
}
}
and in myMain class:
String returned=whatIwantTo();
System.out.print(""+returned)//returns null or never reached.
OK. After @JBNizet request, I copy all code what I used is and I use it in android:
package makgun.webview;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import android.webkit.JavascriptInterface;
import android.webkit.WebSettings;
import android.webkit.WebView;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* Created by makgun on 19.03.2017.
*/
public class MyClass {
private Context context;
private String string;
private WebView webView;
List<Notify> lists;
private static final class Lock { }
private final Object lock = new Lock();
private final CountDownLatch latch = new CountDownLatch(1);
MyClass(Context context) {
this.context = context;
}
public interface Notify {
void onNotify(String result);
}
void onNotifyListener(Notify notify) {
if (lists == null)
lists = new ArrayList<>();
lists.add(notify);
}
private void setNotify(String result) {
if (lists != null)
for (Notify notify : lists)
notify.onNotify(result);
}
String xyz(){
try {
synchronized (lock) {
while (string ==null) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("makgun", "xyz_after_wait");
}
}
}catch (Exception e){
e.printStackTrace();
}
Log.d("makgun","xyz_return : "+ string);
return string;
}
private void ny(){
try {
synchronized (lock) {
Log.d("makgun", "ny()");
lock.notify();
lock.notifyAll();
}
}catch (Exception e){
e.printStackTrace();
}
}
@SuppressLint({"SetJavaScriptEnabled", "AddJavascriptInterface", "JavascriptInterface"})
private void initJs(){
webView = new WebView(context);
WebSettings webSettings = webView.getSettings();
webSettings.setJavaScriptEnabled(true);
webView.addJavascriptInterface(this, "Android");
}
private void runJs(String html) {
webView.loadDataWithBaseURL("", html, "text/html", "charset=UTF-8", null);
}
@JavascriptInterface
public String onData(String value) {
Log.d("makgun",value);
setNotify(value);//For now I can read it via this custom interface
string =value;
Log.d("makgun","string Setted");
latch.countDown();
return value;
}
private String LoadData(String inFile) {
String tContents = "";
try {
InputStream stream = context.getResources().getAssets().open(inFile);
int size = stream.available();
byte[] buffer = new byte[size];
stream.read(buffer);
stream.close();
tContents = new String(buffer);
} catch (IOException e) {
// Handle exceptions here
}
return tContents;
}
String getHtml() {
return LoadData("script.html");
}
public void initJS() throws ExecutionException, InterruptedException {
Activity activity=((Activity)context);
Callable<Void> callable = new Callable<Void>() {
@Override
public Void call() throws Exception {
initJs();
return null;
}
};
FutureTask<Void> task = new FutureTask<>(callable);
activity.runOnUiThread(task);
task.get(); // Blocks
}
public void runJS(final String html) throws ExecutionException, InterruptedException {
Activity activity=((Activity)context);
Callable<Void> callable = new Callable<Void>() {
@Override
public Void call() throws Exception {
runJs(html);
return null;
}
};
FutureTask<Void> task = new FutureTask<>(callable);
activity.runOnUiThread(task);
task.get(); // Blocks
}
String whatIwantTo(String html) throws ExecutionException, InterruptedException {
Log.d("makgun","initJS_started");
long startTime=System.currentTimeMillis();
initJS();
long temp=System.currentTimeMillis();
Log.d("makgun","initJS_finished in ["+(temp-startTime)+" ms]");
runJS(html);
Log.d("makgun","runJS_finished in ["+(System.currentTimeMillis()-temp)+" ms]");
/*After this step it will call onData() but latch.await() locks before interface reached.*/
// latch.await();
return string;
}
}
This is html which will load to webView (named as script.html):
<!doctype html>
<html lang="en-US">
<head>
</head>
<body>
<script>
Android.onData('Hello World I am here!');
</script>
<!--
Empty Body Just For Test
-->
</body>
</html>
And Finally what I used from MainActivity is:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button button = (Button) findViewById(R.id.button);
final MyClass myClass=new MyClass(MainActivity.this);
myClass.onNotifyListener(new MyClass.Notify() {
@Override
public void onNotify(String result) {
Log.d("makgun","OnNotify result: ["+result+"]");
}
});
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String returned = null;
try {
returned=myClass.whatIwantTo(myClass.getHtml());
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("makgun","returned ["+returned+"]");
}
});
}
And the catlog with latch.await disabled!:
03-19 05:05:27.238 10963-10963/? D/makgun: InitJS_started
03-19 05:05:27.308 10963-10963/? D/makgun: initJS_finished in [71 ms]
03-19 05:05:27.318 10963-10963/? D/makgun: initJS_finished in [10 ms]
03-19 05:05:27.318 10963-10963/? D/makgun: returned [null]
03-19 05:05:27.438 10963-11153/? D/makgun: Hello World I am here!
03-19 05:05:27.438 10963-11153/? D/makgun: OnNotify result: [Hello World I am here!]
03-19 05:05:27.438 10963-11153/? D/makgun: string Setted
And Finally the catlog with latch.await NOT disabled!:
Nothing getted and app is freezed.