您的位置:首页 > 移动开发 > Android开发

[转载]Android笔记(三十六)Android中js和java的互调(二)(调用js获取返回值)

2015-05-19 18:18 344 查看
原文地址:Android笔记(三十六)Android中js和java的互调(二)作者:潜易

20.4读取js全局变量或函数返回值

借助现有接口技术,js可以执行原生java代码中的方法,可以得到方法的返回值,可以让原生java代码在主线程中动态的操作UI;但是借助该接口,原生java代码,采用webview.loadUrl("javascript: JsFunctionName"),只能做到执行js中的方法,如果想获取js中定义的全局变量,或者获取某个js函数的返回值,这种方式无法做到,webview也没有提供别的函数来可供使用。

为了实现该功能,我们分析application framework的源代码发现,从webview类loadurl()方法一路追踪,最终在WebViewCore.java中找到如下代码:

private native void passToJs(int frame, int node, int x, int y, int gen,

String currentText, int keyCode, int keyValue, boolean down,

boolean cap, boolean fn, boolean sym);

在BrowserFrame中,追踪到:

private native void nativeAddJavascriptInterface(int nativeFramePointer,

Object obj, String interfaceName);

至此我们知道android的webview实现,使用的是开源的webkit浏览器内核,该内核是用c语言(webcore)和c++语言(jscore)实现的,android的webview底层实现最终是调用的webkit内核代码,如果该内核提供了直接读取js全局变量或者函数返回值的方法,那么我们可以使用JNI(Java Native Interface)的方式来读取出来。
20.4.1反射读取方式

在android.webkit包中有个BrowserFrame私有类,该类中有个Native方法:

public native String stringByEvaluatingJavaScriptFromString(String script);

这个和苹果中的类似:

Public NSString stringByEvaluatingJavaScriptFromString(NSString script);

虽然该类是私有的,但是我们可以利用反射技术来执行这个方法,从而取得js全局变量和函数返回值;

步骤:

1、 扩展WebView,派生出MyWebView类,添加

public String stringByEvaluatingJavaScriptFromString(String script)方法,该方法体中最终利用反射技术实现;

2、 修改布局中的WebView为com.appeon.test.MyWebView类型;

3、 在页面load完成的情况下,编码取得JS变量或函数返回值;

MyWebView.java:

package com.appeon.test;



import java.lang.reflect.Field;

import java.lang.reflect.InvocationTargetException;

import java.lang.reflect.Method;



import android.content.Context;

import android.util.AttributeSet;

import android.webkit.WebView;



public class MyWebView extends WebView {

public MyWebView(Context context) {

super(context);

// TODO Auto-generated constructor stub

}

public MyWebView(Context context, AttributeSet attrs) {

super(context, attrs);

// TODO Auto-generated constructor stub

}

public MyWebView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

// TODO Auto-generated constructor stub

}



public String stringByEvaluatingJavaScriptFromString(String script) {

try {

//由webview取到webviewcore

Field field_webviewcore = WebView.class.getDeclaredField("mWebViewCore");

field_webviewcore.setAccessible(true);

Object obj_webviewcore = field_webviewcore.get(this);

//由webviewcore取到BrowserFrame

Field field_BrowserFrame = obj_webviewcore.getClass().getDeclaredField("mBrowserFrame");

field_BrowserFrame.setAccessible(true);

Object obj_frame = field_BrowserFrame.get(obj_webviewcore);

//获取BrowserFrame对象的stringByEvaluatingJavaScriptFromString方法

Method method_stringByEvaluatingJavaScriptFromString = obj_frame.getClass().getMethod("stringByEvaluatingJavaScriptFromString", String.class);

//执行stringByEvaluatingJavaScriptFromString方法

Object obj_value = method_stringByEvaluatingJavaScriptFromString.invoke(obj_frame, script);

//返回执行结果

return String.valueOf(obj_value);

} catch (SecurityException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (NoSuchFieldException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalArgumentException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IllegalAccessException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (NoSuchMethodException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (InvocationTargetException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return null;

}



}

Layout:

<?xml version="1.0" encoding="utf-8"?>

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

android:orientation="vertical"

android:layout_width="fill_parent"

android:layout_height="fill_parent"

>

<Button android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content"android:layout_height="wrap_content"></Button>

<com.appeon.test.MyWebView android:id="@+id/webView1" android:layout_width="fill_parent"android:layout_height="fill_parent"></ com.appeon.test.MyWebView>

</LinearLayout>

被测试js:

<script type="text/javascript">

var myvalue = "jjjjj";

function fun1() {

return "function return test";

}

</script>

测试代码:

class WebViewListener extends WebViewClient {

@Override

public void onPageFinished(WebView view ,String url) {

//页面内容载入完成时执行

Toast.makeText(AndroidSampleActivity.this,web.stringByEvaluatingJavaScriptFromString("myvalue"),Toast.LENGTH_SHORT).show();

}

}

代码中的web为MyWebView的对象:

web = (MyWebView)this.findViewById(R.id.webView1);
20.4.2 JNI读取方式

除了采用反射方式能访问到私有类BrowserFrame中的stringByEvaluatingJavaScriptFromString方法之外,采用JNI技术,也能做到;下面我们采用JNI技术来实现20.4.1中的MyWebView类。

原理:java->C->java,具体到这里就是mywebview.java调用bridge.c,bridge.c再调用BrowserFrame.java

MyWebView.java:

package com.example.hellojni;



import android.content.Context;

import android.util.AttributeSet;

import android.webkit.WebView;



public class MyWebView extends WebView {

public MyWebView(Context context) {

super(context);

// TODO Auto-generated constructor stub

}

public MyWebView(Context context, AttributeSet attrs) {

super(context, attrs);

// TODO Auto-generated constructor stub

}

public MyWebView(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

// TODO Auto-generated constructor stub

}



public native String stringByEvaluatingJavaScriptFromString(String script);

static {

System.loadLibrary("bridge");

}

}

bridge.c:

#include <string.h>

#include <jni.h>

#include <dlfcn.h>





jstring

Java_com_example_hellojni_MyWebView_stringByEvaluatingJavaScriptFromString( JNIEnv* env,jobject thiz,jstring script )

{

//---------------------c调用java测试------------------------------------

jstring str = NULL;

//由webview获取webviewcore

jclass class_webview = (*env)->GetObjectClass(env, thiz);

jfieldID fid_WebViewCore = (*env)->GetFieldID(env, class_webview, "mWebViewCore", "Landroid/webkit/WebViewCore;");

jobject obj_WebViewCore = (*env)->GetObjectField(env,thiz, fid_WebViewCore);

//由webviewcore获取webframe

jclass class_webviewcore = (*env)->FindClass(env, "android/webkit/WebViewCore");

jfieldID fid_frame = (*env)->GetFieldID(env, class_webviewcore, "mBrowserFrame", "Landroid/webkit/BrowserFrame;");

jobject obj_frame = (*env)->GetObjectField(env,obj_WebViewCore, fid_frame);

//获取webframe的stringByEvaluatingJavaScriptFromString方法ID

jclass class_webframe = (*env)->FindClass(env, "android/webkit/BrowserFrame");

jmethodID mid = (*env)->GetMethodID(env, class_webframe, "stringByEvaluatingJavaScriptFromString", "(Ljava/lang/String;)Ljava/lang/String;");

if (mid) {

//执行webframe对象的stringByEvaluatingJavaScriptFromString方法

str = (*env)->CallObjectMethod(env, obj_frame, mid, script);

}

//返回执行结果

return str;//(*env)->NewStringUTF(env, str);

//------------------------------------------------------------

}

Android.mk添加如下代码:

#-----------------------定义bridge共享库的编译(c)------------------

LOCAL_CPP_EXTENSION := .c

include $(CLEAR_VARS)

LOCAL_MODULE := bridge

LOCAL_SRC_FILES := bridge.c

include $(BUILD_SHARED_LIBRARY)

*****************************************************************

至此,我们实现了mywebview,并为mywebview定义了本地方法和该本地方法的c语言实现,在c语言的具体实现时,又采用jni技术,调用了private类型的BrowserFrame对象中的native类型的stringByEvaluatingJavaScriptFromString方法。

剩下的就是如何使用mywebview定义布局了(静态或动态),具体实现和20.4.1一样。

注意:jni的实现,可以借助NDK框架来简化开发,具体实现参看22节《JNI和NDK》,本例中采用的就是NDK框架,如果不采用NDK也可实现,但原理不变。

在bridge.c中,注意方法的命名,必须是包名+类名+方法名,类名和包名分别对应定义native方法的类和所在的包名称。

*****************************************************************
20.4.3扩展webkit方式

researching

直接扩展WebCore,扩展JSBridge,实现JS的数据类型到J***A数据类型的转换,确切的说是相互转换,这里面J***A部分可以用反射机制来做到。
20.4.4 Plug方式

Researching
采用插件的方式实现。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: