Java 使用Android的Google云频道和隐藏的WebView
关于谷歌不提供Java(和其他)API来访问其GAE频道的讨论很多,目前只能从JavaScript访问GAE频道,如下所述: 基本上,有两种可能的方法:Java 使用Android的Google云频道和隐藏的WebView,java,javascript,android,webview,channel-api,Java,Javascript,Android,Webview,Channel Api,关于谷歌不提供Java(和其他)API来访问其GAE频道的讨论很多,目前只能从JavaScript访问GAE频道,如下所述: 基本上,有两种可能的方法: 编写“模拟”访问的Java类,如下所示: 使用隐藏的WebView并将JavaScript连接到Java,如下所述: 我尝试了选项1,并使其正常工作(因此我假设我的服务器代码很好),但我拒绝了它,因为不能保证Google将来不会更改他们未记录的API 我继续并实现了以下代码: 创建WebView并使用JavaScript事件的Java类:
public class ChannelService {
class ChannelListener {
@JavascriptInterface public void onOpen() {
System.out.println("open"); // PROBLEM: these lines are never called
}
@JavascriptInterface public void onMessage(String message) {
System.out.println("message");
}
@JavascriptInterface public void onError(Integer errorCode, String description) {
System.out.println("error");
}
@JavascriptInterface public void onClose() {
System.out.println("close");
}
}
public ChannelService(Context context) throws IOException {
final WebView webView = new WebView(context);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new ChannelListener(), "channelListener"); // the connection between Java and JavaScript
webView.setWebViewClient(new WebViewClient() {
@Override public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url); // this line is called after the page loads
}
@Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
}
});
String html = AssetsHelper.assetToString(context, "channels.html"); // loads the html file below
html = html.replaceAll("\\{\\{ channelurl \\}\\}", ApiService.channelUrl()); // channelurl and token are replaced fine and should be correct
html = html.replaceAll("\\{\\{ token \\}\\}", Session.getToken().getId());
webView.loadData(html, "text/html", null);
}
}
包含JavaScript代码的channels.html文件:
<html>
<head>
<script src='{{ channelurl }}jsapi'></script>
</head>
<body>
<script type='text/javascript'>
onOpen = function() {
channelListener.onOpen();
}
onMessage = function(message) {
channelListener.onMessage(message);
}
onError = function(errorcode, description) {
channelListener.onError(errorcode, description);
}
onClose = function() {
channelListener.onClose();
}
openChannel = function() {
var token = '{{ token }}';
var channel = new goog.appengine.Channel(token);
var handler = {
'onopen': onOpen,
'onmessage': onMessage,
'onerror': onError,
'onclose': onClose
};
var socket = channel.open(handler);
socket.onopen = onOpen;
socket.onmessage = onMessage;
socket.onerror = onError;
socket.onclose = onClose;
}
</script>
</body>
</html>
onOpen=函数(){
channelListener.onOpen();
}
onMessage=函数(消息){
onMessage(消息);
}
onError=功能(错误代码、说明){
channelListener.onError(错误代码、描述);
}
onClose=函数(){
channelListener.onClose();
}
openChannel=function(){
var-token='{{token}}';
var通道=新的goog.appengine.channel(令牌);
变量处理程序={
“onopen”:onopen,
“onmessage”:onmessage,
“onerror”:onerror,
“onclose”:onclose
};
var socket=channel.open(处理程序);
socket.onopen=onopen;
socket.onmessage=onmessage;
socket.onerror=onerror;
socket.onclose=onclose;
}
感谢Google,WebView不会报告我的代码中的任何错误,这使得调试非常困难。多亏了我自己,我从来没有主动编写过JavaScript代码,所以我在这里非常愚蠢,你可能会笑,并立即发现我的错误
我已经在代码中记录了这一点,但让我在另一个时间说明什么是有效的,什么是无效的:
- 95%的人确信服务器端代码可以正常工作,因为我的选项1可以正常工作。因此,我几乎可以肯定,我的信息是发送给客户的
- 当客户端登录并通过REST端点将令牌传递给客户端时,我正在预创建通道。它完好无损地到达那里(与之相比),而且它毕竟也嵌入得很好
- 当我收到带有正确url的onPageFinished()时,我的网站似乎已加载。(修正:我确实收到过好几次这个事件,有一些看起来像params的东西,但我猜这是安卓和普通的。)
- 然后什么都不会发生,没有异常,Java事件也不会被调用
- 我的Java代码仍在继续,当我设置断点时,可以检索WebView并且它是活动的,我在活动中为它使用了一个公共变量,这应该很好;我确保它在UI线程上
欢迎任何想法,无论是我做错了什么,还是我如何调试它。谢谢 我发现了两个问题:
webView.loadUrl("javascript:openChannel()")
或者完全删除该函数,并将部分脚本留在主体中(请参见下文)
onSomething = function() {
ChannelListener.onSomething();
};
<html>
<head>
<script src='{{ channelurl }}jsapi'></script>
</head>
<body>
<script type="text/javascript">
onOpen = function() {
ChannelListener.onOpen();
};
onMessage = function(message) {
ChannelListener.onMessage(message.data);
};
onError = function(error) {
ChannelListener.onError(error.code, error.description);
};
onClose = function() {
ChannelListener.onClose();
};
var token = '{{ token }}';
var channel = new goog.appengine.Channel(token);
var handler = {
'onopen': onOpen,
'onmessage': onMessage,
'onerror': onError,
'onclose': onClose
};
var socket = channel.open(handler);
socket.onopen = onOpen;
socket.onmessage = onMessage;
socket.onerror = onError;
socket.onclose = onClose;
</script>
</body>
</html>
onOpen=函数(){
ChannelListener.onOpen();
};
onMessage=函数(消息){
ChannelListener.onMessage(message.data);
};
onError=函数(错误){
ChannelListener.onError(error.code,error.description);
};
onClose=函数(){
ChannelListener.onClose();
};
var-token='{{token}}';
var通道=新的goog.appengine.channel(令牌);
变量处理程序={
“onopen”:onopen,
“onmessage”:onmessage,
“onerror”:onerror,
“onclose”:onclose
};
var socket=channel.open(处理程序);
socket.onopen=onopen;
socket.onmessage=onmessage;
socket.onerror=onerror;
socket.onclose=onclose;
现在只剩下一个问题,但情况不同了:onMessage(message)不会在处理程序中传递message参数,可能是因为它是在不同的范围内执行的。我把这个问题作为一个新问题发布在这里:
--
我发现了为什么我没有收到来自服务器的消息的问题,并编辑了上面的代码。要接收消息,必须将message.data而不仅仅是消息传递给Java,以接收服务器发送的字符串消息
--
我也根据这里定义的谷歌规范盲目地调整了onError函数,但由于谷歌在这里的示例代码中没有实现错误处理,我不能肯定它是否能像这样工作,但我假设它能做到。如果您遇到此问题并能确认其工作正常,请发表评论。对不起,我的代码出错了。而不是webView.loadData(html,“text/html”,null);我将数据加载为url webView.loadUrl(html);-OnPageFinished的原因被调用了两次。我已经在上面的代码中修复了这个问题。问题依然存在。