Android与Js基本交互
一直都想写博客,但是一直因为工作、个人时间安排不合理等等各方面原因,一直没有成功(>_<)。不过很开心,今天终于可以付诸行动。分享,带给人乐趣的同时,自己也是种重新学习。
记得去年做项目的时候,有一天快下班,突然下来一个任务,要求给项目添加一段Js的注入代码,附加一个新功能,当天就要完成。当时听到第一个反应:有点模糊。太久没用了,脑袋里只模糊记得一些用法,但具体细节已经记不清了。当时心里就在想,什么时候写一篇关于Android和Js交互的博客,这样以后要是再忘记了,可以随时翻看,也可以为正好用到的朋友提供一个参考。
Android和Js的交互,主要就是分两个部分,一部分:Android调用Js内部的方法;另一部分:Js调用Android端开放的接口方法。Android调用Js内部的方法,一般我们应该比较容易写出:
Android端代码:
mWebview.loadUrl("javascript:jsMethod()");
Js部分代码:
function jsMethod(){
document.getElementById("text").innerHTML = "我来自Android,调用Js内部的方法";
}
当然,这是最简单的直接调用,我们也可以选择在Android端传入一些参数,然后在Js端进行相应的逻辑操作。比如,我们这里Js端有一个用来计算的内部方法:
function jsMethodToCount(a, b){
var r = a + b;
document.getElementById("text").innerHTML = "我来自Android,调用Js计算方法,计算结果为:" + r;
}
计算方法中,将外部传入的两个参数进行简单相加,然后在页面上显示计算结果。
Android端相应调用:
mWebview.loadUrl("javascript:jsMethodToCount(121,8)");
这样,Android调用Js的一个简单过程就完成了。Js调用Android,需要在Android端编写开放的接口方法,比如一个最简单的直接调用,
Android端代码:
@JavascriptInterface
public void callAndroid(){
Log.w(TAG, "Js调Android callAndroid");
Toast.makeText(MainActivity.this, "Js调Android", Toast.LENGTH_SHORT).show();
}
Js端代码:
function callAndroidMethodNoReturn(){
invokeTa.callAndroid();
}
Js调Android接口,最简单的一种调用就完成了。我们当然也可以选择在Js端传入一些参数,然后Android端接收到参数,然后进行相应逻辑。
function callAndroidMethodNoReturn(a){
invokeTa.rideNoReturn(a);
}
Android端接口方法代码:
@JavascriptInterface
public void rideNoReturn(int n){
Log.w(TAG, "Js调Android,接收到传入参数 n:" + n);
//进行相应逻辑
}
运行一下,发现log输出,证明Js调Android接口方法成功,并且参数成功传入到Android端
05-02 18:37:19.457 13148-13182/com.example.androidandjs W/TAG MainActivity: Js调Android,接收到传入参数 n:100
这是没有返回值的调用,如果Js端需要返回值,再进行二次的逻辑操作,那我们可以返回返回值,新建Android端接口方法:
@JavascriptInterface
public int ride(int n){
Log.w(TAG, "Js调Android,接收到传入参数 n:" + n);
//进行相应逻辑
int q = 300;
return q*n;//返回值
}
Js端代码:
function callAndroidMethodWithReturn(a){
var result = invokeTa.ride(a);
document.getElementById("countText").innerHTML = "计算结果:"+result;
}
运行程序,
这里注意,Android端定义接口方法的时候,定义的接口方法名不能一样,不然不能识别,就算方法签名不同。比如,上面已经有个定义的接口方法ride(int n),如果我们再定义一个下面的接口方法
@JavascriptInterface
public int ride(int n, int b){
Log.w(TAG, "Js调Android,接收到传入参数(ride2) n:" + n);
return 300*n*b;
}
然后点击Js触发新定义的接口方法,会发现没有效果,调用失效。Js识别不了相同方法名的函数,不管方法签名是否相同。
到这里,Android和Js基本的相互调用,大致完成了。主要总结的一点:Android调用Js内部的方法,如果存在参数,参数是由Android端传入;Js调用Android端开放的接口方法,如果存在参数,参数是由Js端传入到Android端。参数来去理理清楚,当时公司要求新添加的需求,功能数据有些复杂,这些参数的来来去去,纠结了好一会(>_<)。
完整代码:
public class MainActivity extends AppCompatActivity {
private static final String TAG = "TAG MainActivity";
private Button callJsMethodNoParams, callJsMethodWithParams;
private WebView mWebview;
public void setupView(){
callJsMethodNoParams = (Button)findViewById(R.id.call_jsmethod_button);
callJsMethodWithParams = (Button)findViewById(R.id.call_jsmethod_withParams_button);
mWebview = (WebView)findViewById(R.id.webView);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
setupView();
//设置支持Js
mWebview.getSettings().setJavaScriptEnabled(true);
//添加接口对象 invokeTa 可以随便定义,Android与Js交互的类似标识Id的东西
mWebview.addJavascriptInterface(new MyJsObject(), "invokeTa");
//加载Js
mWebview.loadUrl("file:///android_asset/js.html");
addListener();
}
private void addListener() {
//Android调用Js 无参
callJsMethodNoParams.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebview.loadUrl("javascript:jsMethod('我来自Android,调用Js')");
}
});
//Android调用Js 传入参数
callJsMethodWithParams.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebview.loadUrl("javascript:jsMethodToCount(121,8)");
}
});
}
private class MyJsObject {
@JavascriptInterface
public void callAndroid(){
Log.w(TAG, "Js调Android callAndroid");
Toast.makeText(MainActivity.this, "Js调Android", Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public void rideNoReturn(int n){
Log.w(TAG, "Js调Android,接收到传入参数 n:" + n);
//进行相应逻辑
}
@JavascriptInterface
public int ride(int n){
Log.w(TAG, "Js调Android,接收到传入参数 n:" + n);
//进行相应逻辑
return 300*n;
}
// @JavascriptInterface
// public int ride(int n, int b){
// Log.w(TAG, "Js调Android,接收到传入参数(ride2) n:" + n);
// return 300*n*b;
// }
}
}
js.html代码:
<html>
<head>
<script type="text/javascript">
<!--Js内部方法,供Android调用-->
function jsMethod(){
document.getElementById("text").innerHTML = "我来自Android,调用Js内部的方法";
}
function jsMethodToCount(a, b){
var r = a + b;
document.getElementById("text").innerHTML = "我来自Android,调用Js内部的计算方法,计算结果:" + r;
}
<!--Js调用Android中的无返回值方法 -->
function callAndroidMethodNoReturn(){
invokeTa.callAndroid();
}
function callAndroidMethodNoReturn(a){
invokeTa.rideNoReturn(a);
}
<!--Js调用Android中的有返回值方法 -->
function callAndroidMethodWithReturn(a){
var result = invokeTa.ride(a);
document.getElementById("countText").innerHTML = "计算结果:"+result;
}
</script>
</head>
<body>
<textarea id="text" cols="35" rows="2"></textarea><br/>
<!--<button onclick="callAndroidMethodNoReturn()">Js调用Android中的无返回值方法(无参)</button><br/>-->
<button onclick="callAndroidMethodNoReturn(100)">Js调用Android中的无返回值方法(有参)</button><br/>
<button onclick="callAndroidMethodWithReturn(100)">Js调用Android中的有返回值方法</button><br/>
<textarea id="countText" cols="35" rows="2"></textarea><br/>
</body>
</html>
注意的细节:在写的案例里,因为使用的是本地的js文件,所以不需要使用到网络权限,如果是要用到网络上的js文件,记得在AndroidManifest.xml里添加网络权限:
<uses-permission android:name="android.permission.INTERNET"/>
然后Android调用Js的方法,是直接在代码里写死调用,也可以选择动态注入调用
String js = "var newscript = document.createElement(\"script\");";
js += "newscript.src=\"" + url + "\";";//url网络上的js url
js += String.format("newscript.onload=function(){%s;};", xxx()); // xxx()代表js中的某个方法
js += "document.body.appendChild(newscript);";
mWebview.loadUrl("javascript:" + js);
第一次写博客,有很多不足,也可能有很多表述不清晰或错误的地方,希望朋友们不吝指出。然后第一次弄,项目运行的gif效果,还不会弄,争取下一次的博客里添上。
源码下载