[英]Get return value which assigned in interface method as a method?
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. 之后,我可以从myMain类调用
whatIwantTo()
方法。
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: 在myMain类中:
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:
在@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;
}
}
This is html which will load to webView (named as script.html): 这是html,它将加载到webView(名为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: 最后,我在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+"]");
}
});
}
And the catlog with latch.await disabled!: 并禁用了具有闩锁.await的目录!
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!: 最后,不禁用带有latch.await的目录!
Nothing getted and app is freezed.
You can use a CountdownLatch to do that easily. 您可以使用CountdownLatch轻松做到这一点。
wait()
and notify()
can be used to, but they're too low level, and hard to use correctly. 虽然可以使用
wait()
和notify()
,但是它们的级别太低,很难正确使用。
Here is a complete minimal example. 这是一个完整的最小示例。
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());
}
}
Thanks for your help. 谢谢你的帮助。 Especially to @JBNizet.
特别是@JBNizet。 (
(
I couldn't vote up your answer but thanks. ). 我不能投票赞成你的回答,但是谢谢。
)。 After some research I found why it freezes.
经过一些研究,我发现了为什么冻结。 The problem is
Android
related not Java
.Because Android seperates thread as ui and non-ui. 问题是
Android
与Java
不相关。因为Android将线程分为ui和non-ui。 So if I block it from ui thread,it freezes the interface thread as well. 因此,如果我从ui线程阻止它,它也会冻结接口线程。 So I call method from inside of
new Thread(Runnable)
and now it works. 所以我
new Thread(Runnable)
内部调用方法,现在它可以工作了。 This is how my code looks like: 这是我的代码的样子:
@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]+"]");
}
});
}
I also gain experience that if I do it from in doInBackground, the interface finished time is about [150 - 200 ms]
but for above code, the time is about [1000 - 1500 ms]
even if I load same data from my assets. 我还获得了经验,如果我从doInBackground中进行操作,则接口完成时间约为
[150 - 200 ms]
但是对于上述代码,即使我从资产中加载相同的数据,时间也约为[1000 - 1500 ms]
。 This is huge differents . 这是巨大的差异 。 I couldn't figure out its why.
我不知道为什么。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.