获取在接口方法中指定的返回值作为方法?

时间:2017-03-18 22:01:09

标签: java android

我的类中有一个实现的方法,它分配实例变量。我想创建一个方法,等待接口方法运行,然后返回在那里分配的实例变量。如果我找不到任何解决方案,我需要重新编写我的类来处理这个问题。举个例子:

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.

2 个答案:

答案 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。 (我无法对你的答案进行投票,但谢谢。)。经过一些研究后,我发现它冻结的原因。问题是AndroidJava无关。因为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] 。这是巨大的不同。我无法弄清楚原因。