我的类中有一个实现的方法,它分配实例变量。我想创建一个方法,等待接口方法运行,然后返回在那里分配的实例变量。如果我找不到任何解决方案,我需要重新编写我的类来处理这个问题。举个例子:
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.
}
}
之后我可以从myMain类调用whatIwantTo()
方法。
如果你想知道我尝试了什么,它看起来像:
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;
}
}
并在myMain类中:
String returned=whatIwantTo();
System.out.print(""+returned)//returns null or never reached.
行。在 @JBNizet 请求之后,我复制了我使用的所有代码,并在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;
}
}
这是将加载到webView(命名为script.html)的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>
最后我从MainActivity使用的是:
@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+"]");
}
});
}
禁用了latch.await的catlog!:
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
最后,带有latch.await的catlog未被禁用!:
Nothing getted and app is freezed.
答案 0 :(得分:2)
您可以使用CountdownLatch轻松完成此操作。可以使用wait()
和notify()
,但它们的级别太低,难以正确使用。
这是一个完整的最小例子。
import java.util.concurrent.CountDownLatch;
public class MyClass {
private String string;
private final CountDownLatch latch = new CountDownLatch(1);
public void onData(String value) {
this.string = value;
latch.countDown();
}
public void callOnData(/*some param*/) {
new Thread(() -> {
try {
Thread.sleep(100L);
}
catch (InterruptedException e) {
}
onData("hello");
}).start();
}
public String whatIwantTo() throws InterruptedException {
callOnData();
latch.await();
return this.string;
}
public static void main(String[] args) throws InterruptedException {
MyClass m = new MyClass();
System.out.println(m.whatIwantTo());
}
}
答案 1 :(得分:0)
感谢您的帮助。特别是@JBNizet。 (我无法对你的答案进行投票,但谢谢。)。经过一些研究后,我发现它冻结的原因。问题是Android
与Java
无关。因为Android将线程分隔为ui和非ui。因此,如果我从ui线程阻止它,它也会冻结接口线程。所以我从new Thread(Runnable)
内部调用方法,现在它可以工作了。
这就是我的代码的样子:
@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+"]");
}
});*/ //No more needed!
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final String[] returned=new String[1];
try {
Thread t=new Thread(new Runnable() {
@Override
public void run() {
long a=System.currentTimeMillis();
returned[0] =myClass.whatIwantTo(myClass.getHtml());
Log.d("makgun","Returned in ["+(System.currentTimeMillis()-a)+" ms]");
}
});
t.start();
try {
t.join(/*timeout*/);
} catch (InterruptedException e) {
e.printStackTrace();
}
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("makgun","returned ["+returned[0]+"]");
}
});
}
我也获得了经验,如果我在doInBackground中完成,接口完成时间约为[150 - 200 ms]
但是对于上面的代码,即使我从我的资产加载相同的数据,时间大约为[1000 - 1500 ms]
。这是巨大的不同。我无法弄清楚原因。