欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android中JavaScript和Native之间的Bridge

程序员文章站 2022-05-15 11:51:43
...
为什么手机浏览器或者WebView中不能执行JavaScript调用本地API,而在HTML5混合式应用中却能执行?

JavaScript调用本地API大概有4种方法:

(1)addJavascriptInterface/@JavascriptInterface
Android的WebView类的标准接口。
webView.addJavascriptInterface(new JSHandler(this), "Bridge");

class JSHandler {
	public Context context;
	
	public JSHandler(Context c) {
		this.context = c;
	}

	public void doSomething() {
		Log.d("JSHandler", "doSomething@JSHandler");
		Toast.makeText(this.context, "doSomething@JSHandler", Toast.LENGTH_LONG).show();
	}
}

addJavascriptInterface()方法的第一个参数就是要暴露给JavaScript的Class对象,第二个参数是可以调动该Class方法的JavaScript的全局变量。

$(function() {
	Bridge.doSomething();
});


但是addJavascriptInterface()方法存在安全隐患,在JavaScript中可以反射调用到Class的任意属性,比如以下代码能够取到Activity的package名。
$(function() {  
	var klass = Bridge.getClass();
	var field = klass.getDeclaredField('context');
	field.setAccessible(true);
	var context = field.get(Bridge);
	document.getElementById('res').innerHTML = context.getPackageName();
});

Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  

Android 4.2 Jelly Bean以后版本,只有标示了@JavascriptInterface的方法JavaScript才能调到。
webView.addJavascriptInterface(new MyCustomHander(this), "Bridge");

class MyCustomHander {
	public Context context;
	
	public MyCustomHander(Context c) {
		this.context = c;
	}
	
	@JavascriptInterface
	public void doSomething() {
		Log.d("MyCustomHander", "doSomething@MyCustomHander");
		Toast.makeText(this.context, "doSomething@MyCustomHander", Toast.LENGTH_LONG).show();
	}
	
	public void doSomething2() {
		Log.d("MyCustomHander", "doSomething2@MyCustomHander");
		Toast.makeText(this.context, "doSomething2@MyCustomHander", Toast.LENGTH_LONG).show();
	}
}


Android 4.2以前版本看到的是:"doSomething2@MyCustomHander",而Android 4.2以后版本看到的是:"doSomething@MyCustomHander"。
比如Android 4.1:
Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  

(2)自定义URL

webView.setWebViewClient(new WebViewClient() {
	@Override
	public boolean shouldOverrideUrlLoading(WebView view, String url) {
		if (url.startsWith("apicall://")) {
			Log.d("MyWebViewClient", "doSomething@WebViewClient: " + url);
			Toast.makeText(getBaseContext(), "doSomething@WebViewClient: " + url, Toast.LENGTH_LONG).show();
			return true;
		}
		return false;
	}
});


$(function() {
  window.location = 'apicall:////some_api_name/exec?a=1&b=2';
});

Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  

(3)JsAlert

webView.setWebChromeClient(new WebChromeClient() {
	@Override
	public boolean onJsAlert(WebView view, String url,
			String message, JsResult result) {
		Log.d("MyWebChromeClient", "doSomething@WebChromeClient: " + message);
		Toast.makeText(getBaseContext(), "doSomething@WebChromeClient: " + message, Toast.LENGTH_LONG).show();
		// return super.onJsAlert(view, url, message, result);
		return true;
	}
});


$(function() {
  alert("123");
});

Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  

(4)搭建本地HTTP服务器

NanoHttpd 是一个轻量级的可嵌入的HTTP服务器。
public class APIHttpServer extends NanoHTTPD {

	public APIHttpServer() {
		super(4000);
	}

	@Override
	public Response serve(String uri, Method method,
			Map<String, String> headers, Map<String, String> params,
			Map<String, String> files) {

		String data = "uri=" + uri + ", params=" + params;
		Log.d("APIHttpServer", "doSomething@APIHttpServer: " + data);

		return new NanoHTTPD.Response(Status.OK, "application/json", "{msg:'nano'}");
	}
}

APIHttpServer server = new APIHttpServer();
server.start();


$(function() {
  $.getJSON("http://localhost:4000/some_api_name?a=1&b=2&c=3");
});


AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<uses-permission android:name="android.permission.INTERNET"/>


Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  
Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  

还有一种方法,通过URL的变更也能够实现,但是存在安全问题,基本不使用!
window.location = 'http://cdv_exec/' + service + '#' + action + '#' + callbackId + '#' + argsJson;

public class MyWebViewClient extends WebViewClient {
  public boolean shouldOverrideUrlLoading(WebView view, String url) {
    if (url.startsWith("http://cdv_exec/")) {
      handleExecUrl(url); // 截取参数处理
    }
  }
}


参考:
http://www.buildinsider.net/mobile/bookhtml5hybrid
  • Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  
  • 大小: 49.3 KB
  • Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  
  • 大小: 21.9 KB
  • Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  
  • 大小: 18.8 KB
  • Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  
  • 大小: 19.9 KB
  • Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  
  • 大小: 13.3 KB
  • Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  
  • 大小: 11 KB
  • Android中JavaScript和Native之间的Bridge
            
    
    博客分类: Android  
  • 大小: 3 KB