今回は Android と HTML の混合開発の使い方と、 Android と HTML の混合開発を使用する際の注意点について説明します。 以下は実際のケースですので、見てみましょう。
現在、多くのアプリには、頻繁に変更される HTML5 ページが埋め込まれています。一部のページでは、HTML5 での js とのやり取りにネイティブ Java が必要です。ここでは、Android での HTML5 の使用について紹介します。 HTML5 Cookie
ユーザー情報などの多くのパラメーターが Web ページで使用される場合があります。次の方法を使用して、この情報を Cookie に事前に含めることができます。 Web ページは、Cookie を通じて対応するパラメータ値を取得できます。
2. jsのセキュリティ問題について
jsには4.2以前に脆弱性がありましたJavaScriptを介して、連絡先情報やテキストメッセージなども含め、現在のデバイスのSDカードにあるものにアクセスできます。さて、このエラーがどのように発生したかを見てみましょう。 1. WebView には JavaScript オブジェクトが追加されており、現在のアプリケーションには SDCard の読み取りと書き込みの権限があります。つまり、android.permission.WRITE_EXTERNAL_STORAGE
2. JS では、ウィンドウ オブジェクトを走査してオブジェクトを見つけることができます。次に、リフレクション メカニズムを使用して Runtime オブジェクトを取得し、ファイルにアクセスするコマンドなどのいくつかのコマンドを実行します。
3.コマンドを実行するとファイル名情報が取得できます。そうなるとやりたい放題になってしまい、とても危険です。コアの JS コードは次のとおりです。
public static void addCookies(Context context, WebView webView, String url) { String url=“https://www.xxxx.com/xx/xx/” String protocol = ""; String authority = ""; try { URL urlObj = new URL(url); protocol = urlObj.getProtocol(); authority = urlObj.getAuthority(); } catch (Exception e) { e.printStackTrace(); } String ua = webView.getSettings().getUserAgentString(); webView.getSettings().setUserAgentString(Constant.PROJECT_NAME + "/" + ParamHandler.getVersion(context) + "(" + ua + "; HFWSH)"); if (!TextUtils.isEmpty(url) && !TextUtils.isEmpty(protocol) && !TextUtils.isEmpty(authority)) { if (protocol.equals("https") && authority.indexOf("liepin.com") > -1) { CookieSyncManager.createInstance(context); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.setAcceptCookie(true); try { List<String> data = getCookiesString(); if (!ListUtils.isEmpty(data)) { for (String value : data) { cookieManager.setCookie(url, value); } } cookieManager.setCookie(url, "client_id=" + Constant.CLIENT_ID + ";path=/;domain=.XXXX.com"); cookieManager.setCookie(url, "appVersion=" + Constant .VERSION + ";path=/;domain=.XXXX.com"); CookieSyncManager.getInstance().sync(); } catch (Exception e) { LogUtils.e("Exception:" + e.getMessage()); } } } }
Android 4.2 以降では、Google は Java リモート メソッドで @JavascriptInterface を宣言することで修正を加えました。上記のコード:
public List<String> getCookiesString() { ArrayList data = new ArrayList(); this.clearExpired(); Collection values = this.mCookies.values(); Iterator var3 = values.iterator(); while(var3.hasNext()) { SwiftCookie c = (SwiftCookie)var3.next(); data.add(c.toCookieString()); } return data; }
2、Android 4.2 未満のシステム用
この問題は解決がより困難ですが、解決することが不可能ではありません。
まず第一に、addJavascriptInterface メソッドを呼び出すことはできなくなりました。この問題に関して、最も重要なことは、JS イベントのアクションを知ることです。プロンプト、アラートなど、JS と Java 間の対話には次のタイプがあることがわかっています。そのようなアクションは、対応するメソッドに対応します。 WebChromeClient クラスでは、プロンプトの対応するメソッドは onJsPrompt メソッドです。このメソッドの宣言は次のとおりです。
function execute(cmdArgs) { for (var obj in window) { if ("getClass" in window[obj]) { alert(obj); return window[obj].getClass().forName("java.lang.Runtime") .getMethod("getRuntime",null).invoke(null,null).exec(cmdArgs); } } }
このメソッドを通じて、JS は情報 (テキスト) を Java に転送でき、Java も情報 (テキスト) を転送できます。 ) を JS に通知して、このアイデアに対する解決策を見つけることができますか?
いくつかの試行と分析の結果、より実現可能な解決策が見つかりました。以下のいくつかの点をご覧ください:
[1] JS に Javascript メソッドを呼び出します。このメソッドでは、prompt メソッドが呼び出され、JS がロードされます。プロンプトを通じて情報が渡されます。これは、特定の識別子、メソッド名、パラメータなどを含む、意味のあるテキストを組み合わせたものである必要があります。
onJsPrompt メソッドでは、渡されたテキストを解析し、メソッド名、パラメータなどを取得し、リフレクション機構を通じて指定されたメソッドを呼び出し、それによって Java オブジェクトのメソッドを呼び出します。
【2】戻り値はプロンプトで返すことができるので、Javaでのメソッドの処理結果をJsに返すことができます。
【3】JavaScript メソッドを宣言する JS スクリプトを動的に生成し、loadUrl を介してロードし、HTML ページに登録する必要があります。具体的なコードは次のとおりです。
class JsObject { @JavascriptInterface public String toString() { return "injectedObject"; } } webView.addJavascriptInterface(new JsObject(), "injectedObject"); webView.loadData("", "text/html", null); webView.loadUrl("javascript:alert(injectedObject.toString())");
手順:
1.上記コードの は登録するオブジェクトの名前です。onButtonClick(arg0)とonImageClick(arg0, arg1, arg2)の2つのメソッドを登録します。戻り値がある場合はreturnを追加します。
2. プロンプトは、特定の識別子 MyApp: と、それに続くメソッド名、パラメーター、オブジェクト名などの JSON 文字列を含む文字列です。
3. JS が onButtonClick または onImageClick を呼び出すと、Java レイヤーの onJsPrompt メソッドがコールバックされ、メソッド名、パラメーター、オブジェクト名が解析され、メソッドがリフレクション的に呼び出されます。
4. window.jsInterface は、Js オブジェクトがウィンドウ上で宣言されることを意味します。 宣言されたメソッドの形式は次のとおりです: メソッド名: 関数 (パラメーター 1、パラメーター 2)
3. html5 における Java と js の相互作用1)、方法 1:mWebView.getSettings().setJavaScriptEnabled(true); mWebView.addJavascriptInterface(this, "xxx");
然后在当前类中实现以下方法:
@JavascriptInterface public void callbackFromH5(final String j) { //TODO }
callbackFromH5的名字必须和网页中的js方法名一样
Java调用js方法:
mWebView.loadUrl(String.format("javascript:java2js(0)"));//这里是java端调用webview的JS
js方法名需要和网页端一直
2)方法二:
jsbridge方法(https://github.com/lzyzsd/JsBridge)
Android JsBridge 就是用来在 Android app的原生 java 代码与 javascript 代码中架设通信(调用)桥梁的辅助工具
1 将jsBridge.jar引入到我们的工程
Android Studio:
repositories { // ... maven { url "https://jitpack.io" } } dependencies { compile 'com.github.lzyzsd:jsbridge:1.0.4' }
2、布局文件
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <!-- button 演示Java调用web --> <Button android:id="@+id/button" android:layout_width="match_parent" android:text="@string/button_name" android:layout_height="dp" /> <!-- webview 演示web调用Java --> <com.github.lzyzsd.jsbridge.BridgeWebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent" > </com.github.lzyzsd.jsbridge.BridgeWebView> </LinearLayout>
3、java代码
//加载服务器网页 webView.loadUrl("https://www.baidu.com"); //必须和js同名函数。 webView.registerHandler("submitFromWeb", new BridgeHandler() { @Override public void handler(String data, CallBackFunction function) { String str ="html返回给java的数据:" + data; makeText(MainActivity.this, str, LENGTH_SHORT).show(); Log.i(TAG, "handler = submitFromWeb, data from web = " + data); function.onCallBack( str + ",Java经过处理后:"+ str.substring(,)); } }); //模拟用户获取本地位置 User user = new User(); Location location = new Location(); location.address = "xxx"; user.location = location; user.name = "Bruce"; webView.callHandler("functionInJs", new Gson().toJson(user), new CallBackFunction() { @Override public void onCallBack(String data) { makeText(MainActivity.this, "网页在获取你的信息", LENGTH_SHORT).show(); } }); webView.send("hello");
webView.callHandler("functionInJs", "data from Java", new CallBackFunction() { @Override public void onCallBack(String data) { // TODO Auto-generated method stub Log.i(TAG, "reponse data from js " + data); } });
js调用
var str1 = document.getElementById("text1").value; var str2 = document.getElementById("text2").value; //调用本地java方法 window.WebViewJavascriptBridge.callHandler( 'submitFromWeb' , {'param': str} , function(responseData) { document.getElementById("show").innerHTML = "send get responseData from java, data = " + responseData } ); //注册事件监听 document.addEventListener( 'WebViewJavascriptBridgeReady' , function() { callback(WebViewJavascriptBridge) }, false ); //注册回调函数,第一次连接时调用 初始化函数 connectWebViewJavascriptBridge(function(bridge) { bridge.init(function(message, responseCallback) { console.log('JS got a message', message); var data = { 'Javascript Responds': 'Wee!' }; console.log('JS responding with', data); responseCallback(data); }); bridge.registerHandler("functionInJs", function(data, responseCallback) { document.getElementById("show").innerHTML = ("data from Java: = " + data); var responseData = "Javascript Says Right back aka!"; responseCallback(responseData); }); })
4、关于webView的优化
1、设置WebView 缓存模式
private void initWebView() { mWebView.getSettings().setJavaScriptEnabled(true); mWebView.getSettings().setRenderPriority(RenderPriority.HIGH); mWebView.getSettings().setCacheMode(WebSettings.LOAD_DEFAULT); //设置 缓存模式 // 开启 DOM storage API 功能 mWebView.getSettings().setDomStorageEnabled(true); //开启 database storage API 功能 mWebView.getSettings().setDatabaseEnabled(true); String cacheDirPath = getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME; // String cacheDirPath = getCacheDir().getAbsolutePath()+Constant.APP_DB_DIRNAME; Log.i(TAG, "cacheDirPath="+cacheDirPath); //设置数据库缓存路径 mWebView.getSettings().setDatabasePath(cacheDirPath); //设置 Application Caches 缓存目录 mWebView.getSettings().setAppCachePath(cacheDirPath); //开启 Application Caches 功能 mWebView.getSettings().setAppCacheEnabled(true);
2、清除缓存
/** * 清除WebView缓存 */ public void clearWebViewCache(){ //清理Webview缓存数据库 try { deleteDatabase("webview.db"); deleteDatabase("webviewCache.db"); } catch (Exception e) { e.printStackTrace(); } //WebView 缓存文件 File appCacheDir = new File(getFilesDir().getAbsolutePath()+APP_CACAHE_DIRNAME); Log.e(TAG, "appCacheDir path="+appCacheDir.getAbsolutePath()); File webviewCacheDir = new File(getCacheDir().getAbsolutePath()+"/webviewCache"); Log.e(TAG, "webviewCacheDir path="+webviewCacheDir.getAbsolutePath()); //删除webview 缓存目录 if(webviewCacheDir.exists()){ deleteFile(webviewCacheDir); } //删除webview 缓存 缓存目录 if(appCacheDir.exists()){ deleteFile(appCacheDir); } }
3、在使用WebView加载网页的时候,有一些固定的资源文件如js/css/图片等资源会比较大,如果直接从网络加载会导致页面加载的比较慢,而且会消耗比较多的流量。所以这些文件应该放在assets里面同app打包。
解决这个问题用到API 11(HONEYCOMB)提供的shouldInterceptRequest(WebView view, String url) 函数来加载本地资源。
API 21又将这个方法弃用了,是重载一个新的shouldInterceptRequest,需要的参数中将url替换成了成了request。
比如有一个图片xxxxx.png,这个图片已经放在了assets中,现在加载了一个外部html,就需要直接把assets里面的图片拿出来加载而不需要重新从网络获取。当然可以在html里面将图片链接换成file:///android_asset/xxxxx.png,
但是这样这个html就不能在Android ,ios,WAP中公用了。
webView.setWebViewClient(new WebViewClient() { @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { WebResourceResponse response = null; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){ response = super.shouldInterceptRequest(view,url); if (url.contains("xxxxx.png")){ try { response = new WebResourceResponse("image/png","UTF-8",getAssets().open("xxxxx.png")); } catch (IOException e) { e.printStackTrace(); } } } // return super.shouldInterceptRequest(view, url); return response; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) @Override public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) { WebResourceResponse response = null; response = super.shouldInterceptRequest(view, request); if (url.contains("xxxxx.png")){ try { response = new WebResourceResponse("image/png","UTF-",getAssets().open("xxxxx.png")); } catch (IOException e) { e.printStackTrace(); } } return response; } }
相信看了本文案例你已经掌握了方法,更多精彩请关注php中文网其它相关文章!
推荐阅读:
以上がAndroidとHTMLの混合開発の使い方の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。