念念不忘,必有回響,永遠堅持你所堅持的!
一直在用WebView,還沒有系統的總結過它的用法,下面就系統的總結下,分享給大家
一、基本用法
1、加載在線URL
void loadUrl(String url)
這個函數主要加載url所對應的網頁地址,或者用於調用網頁中的指定的JS方法(調用js方法的用法,后面會講),但有一點必須注意的是:loadUrl()必須在主線程中執行!!!否則就會報錯!!!。
注意:加載在線網頁地址是會用到聯網permission權限的,所以需要在AndroidManifest.xml中寫入下面代碼申請權限:
<uses-permission android:name="android.permission.INTERNET" />
本示例效果為:
從效果圖中可以明顯看出本示例的布局: main.xml
<?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:id="@+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="加載URL"/>
<WebView
android:id="@+id/webview"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
對應的處理代碼如下:
public class MyActivity extends Activity {
private WebView mWebView;
private Button mBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView)findViewById(R.id.webview);
mBtn = (Button)findViewById(R.id.btn);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.loadUrl("http://www.baidu.com");
}
});
}
}
代碼很簡單,就是在點擊按鈕的時候加載網址,但需要注意的是:網址必須完整即以http://或者ftp://等協議開頭,不能省略!不然將加載不出來,這是因為webview是沒有自動補全協議功能的,所以如果我們不加,它將識別不出來網址類型,也就加載不出來了。
但如果我們運行上面的代碼,效果卻是利用瀏覽器來打開網址,卻不是使用webview打開網址:
如果我們想實現像示例一樣在webview中打開網址需要怎么做呢?
我們需要設置WebViewClient:
修改后的代碼為:
public class MyActivity extends Activity {
private WebView mWebView;
private Button mBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView)findViewById(R.id.webview);
mBtn = (Button)findViewById(R.id.btn);
mWebView.setWebViewClient(new WebViewClient());
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.loadUrl("http://www.baidu.com");
}
});
}
}
在上面的基礎上,我們添加了下面一段代碼:
mWebView.setWebViewClient(new WebViewClient());
在這里我們利用mWebView.setWebViewClient()函數僅僅設置了一個WebViewClient實例,就可以實現在WebView中打開鏈接了,至於原因我們下篇會講到,這里就先忽略了,大家只需要知道要在WebView中打開鏈接,就必須要設置WebViewClient;
最終的效果圖就與開篇時一樣的了,這里就不再帖效果圖了,下面我們來看看如何加載本地html網頁。
2、加載本地URL
一般而言,我們會將本地html文件放在assets文件夾下,比如:
web.html的內容為:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<h1>歡迎光臨啟艦的blog</h1>
</head>
<body>
</body>
</html>
即大標題顯示一段文字
我們同樣在上面的示例的基礎上加以改造,在點擊按鈕的時候加載本地web.html文件
public class MyActivity extends Activity {
private WebView mWebView;
private Button mBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView)findViewById(R.id.webview);
mBtn = (Button)findViewById(R.id.btn);
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.loadUrl("file:///android_asset/web.html");
}
});
}
}
從這里可以看到與加載在線URL有兩點不同:
- 1、URL類型不一樣
在加載本地URL時,是以“file:///”開頭的,而assets目錄所對應的路徑名為anroid_asset,寫成其它的將識別不了,這是assets目錄的以file開頭的url形式的固定訪問形式。
- 2、不需要設置WebViewClient
這里很明顯沒有設置WebViewClient函數,但仍然是在webview中打開的本地文件。具體原因下篇文章講到WebViewClient時我們會具體解釋。
本例效果圖如下:
所以對於加載URL的總結就是:
- 1、如果是在線網址記得添加網絡訪問權限
- 2、在線網址中,如果要使用webview打開,記得設置WebViewClient
- 3、打開本地html文件時,是不需要設置WebViewClient,對應的asstes目錄的url為:file:///android_asset/xxxxx
3、WebView基本設置
如果我們需要設置WebView的屬性,是通過WebView.getSettings()獲取設置WebView的WebSettings對象,然后調用WebSettings中的方法來實現的。
WebSettings的方法及說明如下:(這里先列出來所有的方法及解釋,大家可以先忽略,看后面的舉例中所使用的幾個常用方法即可,用到哪個函數的時候再回來查查就可以了)
/**
* 是否支持縮放,配合方法setBuiltInZoomControls使用,默認true
*/
setSupportZoom(boolean support)
/**
* 是否需要用戶手勢來播放Media,默認true
*/
setMediaPlaybackRequiresUserGesture(boolean require)
/**
* 是否使用WebView內置的縮放組件,由浮動在窗口上的縮放控制和手勢縮放控制組成,默認false
*/
setBuiltInZoomControls(boolean enabled)
/**
* 是否顯示窗口懸浮的縮放控制,默認true
*/
setDisplayZoomControls(boolean enabled)
/**
* 是否允許訪問WebView內部文件,默認true
*/
setAllowFileAccess(boolean allow)
/**
* 是否允許獲取WebView的內容URL ,可以讓WebView訪問ContentPrivider存儲的內容。 默認true
*/
setAllowContentAccess(boolean allow)
/**
* 是否啟動概述模式瀏覽界面,當頁面寬度超過WebView顯示寬度時,縮小頁面適應WebView。默認false
*/
setLoadWithOverviewMode(boolean overview)
/**
* 是否保存表單數據,默認false
*/
setSaveFormData(boolean save)
/**
* 設置頁面文字縮放百分比,默認100%
*/
setTextZoom(int textZoom)
/**
* 是否支持ViewPort的meta tag屬性,如果頁面有ViewPort meta tag 指定的寬度,則使用meta tag指定的值,否則默認使用寬屏的視圖窗口
*/
setUseWideViewPort(boolean use)
/**
* 是否支持多窗口,如果設置為true ,WebChromeClient#onCreateWindow方法必須被主程序實現,默認false
*/
setSupportMultipleWindows(boolean support)
/**
* 指定WebView的頁面布局顯示形式,調用該方法會引起頁面重繪。默認LayoutAlgorithm#NARROW_COLUMNS
*/
setLayoutAlgorithm(LayoutAlgorithm l)
/**
* 設置標准的字體族,默認”sans-serif”。font-family 規定元素的字體系列。
* font-family 可以把多個字體名稱作為一個“回退”系統來保存。如果瀏覽器不支持第一個字體,
* 則會嘗試下一個。也就是說,font-family 屬性的值是用於某個元素的字體族名稱或/及類族名稱的一個
* 優先表。瀏覽器會使用它可識別的第一個值。
*/
setStandardFontFamily(String font)
/**
* 設置混合字體族。默認”monospace”
*/
setFixedFontFamily(String font)
/**
* 設置SansSerif字體族。默認”sans-serif”
*/
setSansSerifFontFamily(String font)
/**
* 設置SerifFont字體族,默認”sans-serif”
*/
setSerifFontFamily(String font)
/**
* 設置CursiveFont字體族,默認”cursive”
*/
setCursiveFontFamily(String font)
/**
* 設置FantasyFont字體族,默認”fantasy”
*/
setFantasyFontFamily(String font)
/**
* 設置最小字體,默認8. 取值區間[1-72],超過范圍,使用其上限值。
*/
setMinimumFontSize(int size)
/**
* 設置最小邏輯字體,默認8. 取值區間[1-72],超過范圍,使用其上限值。
*/
setMinimumLogicalFontSize(int size)
/**
* 設置默認字體大小,默認16,取值區間[1-72],超過范圍,使用其上限值。
*/
setDefaultFontSize(int size)
/**
* 設置默認填充字體大小,默認16,取值區間[1-72],超過范圍,使用其上限值。
*/
setDefaultFixedFontSize(int size)
/**
* 設置是否加載圖片資源,注意:方法控制所有的資源圖片顯示,包括嵌入的本地圖片資源。
* 使用方法setBlockNetworkImage則只限制網絡資源圖片的顯示。值設置為true后,
* webview會自動加載網絡圖片。默認true
*/
setLoadsImagesAutomatically(boolean flag)
/**
* 是否加載網絡圖片資源。注意如果getLoadsImagesAutomatically返回false,則該方法沒有效果。
* 如果使用setBlockNetworkLoads設置為false,該方法設置為false,也不會顯示網絡圖片。
* 當值從true改為false時。WebView會自動加載網絡圖片。
*/
setBlockNetworkImage(boolean flag)
/**
* 設置是否加載網絡資源。注意如果值從true切換為false后,WebView不會自動加載,
* 除非調用WebView#reload().如果沒有android.Manifest.permission#INTERNET權限,
* 值設為false,則會拋出java.lang.SecurityException異常。
* 默認值:有android.Manifest.permission#INTERNET權限時為false,其他為true。
*/
setBlockNetworkLoads(boolean flag)
/**
* 設置是否允許執行JS。
*/
setJavaScriptEnabled(boolean flag)
/**
* 是否允許Js訪問任何來源的內容。包括訪問file scheme的URLs。考慮到安全性,
* 限制Js訪問范圍默認禁用。注意:該方法只影響file scheme類型的資源,其他類型資源如圖片類型的,
* 不會受到影響。ICE_CREAM_SANDWICH_MR1版本以及以下默認為true,JELLY_BEAN版本
* 以上默認為false
*/
setAllowUniversalAccessFromFileURLs(boolean flag)
/**
* 是否允許Js訪問其他file scheme的URLs。包括訪問file scheme的資源。考慮到安全性,
* 限制Js訪問范圍默認禁用。注意:該方法只影響file scheme類型的資源,其他類型資源如圖片類型的,
* 不會受到影響。如果getAllowUniversalAccessFromFileURLs為true,則該方法被忽略。
* ICE_CREAM_SANDWICH_MR1版本以及以下默認為true,JELLY_BEAN版本以上默認為false
*/
setAllowFileAccessFromFileURLs(boolean flag)
/**
* 設置存儲定位數據庫的位置,考慮到位置權限和持久化Cache緩存,Application需要擁有指定路徑的
* write權限
*/
setGeolocationDatabasePath(String databasePath)
/**
* 是否允許Cache,默認false。考慮需要存儲緩存,應該為緩存指定存儲路徑setAppCachePath
*/
setAppCacheEnabled(boolean flag)
/**
* 設置Cache API緩存路徑。為了保證可以訪問Cache,Application需要擁有指定路徑的write權限。
* 該方法應該只調用一次,多次調用自動忽略。
*/
setAppCachePath(String appCachePath)
/**
* 是否允許數據庫存儲。默認false。查看setDatabasePath API 如何正確設置數據庫存儲。
* 該設置擁有全局特性,同一進程所有WebView實例共用同一配置。注意:保證在同一進程的任一WebView
* 加載頁面之前修改該屬性,因為在這之后設置WebView可能會忽略該配置
*/
setDatabaseEnabled(boolean flag)
/**
* 是否存儲頁面DOM結構,默認false。
*/
setDomStorageEnabled(boolean flag)
/**
* 是否允許定位,默認true。注意:為了保證定位可以使用,要保證以下幾點:
* Application 需要有android.Manifest.permission#ACCESS_COARSE_LOCATION的權限
* Application 需要實現WebChromeClient#onGeolocationPermissionsShowPrompt的回調,
* 接收Js定位請求訪問地理位置的通知
*/
setGeolocationEnabled(boolean flag)
/**
* 是否允許JS自動打開窗口。默認false
*/
setJavaScriptCanOpenWindowsAutomatically(boolean flag)
/**
* 設置頁面的編碼格式,默認UTF-8
*/
setDefaultTextEncodingName(String encoding)
/**
* 設置WebView代理,默認使用默認值
*/
setUserAgentString(String ua)
/**
* 通知WebView是否需要設置一個節點獲取焦點當
* WebView#requestFocus(int,android.graphics.Rect)被調用的時候,默認true
*/
setNeedInitialFocus(boolean flag)
/**
* 基於WebView導航的類型使用緩存:正常頁面加載會加載緩存並按需判斷內容是否需要重新驗證。
* 如果是頁面返回,頁面內容不會重新加載,直接從緩存中恢復。setCacheMode允許客戶端根據指定的模式來
* 使用緩存。
* LOAD_DEFAULT 默認加載方式
* LOAD_CACHE_ELSE_NETWORK 按網絡情況使用緩存
* LOAD_NO_CACHE 不使用緩存
* LOAD_CACHE_ONLY 只使用緩存
*/
setCacheMode(int mode)
/**
* 設置加載不安全資源的WebView加載行為。KITKAT版本以及以下默認為MIXED_CONTENT_ALWAYS_ALLOW方
* 式,LOLLIPOP默認MIXED_CONTENT_NEVER_ALLOW。強烈建議:使用MIXED_CONTENT_NEVER_ALLOW
*/
setMixedContentMode(int mode)
下面我們就舉個例子來看下用法
示例1:在WebView中啟用JavaScript:
public class MyActivity extends Activity {
private WebView mWebView;
private Button mBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
mBtn = (Button) findViewById(R.id.btn);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
}
}
示例2:設置緩存
優先使用緩存
webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
不使用緩存:
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
示例3:打開頁面時, 自適應屏幕:
WebSettings webSettings = mWebView .getSettings();
webSettings.setUseWideViewPort(true);//設置此屬性,可任意比例縮放
webSettings.setLoadWithOverviewMode(true);
效果圖如下:(所使用的網址為:http://www.w3school.com.cn/)
注意:對於我們自己寫的網頁代碼,不必利用這處函數來做頁面縮放適配,因為對於有些手機存在適配問題,只需要在HTML中做寬度100%自適應屏幕就行了
示例4:使頁面支持縮放:
WebSettings webSettings = mWebView.getSettings();
//開啟javascript支持
webSettings.setJavaScriptEnabled(true);
// 設置可以支持縮放
webSettings.setSupportZoom(true);
// 設置出現縮放工具
webSettings.setBuiltInZoomControls(true);
示例5:.如果webView中需要用戶手動輸入用戶名、密碼或其他,則webview必須設置支持獲取手勢焦點
webview.requestFocusFromTouch();
其它的設置就靠大家自己去嘗試啦,這里就不再一一綴述了,大家只需要記着,如果需要設置webview就從WebSettings里面去找就可以啦。
二、JS調用Java代碼
在看了如何設置webview以后,我們來看下如何讓Webview與網頁中的JS代碼交互的問題。
1、概述
更多時候,網頁中需要通過JS代碼來調用本地的Android代碼,比如H5頁面需要判斷當前用戶是否登錄等。
利用JS代碼調用JAVA代碼,主要是用到WebView下面的一個函數:
public void addJavascriptInterface(Object obj, String interfaceName)
這個函數有兩個參數:
- Object obj:interfaceName所綁定的對象
- String interfaceName:所綁定的對象所對應的名稱
它有意義就是向WebView注入一個interfaceName的對象,這個對象綁定的是obj對象,通過interfaceName就可以調用obj對象中的方法,這個表述可能大家不太理解,因為interfaceName是一個String,怎么被你說成對象了,理解不了沒關系,下面有具體示例
2、示例
下面同樣是上面的示例,我們對它加以更改,效果圖如下:
在原來html上面添加了一個按鈕,當點擊按鈕時調用Android的Toast函數彈出一個toast消息。
先看Android代碼:
public class MyActivity extends Activity {
private WebView mWebView;
private Button mBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(this, "android");
mWebView.loadUrl("file:///android_asset/web.html");
}
public void toastMessage(String message) {
Toast.makeText(getApplicationContext(), "通過Natvie傳遞的Toast:"+message, Toast.LENGTH_LONG).show();
}
}
這里最主要是的下面這句:
mWebView.addJavascriptInterface(this, "android");
這句的意思是把MyActivity對象注入到WebView中,在WebView中的對象別名叫android;另外,我們還在MyActivity中額外寫了一個函數toastMessage(String message),用於彈出MSG
下面我們看看html代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<h1>歡迎光臨啟艦的blog</h1>
<input type="button" value="js調native" onclick="ok()">
</head>
<body>
<script type="text/javascript">
function ok() {
android.toastMessage("哈哈,i m webview msg");
}
</script>
</body>
</html>
在這個html中,我添加了一個button按鈕,當點擊時調用ok函數:
function ok() {
android.toastMessage("哈哈,i m webview msg");
}
它的意義就是調用android對象里的toastMessage方法,這個android就是我們利用mWebView.addJavascriptInterface(this, “android”)注入到WebView的android,它所對應的對象就將MyActivity;所以在JS中,我們就可以通過android這個別名來調用MyActivity對象中的任何public方法。
3、addJavascriptInterface自定義作用對象
在上面的示例中mWebView.addJavascriptInterface(this, “android”);我們直接通過this,把當前整個類作為對象傳給WebView了,但這會有很大風險,因為我們這個類中可能會有各種函數,而這些函數是只有本地Native代碼才會用到,WebView是根本用不到的。所以如果通過全部注入給WebView的話,那么一些存心不良的同學就可以任意調用我們這個類中的方法,給我們APP帶來危害。
所以,一般而言,我們很少直接會傳this,把整個類注入給WebView,而是單獨寫一個類專門用於JS交互,比如:
public class MyActivity extends Activity {
private WebView mWebView;
private Button mBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
mBtn = (Button) findViewById(R.id.btn);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new JSBridge(), "android");
mWebView.loadUrl("file:///android_asset/web.html");
}
public class JSBridge{
public void toastMessage(String message) {
Toast.makeText(getApplicationContext(), "通過Natvie傳遞的Toast:"+message, Toast.LENGTH_LONG).show();
}
}
}
在這段代碼中,在通過addJavascriptInterface注入時:
mWebView.addJavascriptInterface(new JSBridge(), "android");
指定android對象綁定的是JSBridge對象!所以在WebView中,通過JS只能訪問JSBridge中所定義的對象,如果訪問其它類的函數,比如MyActivity中的函數,就會報下面的錯誤(即方法找不到)
大家可以自己嘗試下;
然后對應的html代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<h1>歡迎光臨啟艦的blog</h1>
<input type="button" value="js調native" onclick="ok()">
</head>
<body>
<script type="text/javascript">
function ok() {
android.toastMessage("哈哈,i m webview msg");
}
</script>
</body>
</html>
由於在注入時的對象別名和所調用的函數名都沒有變,所以HTML中的JS代碼是無需更改的。效果圖與上節一樣,就不再帖出了。
4、addJavascriptInterface注入漏洞
上面我們說了在addJavascriptInterface注入時,為了防止WebView調用我們不想被它調用的函數,所以我們需要單獨為WebView交互定義一個類,讓它只執行這個類里面的函數
但……這真的能擋住黑客的攻擊嗎?
當然是NO……,不然我就不會寫這一段了……
mWebView.addJavascriptInterface(new JSBridge(), "android");
在注入時,我們已經把對象傳給了JS,在JS中當然可以通過反射得到APP中的各種類的實例!現在反編譯Android代碼可不是什么難事(本文結尾附jadx反編譯方法),很容易拿到你有哪些類,有哪些函數,通過這些就可以想執行哪個執行哪個了,有沒有細思極恐……
具體的細節我就不講了,不在本篇范圍,給大家找了篇文章,有興趣的同學可以參考下:《Android WebView的Js對象注入漏洞解決方案》
5、JavascriptInterface注解
為了解決addJavascriptInterface()函數的安全問題,在android:targetSdkVersion數值為17(Android4.2)及以上的APP中,JS只能訪問帶有 @JavascriptInterface注解的Java函數,所以如果你的android:targetSdkVersion是17+,與JS交互的Native函數中,必須添加JavascriptInterface注解,不然無效,比如:
public class JSBridge {
@JavascriptInterface
public void toastMessage(String message) {
Toast.makeText(getApplicationContext(), "通過Natvie傳遞的Toast:" + message, Toast.LENGTH_LONG).show();
}
}
這也就是很多同學在高target上,addJavascriptInterface()函數無效的主要原因。
注意:雖然在target 17以后,已經修復了這個安全問題,但目前大多數APP都還是target 17以前的,所以大家可以嘗試着找一些APP來演示下這個漏洞哦……
三、JAVA調用JS代碼
1、JAVA調用JS代碼
前面給大家演示了如何通過JS調用Java代碼,這里就反過來看看,如何在Native中調用JS的代碼
本例的效果圖如下:
在點擊“加載URL”按鈕時,調用webview中的JavaScript求和函數,將結果顯示在標題中。
先看html代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<h1 id="h">歡迎光臨啟艦的blog</h1>
<input type="button" value="js調native" onclick="ok()">
</head>
<body>
<script type="text/javascript">
function sum(i,m)
{
document.getElementById("h").innerHTML= (i+m);
}
</script>
</body>
</html>
在這里,我們寫了一個求和函數sum(i,m)
結果中就是把h1標簽的內容改為求和后的結果值。
再來看看JAVA的調用代碼:
public class MyActivity extends Activity {
private WebView mWebView;
private Button mBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
mBtn = (Button) findViewById(R.id.btn);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.loadUrl("file:///android_asset/web.html");
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.loadUrl("javascript:sum(3,8)");
}
});
}
}
在點擊按鈕時調用JS函數:
mWebView.loadUrl("javascript:sum(3,8)");
這里也就是在JAVA中調用JS函數的方法:
String url = "javascript:methodName(params……);"
webView.loadUrl(url);
javascript:偽協議讓我們可以通過一個鏈接來調用JavaScript函數
中間methodName是JavaScript中實現的函數
jsonParams是傳入的參數列表
使用起來難度不大,就不再多講了
2、JAVA中如何得到JS中的返回值(Android4.4以前)
現在我們再考慮一下,如果我們要在JAVA中需要得到JS的結果返回值要怎么辦?比如在上面的例子中,我們需要在JAVA中得到在計算后的結果值
Android在4.4之前並沒有提供直接調用js函數並獲取值的方法,也就是說,我們只能調用JS中的函數,並不能得到該函數的返回值,想得到返回值我們就得想其它辦法,所以在此之前,常用的思路是 java調用js方法,js方法執行完畢,再次調用java代碼將值返回。
- 1.Java調用js代碼
webView.addJavascriptInterface(this, "android");
mWebView.loadUrl("javascript:sum(3,8)");
注意,這里通過addJavascriptInterface將MyActiviy所對應的對象注入到WebView中了。
- 2.js函數處理,並將結果通過調用java方法返回
function sum(i,m){
var result = i+m;
document.getElementById("h").innerHTML= result;
android.onSumResult(result)
}
- 3..Java在回調方法中獲取js函數返回值
public void onSumResult(int result) {
Log.i(LOGTAG, "onSumResult result=" + result);
}
先看下效果圖:
下面我們就完整地看一下代碼:
JS代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<h1 id="h">歡迎光臨啟艦的blog</h1>
<input type="button" value="js調native" onclick="ok()">
</head>
<body>
<script type="text/javascript">
function sum(i,m){
var result = i+m;
document.getElementById("h").innerHTML= result;
android.onSumResult(result);
}
</script>
</body>
</html>
在function sum(i,m)中,先通過result得到結果,最后通過android.onSumResult(result);將結果傳給Native
然后再來看看JAVA代碼:
public class MyActivity extends Activity {
private WebView mWebView;
private Button mBtn;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mWebView = (WebView) findViewById(R.id.webview);
mBtn = (Button) findViewById(R.id.btn);
WebSettings webSettings = mWebView.getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(this, "android");
mWebView.loadUrl("file:///android_asset/web.html");
mBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mWebView.loadUrl("javascript:sum(3,8)");
}
});
}
public void onSumResult(int result) {
Toast.makeText(this,"received result:"+result,Toast.LENGTH_SHORT).show();
}
}
這里主要做了兩件事:
- 第一,通過addJavascriptInterface注入MyActivity對象,以便JS訪問其中的函數
mWebView.addJavascriptInterface(this, "android");
- 第二:供JS調用,以返回結果的函數onSumResult():
public void onSumResult(int result) { Toast.makeText(this,"received result:"+result,Toast.LENGTH_SHORT).show(); }
- 3、JAVA中如何得到JS中的返回值(Android4.4之后)
Android 4.4之后使用evaluateJavascript即可。這里展示一個簡單的交互示例
先寫一個具有返回值的js方法
function getGreetings() {
return 1;
}
java代碼時用evaluateJavascript方法調用:
private void testEvaluateJavascript(WebView webView) {
webView.evaluateJavascript("getGreetings()", new ValueCallback() {
@Override
public void onReceiveValue(String value) {
Log.i(LOGTAG, "onReceiveValue value=" + value);
}
});
}
從上面的用法中很明顯看到,通過evaluateJavascript調用JS中的方法,可以向其中添加結果回調,來接收JS的return值。
注意:
- 上面限定了結果返回結果為String,對於簡單的類型會嘗試轉換成字符串返回,對於復雜的數據類型,建議以字符串形式的json返回。
- evaluateJavascript方法必須在UI線程(主線程)調用,因此onReceiveValue也執行在主線程。
好了,這篇文章中有關WebView的知識就先到這,下篇繼續講。下面給大家講講有關jadx-gui反編譯的知識。
四、jadx-gui反編譯
在遇到jadx-gui反編譯之前,都是使用apktools進行反編譯的,apktools有些是反編譯不出來的,而且……難用……
想知道jadx-gui有多強?它都可以反編譯淘寶、微信代碼,是不是夠強了。下面我們就來看看它是如何反編譯的;
1、下載、配置
jadx-gui是開源的,項目地址:《https://github.com/skylot/jadx》
mac電腦:
打開終端,切到某個路徑下,輸入以下命令:
git clone https://github.com/skylot/jadx.git
cd jadx
./gradlew dist
其實這里只是做了兩個動作:
第一,使用git命令將 項目clone下來(這里需要配置git環境,如果沒有,請先搜資料配置git環境,然后再來)
然后,執行jadx目錄 下gradlew腳本,這個是shell腳本
windows電腦:
git clone https://github.com/skylot/jadx.git
cd jadx
gradlew.bat dist
在windows電腦中,步驟與mac是一樣的,只是最后一步中,已經不再是./gradlew所對應的shell腳本了,而是windows平台上的bat腳本。
可見作者有多牛X,shell腳本和bat腳本都會寫,真是屌的不能直視
整個編譯過程是比較慢的,這里需要耐心等待下。因為目前會使用gradle 2.7來編譯項目,如果沒有在環境變量中環境gradle 2.7的環境變量,會自己下載gradle 2.7
編譯成功后會打出BUILD SUCCESS字樣,如下圖所示:
在編譯成功后,在jadx目錄下,會生成一個build目錄,其中包含jadx目錄和一個jadx-xxx-dev.zip的打包文件。在build/jadx目錄下,就是源碼編譯出的jadx工具及所用jar包。jadx-xxx-dev.zip解壓后的內容與build/jadx內容一樣,只是將其打包了一下而已,方便移值,可見作者有多用心。build目錄結構如下圖所示:
2、開始反編譯
等完畢后,可以開始了,我就介紹個最簡單最常用的用法
- 把apk改成zip
- 解壓zip獲取class.dex文件
- 將class.dex文件放到jadx目錄下
cd build/jadx/
bin/jadx -d out classes.dex # 反編譯后放入out文件夾下(如果out不存在它會自動創建)
#or
bin/jadx-gui classes.dex # 會反編譯,並且使用gui打開
目錄結構圖如下:
在使用jadx-gui反編譯時,左下角會顯示當前反編譯的進度:
在反編譯完成后,在左側就可以看到目錄結構和對應的代碼,目錄結構中顯示的a,b,c,d這些字母是由於在生成apk時使用proguard混淆造成的,proguard混淆的類名是沒辦法反編譯出對應的原類名的,這也是反編譯代碼中比較蛋疼的地方,下面給大家演示下結果:(還可以通過點擊搜索按鈕,搜索其中的代碼)
有關jadx-gui工具的更多用法,只有靠大家自己去研究啦,到這里我們的源碼就已經反編譯出來了。
源碼下載地址:http://download.csdn.net/detail/harvic880925/9526801
本文轉載自 【啟艦】博客 如需轉載請聯系博主!
原文鏈接:http://blog.csdn.net/harvic880925/article/details/51464687