Android WebView使用方法詳解 附j(luò)s交互調(diào)用方法
目前很多Android app都內(nèi)置了可以顯示web頁面的界面,會發(fā)現(xiàn)這個界面一般都是由一個叫做WebView的組件渲染出來的,學(xué)習(xí)該組件可以為你的app開發(fā)提升擴(kuò)展性。
先說下WebView的一些優(yōu)點(diǎn):
--可以直接顯示和渲染web頁面,直接顯示網(wǎng)頁
--webview可以直接用html文件(網(wǎng)絡(luò)上或本地assets中)作布局
--和JavaScript交互調(diào)用
一、基本使用
首先layout中即為一個基本的簡單控件:
<WebView android:id="@+id/webView1" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginTop="10dp" />
同時,因?yàn)橐吭L問網(wǎng)絡(luò),所以manifest中必須要加uses-permission:
<uses-permission android:name="android.permission.INTERNET"/>
在activity中即可獲得webview的引用,同時load一個網(wǎng)址:
webview = (WebView) findViewById(R.id.webView1); webview.loadUrl("http://www.baidu.com/"); //webview.reload();// reload page
這個時候發(fā)現(xiàn)一個問題,啟動應(yīng)用后,自動的打開了系統(tǒng)內(nèi)置的瀏覽器,解決這個問題需要為webview設(shè)置 WebViewClient,并重寫方法:
webview.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } });
若自己定義了一個頁面加載進(jìn)度的progressbar,需要展示給用戶的時候,可以通過如下方式獲取webview內(nèi)頁面的加載進(jìn)度:
webview.setWebChromeClient(new WebChromeClient(){ @Override public void onProgressChanged(WebView view, int newProgress) { //get the newProgress and refresh progress bar } });
每個頁面,都有一個標(biāo)題,比如www.baidu.com這個頁面的title即“百度一下,你就知道”,那么如何知道當(dāng)前webview正在加載的頁面的title呢:
webview.setWebChromeClient(new WebChromeClient(){ @Override public void onReceivedTitle(WebView view, String title) { titleview.setText(title);//a textview } });
二、通過webview控件下載文件
通常webview渲染的界面中含有可以下載文件的鏈接,點(diǎn)擊該鏈接后,應(yīng)該開始執(zhí)行下載的操作并保存文件到本地中。webview來下載頁面中的文件通常有兩種方式:
1. 自己通過一個線程寫java io的代碼來下載和保存文件(可控性好)
2. 調(diào)用系統(tǒng)download的模塊(代碼簡單)
方法一:
首先要寫一個下載并保存文件的線程類
public class HttpThread extends Thread { private String mUrl; public HttpThread(String mUrl) { this.mUrl = mUrl; } @Override public void run() { URL url; try { url = new URL(mUrl); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setDoInput(true); conn.setDoOutput(true); InputStream in = conn.getInputStream(); File downloadFile; File sdFile; FileOutputStream out = null; if(Environment.getExternalStorageState().equals(Environment.MEDIA_UNMOUNTED)){ downloadFile = Environment.getExternalStorageDirectory(); sdFile = new File(downloadFile, "test.file"); out = new FileOutputStream(sdFile); } //buffer 4k byte[] buffer = new byte[1024 * 4]; int len = 0; while((len = in.read(buffer)) != -1){ if(out != null) out.write(buffer, 0, len); } //close resource if(out != null) out.close(); if(in != null){ in.close(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
隨后要實(shí)現(xiàn)一個DownloadListener接口,這個接口實(shí)現(xiàn)方法OnDownloadStart(),當(dāng)用戶點(diǎn)擊一個可以下載的鏈接時,該回調(diào)方法被調(diào)用同時傳進(jìn)來該鏈接的URL,隨后即可以對該URL塞入HttpThread進(jìn)行下載操作:
//創(chuàng)建DownloadListener (webkit包) class MyDownloadListenter implements DownloadListener{ @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { System.out.println("url ==== >" + url); new HttpThread(url).start(); } } //給webview加入監(jiān)聽 webview.setDownloadListener(new MyDownloadListenter());
方法二:
直接發(fā)送一個action_view的intent即可:
class MyDownloadListenter implements DownloadListener{ @Override public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) { System.out.println("url ==== >" + url); //new HttpThread(url).start(); Uri uri = Uri.parse(url); Intent intent = new Intent(Intent.ACTION_VIEW, uri); startActivity(intent); } }
三、錯誤處理
當(dāng)我們使用瀏覽器的時候,通常因?yàn)榧虞d的頁面的服務(wù)器的各種原因?qū)е赂鞣N出錯的情況,最平常的比如404錯誤,通常情況下瀏覽器會提示一個錯誤提示頁面。事實(shí)上這個錯誤提示頁面是瀏覽器在加載了本地的一個頁面,用來提示用戶目前已經(jīng)出錯了。
但是當(dāng)我們的app里面使用webview控件的時候遇到了諸如404這類的錯誤的時候,若也顯示瀏覽器里面的那種錯誤提示頁面就顯得很丑陋了,那么這個時候我們的app就需要加載一個本地的錯誤提示頁面,即webview如何加載一個本地的頁面。
1. 首先我們需要些一個html文件,比如error_handle.html,這個文件里面就是當(dāng)出錯的時候需要展示給用戶看的一個錯誤提示頁面,盡量做的精美一些。然后將該文件放置到代碼根目錄的assets文件夾下。
2. 隨后我們需要復(fù)寫WebViewClient的onRecievedError方法,該方法傳回了錯誤碼,根據(jù)錯誤類型可以進(jìn)行不同的錯誤分類處理
webview.setWebViewClient(new WebViewClient(){ @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { switch(errorCode) { case HttpStatus.SC_NOT_FOUND: view.loadUrl("file:///android_assets/error_handle.html"); break; } } });
其實(shí),當(dāng)出錯的時候,我們可以選擇隱藏掉webview,而顯示native的錯誤處理控件,這個時候只需要在onReceivedError里面顯示出錯誤處理的native控件同時隱藏掉webview即可。
四、webview同步cookies
cookies是服務(wù)器用來保存每個客戶的常用信息的,下次客戶進(jìn)入一個諸如登陸的頁面時服務(wù)器會檢測cookie信息,如果通過則直接進(jìn)入登陸后的頁面。
在webview中,如果之前已經(jīng)登陸過了,那么下次再進(jìn)入同樣的登陸界面時,若需要再次登陸的話,一定會很惱人,所以這里提供一個webview同步cookies的方法。
1.首先,我們假設(shè)某個網(wǎng)站的登陸界面需要提供兩個參數(shù),一個是name,一個是pwd,那么要是對這個頁面進(jìn)行登陸,那么必須給與這兩個信息。我們假設(shè)服務(wù)器已經(jīng)注冊了name為jason,pwd為123456這個賬號。
2.下面,寫一個Thread用來將name和pwd自動的登入,在服務(wù)器返回的response中獲得cookie信息,稍后對這個cookie進(jìn)行保存,這里先給出這個Thread的代碼:
public class HttpCookie extends Thread { private Handler mHandler; public HttpCookie(Handler mHandler) { this.mHandler = mHandler; } @Override public void run() { HttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost("");//this place should add the login address List<NameValuePair> list = new ArrayList<NameValuePair>(); list.add(new BasicNameValuePair("name", "jason")); list.add(new BasicNameValuePair("pwd", "123456")); try { post.setEntity(new UrlEncodedFormEntity(list)); HttpResponse reponse = client.execute(post); if(reponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK){ AbstractHttpClient absClient = (AbstractHttpClient) client; List<Cookie> cookies = absClient.getCookieStore().getCookies(); for(Cookie cookie:cookies){ if(cookie != null){ //TODO //this place would get the cookies } } } } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (ClientProtocolException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
由于這是一個子線程,所以需要在主線程中創(chuàng)建并執(zhí)行。
同時,因?yàn)槠鋵?shí)子線程,那么里面必須含有一個handler的元素,用來當(dāng)成功獲取cookie后通知主線程進(jìn)行同步和保存。初始化這個子線程的時候需要將主線程上的handler給傳過來,隨后在以上代碼的TODO中發(fā)送消息,讓主線程記錄cookie,發(fā)送的這個消息需要將cookie信息包含進(jìn)去:
if(cookie != null){ //TODO //this place would get the cookies Message msg = new Message(); msg.obj = cookie; if(mHandler != null){ mHandler.sendMessage(msg); return; } }
隨后在主線程中(webview加載登陸界面前),在handler中將會獲取到cookie信息,下面將對該cookie進(jìn)行保存和同步:
private Handler mHandler = new Handler(){ public void handleMessage(android.os.Message msg) { CookieSyncManager.createInstance(MainActivity.this); CookieManager cookieMgr = CookieManager.getInstance(); cookieMgr.setAcceptCookie(true); cookieMgr.setCookie("", msg.obj.toString());// this place should add the login host address(not the login index address) CookieSyncManager.getInstance().sync(); webview.loadUrl("");// login index address }; };
這個時候發(fā)現(xiàn)webview加載的login index頁面中可以自動的登陸了并顯示登陸后的界面。
五、 WebView與JavaScript的交互
1. webview調(diào)用js
mWebView.loadUrl("javascript:do()");
以上是webview在調(diào)用js中的一個叫做do的方法,該js所在的html文件大致如下:
<html> <script language="javascript"> /* This function is invoked by the webview*/ function do() { alert("1"); } </script> <body> <a onClick="window.demo.clickOnAndroid()"><div style="width:80px; margin:0px auto; padding:10px; text-align:center; border:2px solid #111111;" > <img id="droid" src="xx.png"/><br> Click me! </div></a> </body> </html>
2. js調(diào)用webview
我們假設(shè)下列的本地類是要給js調(diào)用的:
package com.test.webview; class DemoJavaScriptInterface { DemoJavaScriptInterface() { } /** * This is not called on the UI thread. Post a runnable to invoke * loadUrl on the UI thread. */ public void clickOnAndroid() { mHandler.post(new Runnable() { public void run() { //TODO } }); } }
首先給webview設(shè)置:
mWebview.setJavaScriptEnabled(true);
隨后將本地的類(被js調(diào)用的)映射出去:
mWebView.addJavascriptInterface(new DemoJavaScriptInterface(), "demo");
“demo”這個名字就是公布出去給JS調(diào)用的,那么js久可以直接用下列代碼調(diào)用本地的DemoJavaScriptInterface類中的方法了:
<body onload="javascript:demo.clickOnAndroid()">
...
</body>
六、WebView與JavaScript相互調(diào)用混淆問題
若webview中的js調(diào)用了本地的方法,正常情況下發(fā)布的debug包js調(diào)用的時候是沒有問題的,但是通常發(fā)布商業(yè)版本的apk都是要經(jīng)過混淆的步驟,這個時候會發(fā)現(xiàn)之前調(diào)用正常的js卻無法正常調(diào)用本地方法了。
這是因?yàn)榛煜臅r候已經(jīng)把本地的代碼的引用給打亂了,導(dǎo)致js中的代碼找不到本地的方法的地址。
解決這個問題很簡單,即在proguard.cfg文件中加上一些代碼,聲明本地中被js調(diào)用的代碼不被混淆。下面舉例說明:
第五節(jié)中被js調(diào)用的那個類DemoJavaScriptInterface的包名為com.test.webview,那么就要在proguard.cfg文件中加入:
-keep public class com.test.webview.DemoJavaScriptInterface{
public <methods>;
}
若是內(nèi)部類,則大致寫成如下形式:
-keep public class com.test.webview.DemoJavaScriptInterface$InnerClass{
public <methods>;
}
若android版本比較新,可能還需要添加上下列代碼:
-keepattributes *Annotation*
-keepattributes *JavascriptInterface*
另一種講解:
一、基本用法
1、加載在線URL
void loadUrl(String url)
這個函數(shù)主要加載url所對應(yīng)的網(wǎng)頁地址,或者用于調(diào)用網(wǎng)頁中的指定的JS方法(調(diào)用js方法的用法,后面會講),但有一點(diǎn)必須注意的是:loadUrl()必須在主線程中執(zhí)行?。。》駝t就會報錯?。?!。
注意:加載在線網(wǎng)頁地址是會用到聯(lián)網(wǎng)permission權(quán)限的,所以需要在AndroidManifest.xml中寫入下面代碼申請權(quán)限:
<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>
對應(yīng)的處理代碼如下
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"); } }); } }
代碼很簡單,就是在點(diǎn)擊按鈕的時候加載網(wǎng)址,但需要注意的是:網(wǎng)址必須完整即以http://或者ftp://等協(xié)議開頭,不能省略!不然將加載不出來,這是因?yàn)閣ebview是沒有自動補(bǔ)全協(xié)議功能的,所以如果我們不加,它將識別不出來網(wǎng)址類型,也就加載不出來了。
但如果我們運(yùn)行上面的代碼,效果卻是利用瀏覽器來打開網(wǎng)址,卻不是使用webview打開網(wǎng)址:
如果我們想實(shí)現(xiàn)像示例一樣在webview中打開網(wǎng)址需要怎么做呢?
我們需要設(shè)置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"); } }); } }
在上面的基礎(chǔ)上,我們添加了下面一段代碼:
mWebView.setWebViewClient(new WebViewClient());
在這里我們利用mWebView.setWebViewClient()函數(shù)僅僅設(shè)置了一個WebViewClient實(shí)例,就可以實(shí)現(xiàn)在WebView中打開鏈接了,至于原因我們下篇會講到,這里就先忽略了,大家只需要知道要在WebView中打開鏈接,就必須要設(shè)置WebViewClient;
最終的效果圖就與開篇時一樣的了,這里就不再帖效果圖了,下面我們來看看如何加載本地html網(wǎng)頁。
2、加載本地URL
一般而言,我們會將本地html文件放在assets文件夾下,比如:
web.html的內(nèi)容為:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <h1>歡迎光臨啟艦的blog</h1> </head> <body> </body> </html>
即大標(biāo)題顯示一段文字
我們同樣在上面的示例的基礎(chǔ)上加以改造,在點(diǎn)擊按鈕的時候加載本地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有兩點(diǎn)不同:
1)、URL類型不一樣
在加載本地URL時,是以“file:///”開頭的,而assets目錄所對應(yīng)的路徑名為anroid_asset,寫成其它的將識別不了,這是assets目錄的以file開頭的url形式的固定訪問形式。
2)、不需要設(shè)置WebViewClient
這里很明顯沒有設(shè)置WebViewClient函數(shù),但仍然是在webview中打開的本地文件。具體原因下篇文章講到WebViewClient時我們會具體解釋。
本例效果圖如下:
所以對于加載URL的總結(jié)就是:
1、如果是在線網(wǎng)址記得添加網(wǎng)絡(luò)訪問權(quán)限
2、在線網(wǎng)址中,如果要使用webview打開,記得設(shè)置WebViewClient
3、打開本地html文件時,是不需要設(shè)置WebViewClient,對應(yīng)的asstes目錄的url為:file:///android_asset/xxxxx
3、WebView基本設(shè)置
如果我們需要設(shè)置WebView的屬性,是通過WebView.getSettings()獲取設(shè)置WebView的WebSettings對象,然后調(diào)用WebSettings中的方法來實(shí)現(xiàn)的。
WebSettings的方法及說明如下:(這里先列出來所有的方法及解釋,大家可以先忽略,看后面的舉例中所使用的幾個常用方法即可,用到哪個函數(shù)的時候再回來查查就可以了)
/** * 是否支持縮放,配合方法setBuiltInZoomControls使用,默認(rèn)true */ setSupportZoom(boolean support) /** * 是否需要用戶手勢來播放Media,默認(rèn)true */ setMediaPlaybackRequiresUserGesture(boolean require) /** * 是否使用WebView內(nèi)置的縮放組件,由浮動在窗口上的縮放控制和手勢縮放控制組成,默認(rèn)false */ setBuiltInZoomControls(boolean enabled) /** * 是否顯示窗口懸浮的縮放控制,默認(rèn)true */ setDisplayZoomControls(boolean enabled) /** * 是否允許訪問WebView內(nèi)部文件,默認(rèn)true */ setAllowFileAccess(boolean allow) /** * 是否允許獲取WebView的內(nèi)容URL ,可以讓W(xué)ebView訪問ContentPrivider存儲的內(nèi)容。 默認(rèn)true */ setAllowContentAccess(boolean allow) /** * 是否啟動概述模式瀏覽界面,當(dāng)頁面寬度超過WebView顯示寬度時,縮小頁面適應(yīng)WebView。默認(rèn)false */ setLoadWithOverviewMode(boolean overview) /** * 是否保存表單數(shù)據(jù),默認(rèn)false */ setSaveFormData(boolean save) /** * 設(shè)置頁面文字縮放百分比,默認(rèn)100% */ setTextZoom(int textZoom) /** * 是否支持ViewPort的meta tag屬性,如果頁面有ViewPort meta tag 指定的寬度,則使用meta tag指定的值,否則默認(rèn)使用寬屏的視圖窗口 */ setUseWideViewPort(boolean use) /** * 是否支持多窗口,如果設(shè)置為true ,WebChromeClient#onCreateWindow方法必須被主程序?qū)崿F(xiàn),默認(rèn)false */ setSupportMultipleWindows(boolean support) /** * 指定WebView的頁面布局顯示形式,調(diào)用該方法會引起頁面重繪。默認(rèn)LayoutAlgorithm#NARROW_COLUMNS */ setLayoutAlgorithm(LayoutAlgorithm l) /** * 設(shè)置標(biāo)準(zhǔn)的字體族,默認(rèn)”sans-serif”。font-family 規(guī)定元素的字體系列。 * font-family 可以把多個字體名稱作為一個“回退”系統(tǒng)來保存。如果瀏覽器不支持第一個字體, * 則會嘗試下一個。也就是說,font-family 屬性的值是用于某個元素的字體族名稱或/及類族名稱的一個 * 優(yōu)先表。瀏覽器會使用它可識別的第一個值。 */ setStandardFontFamily(String font) /** * 設(shè)置混合字體族。默認(rèn)”monospace” */ setFixedFontFamily(String font) /** * 設(shè)置SansSerif字體族。默認(rèn)”sans-serif” */ setSansSerifFontFamily(String font) /** * 設(shè)置SerifFont字體族,默認(rèn)”sans-serif” */ setSerifFontFamily(String font) /** * 設(shè)置CursiveFont字體族,默認(rèn)”cursive” */ setCursiveFontFamily(String font) /** * 設(shè)置FantasyFont字體族,默認(rèn)”fantasy” */ setFantasyFontFamily(String font) /** * 設(shè)置最小字體,默認(rèn)8. 取值區(qū)間[1-72],超過范圍,使用其上限值。 */ setMinimumFontSize(int size) /** * 設(shè)置最小邏輯字體,默認(rèn)8. 取值區(qū)間[1-72],超過范圍,使用其上限值。 */ setMinimumLogicalFontSize(int size) /** * 設(shè)置默認(rèn)字體大小,默認(rèn)16,取值區(qū)間[1-72],超過范圍,使用其上限值。 */ setDefaultFontSize(int size) /** * 設(shè)置默認(rèn)填充字體大小,默認(rèn)16,取值區(qū)間[1-72],超過范圍,使用其上限值。 */ setDefaultFixedFontSize(int size) /** * 設(shè)置是否加載圖片資源,注意:方法控制所有的資源圖片顯示,包括嵌入的本地圖片資源。 * 使用方法setBlockNetworkImage則只限制網(wǎng)絡(luò)資源圖片的顯示。值設(shè)置為true后, * webview會自動加載網(wǎng)絡(luò)圖片。默認(rèn)true */ setLoadsImagesAutomatically(boolean flag) /** * 是否加載網(wǎng)絡(luò)圖片資源。注意如果getLoadsImagesAutomatically返回false,則該方法沒有效果。 * 如果使用setBlockNetworkLoads設(shè)置為false,該方法設(shè)置為false,也不會顯示網(wǎng)絡(luò)圖片。 * 當(dāng)值從true改為false時。WebView會自動加載網(wǎng)絡(luò)圖片。 */ setBlockNetworkImage(boolean flag) /** * 設(shè)置是否加載網(wǎng)絡(luò)資源。注意如果值從true切換為false后,WebView不會自動加載, * 除非調(diào)用WebView#reload().如果沒有android.Manifest.permission#INTERNET權(quán)限, * 值設(shè)為false,則會拋出java.lang.SecurityException異常。 * 默認(rèn)值:有android.Manifest.permission#INTERNET權(quán)限時為false,其他為true。 */ setBlockNetworkLoads(boolean flag) /** * 設(shè)置是否允許執(zhí)行JS。 */ setJavaScriptEnabled(boolean flag) /** * 是否允許Js訪問任何來源的內(nèi)容。包括訪問file scheme的URLs。考慮到安全性, * 限制Js訪問范圍默認(rèn)禁用。注意:該方法只影響file scheme類型的資源,其他類型資源如圖片類型的, * 不會受到影響。ICE_CREAM_SANDWICH_MR1版本以及以下默認(rèn)為true,JELLY_BEAN版本 * 以上默認(rèn)為false */ setAllowUniversalAccessFromFileURLs(boolean flag) /** * 是否允許Js訪問其他file scheme的URLs。包括訪問file scheme的資源。考慮到安全性, * 限制Js訪問范圍默認(rèn)禁用。注意:該方法只影響file scheme類型的資源,其他類型資源如圖片類型的, * 不會受到影響。如果getAllowUniversalAccessFromFileURLs為true,則該方法被忽略。 * ICE_CREAM_SANDWICH_MR1版本以及以下默認(rèn)為true,JELLY_BEAN版本以上默認(rèn)為false */ setAllowFileAccessFromFileURLs(boolean flag) /** * 設(shè)置存儲定位數(shù)據(jù)庫的位置,考慮到位置權(quán)限和持久化Cache緩存,Application需要擁有指定路徑的 * write權(quán)限 */ setGeolocationDatabasePath(String databasePath) /** * 是否允許Cache,默認(rèn)false??紤]需要存儲緩存,應(yīng)該為緩存指定存儲路徑setAppCachePath */ setAppCacheEnabled(boolean flag) /** * 設(shè)置Cache API緩存路徑。為了保證可以訪問Cache,Application需要擁有指定路徑的write權(quán)限。 * 該方法應(yīng)該只調(diào)用一次,多次調(diào)用自動忽略。 */ setAppCachePath(String appCachePath) /** * 是否允許數(shù)據(jù)庫存儲。默認(rèn)false。查看setDatabasePath API 如何正確設(shè)置數(shù)據(jù)庫存儲。 * 該設(shè)置擁有全局特性,同一進(jìn)程所有WebView實(shí)例共用同一配置。注意:保證在同一進(jìn)程的任一WebView * 加載頁面之前修改該屬性,因?yàn)樵谶@之后設(shè)置WebView可能會忽略該配置 */ setDatabaseEnabled(boolean flag) /** * 是否存儲頁面DOM結(jié)構(gòu),默認(rèn)false。 */ setDomStorageEnabled(boolean flag) /** * 是否允許定位,默認(rèn)true。注意:為了保證定位可以使用,要保證以下幾點(diǎn): * Application 需要有android.Manifest.permission#ACCESS_COARSE_LOCATION的權(quán)限 * Application 需要實(shí)現(xiàn)WebChromeClient#onGeolocationPermissionsShowPrompt的回調(diào), * 接收J(rèn)s定位請求訪問地理位置的通知 */ setGeolocationEnabled(boolean flag) /** * 是否允許JS自動打開窗口。默認(rèn)false */ setJavaScriptCanOpenWindowsAutomatically(boolean flag) /** * 設(shè)置頁面的編碼格式,默認(rèn)UTF-8 */ setDefaultTextEncodingName(String encoding) /** * 設(shè)置WebView代理,默認(rèn)使用默認(rèn)值 */ setUserAgentString(String ua) /** * 通知WebView是否需要設(shè)置一個節(jié)點(diǎn)獲取焦點(diǎn)當(dāng) * WebView#requestFocus(int,android.graphics.Rect)被調(diào)用的時候,默認(rèn)true */ setNeedInitialFocus(boolean flag) /** * 基于WebView導(dǎo)航的類型使用緩存:正常頁面加載會加載緩存并按需判斷內(nèi)容是否需要重新驗(yàn)證。 * 如果是頁面返回,頁面內(nèi)容不會重新加載,直接從緩存中恢復(fù)。setCacheMode允許客戶端根據(jù)指定的模式來 * 使用緩存。 * LOAD_DEFAULT 默認(rèn)加載方式 * LOAD_CACHE_ELSE_NETWORK 按網(wǎng)絡(luò)情況使用緩存 * LOAD_NO_CACHE 不使用緩存 * LOAD_CACHE_ONLY 只使用緩存 */ setCacheMode(int mode) /** * 設(shè)置加載不安全資源的WebView加載行為。KITKAT版本以及以下默認(rèn)為MIXED_CONTENT_ALWAYS_ALLOW方 * 式,LOLLIPOP默認(rèn)MIXED_CONTENT_NEVER_ALLOW。強(qiáng)烈建議:使用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:設(shè)置緩存
優(yōu)先使用緩存
webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
不使用緩存:
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
示例3:打開頁面時,自適應(yīng)屏幕:
WebSettings webSettings = mWebView .getSettings(); webSettings.setUseWideViewPort(true);//設(shè)置此屬性,可任意比例縮放 webSettings.setLoadWithOverviewMode(true);
效果圖如下:
示例4:使頁面支持縮放:
WebSettings webSettings = mWebView.getSettings(); //開啟javascript支持 webSettings.setJavaScriptEnabled(true); // 設(shè)置可以支持縮放 webSettings.setSupportZoom(true); // 設(shè)置出現(xiàn)縮放工具 webSettings.setBuiltInZoomControls(true);
示例5:.如果webView中需要用戶手動輸入用戶名、密碼或其他,則webview必須設(shè)置支持獲取手勢焦點(diǎn)
webview.requestFocusFromTouch();
其它的設(shè)置就靠大家自己去嘗試?yán)?,這里就不再一一綴述了,大家只需要記著,如果需要設(shè)置webview就從WebSettings里面去找就可以啦。
二、JS調(diào)用Java代碼
在看了如何設(shè)置webview以后,我們來看下如何讓W(xué)ebview與網(wǎng)頁中的JS代碼交互的問題。
1、概述
更多時候,網(wǎng)頁中需要通過JS代碼來調(diào)用本地的Android代碼,比如H5頁面需要判斷當(dāng)前用戶是否登錄等。
利用JS代碼調(diào)用JAVA代碼,主要是用到WebView下面的一個函數(shù):
public void addJavascriptInterface(Object obj, String interfaceName)
這個函數(shù)有兩個參數(shù):
--Object obj:interfaceName所綁定的對象
--String interfaceName:所綁定的對象所對應(yīng)的名稱
它有意義就是向WebView注入一個interfaceName的對象,這個對象綁定的是obj對象,通過interfaceName就可以調(diào)用obj對象中的方法,這個表述可能大家不太理解,因?yàn)閕nterfaceName是一個String,怎么被你說成對象了,理解不了沒關(guān)系,下面有具體示例
2、示例
下面同樣是上面的示例,我們對它加以更改,效果圖如下:
在原來html上面添加了一個按鈕,當(dāng)點(diǎn)擊按鈕時調(diào)用Android的Toast函數(shù)彈出一個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中額外寫了一個函數(shù)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調(diào)native" onclick="ok()"> </head> <body> <script type="text/javascript"> function ok() { android.toastMessage("哈哈,i m webview msg"); } </script> </body> </html>
在這個html中,我添加了一個button按鈕,當(dāng)點(diǎn)擊時調(diào)用ok函數(shù):
function ok() { android.toastMessage("哈哈,i m webview msg"); }
它的意義就是調(diào)用android對象里的toastMessage方法,這個android就是我們利用mWebView.addJavascriptInterface(this, “android”)注入到WebView的android,它所對應(yīng)的對象就將MyActivity;所以在JS中,我們就可以通過android這個別名來調(diào)用MyActivity對象中的任何public方法。
3、addJavascriptInterface自定義作用對象
在上面的示例中mWebView.addJavascriptInterface(this, “android”);我們直接通過this,把當(dāng)前整個類作為對象傳給WebView了,但這會有很大風(fēng)險,因?yàn)槲覀冞@個類中可能會有各種函數(shù),而這些函數(shù)是只有本地Native代碼才會用到,WebView是根本用不到的。所以如果通過全部注入給WebView的話,那么一些存心不良的同學(xué)就可以任意調(diào)用我們這個類中的方法,給我們APP帶來危害。
所以,一般而言,我們很少直接會傳this,把整個類注入給WebView,而是單獨(dú)寫一個類專門用于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中所定義的對象,如果訪問其它類的函數(shù),比如MyActivity中的函數(shù),就會報下面的錯誤(即方法找不到)
大家可以自己嘗試下;
然后對應(yīng)的html代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <h1>歡迎光臨啟艦的blog</h1> <input type="button" value="js調(diào)native" onclick="ok()"> </head> <body> <script type="text/javascript"> function ok() { android.toastMessage("哈哈,i m webview msg"); } </script> </body> </html>
由于在注入時的對象別名和所調(diào)用的函數(shù)名都沒有變,所以HTML中的JS代碼是無需更改的。效果圖與上節(jié)一樣,就不再帖出了。
源碼在文章底部給出
4、addJavascriptInterface注入漏洞
上面我們說了在addJavascriptInterface注入時,為了防止WebView調(diào)用我們不想被它調(diào)用的函數(shù),所以我們需要單獨(dú)為WebView交互定義一個類,讓它只執(zhí)行這個類里面的函數(shù)
但……這真的能擋住黑客的攻擊嗎?
當(dāng)然是NO……,不然我就不會寫這一段了……
mWebView.addJavascriptInterface(new JSBridge(), "android");
在注入時,我們已經(jīng)把對象傳給了JS,在JS中當(dāng)然可以通過反射得到APP中的各種類的實(shí)例!現(xiàn)在反編譯Android代碼可不是什么難事(本文結(jié)尾附j(luò)adx反編譯方法),很容易拿到你有哪些類,有哪些函數(shù),通過這些就可以想執(zhí)行哪個執(zhí)行哪個了,有沒有細(xì)思極恐……
具體的細(xì)節(jié)我就不講了,不在本篇范圍,給大家找了篇文章,有興趣的同學(xué)可以參考下:《Android WebView的Js對象注入漏洞解決方案》
5、JavascriptInterface注解
為了解決addJavascriptInterface()函數(shù)的安全問題,在android:targetSdkVersion數(shù)值為17(Android4.2)及以上的APP中,JS只能訪問帶有 @JavascriptInterface注解的Java函數(shù),所以如果你的android:targetSdkVersion是17+,與JS交互的Native函數(shù)中,必須添加JavascriptInterface注解,不然無效,比如:
public class JSBridge { @JavascriptInterface public void toastMessage(String message) { Toast.makeText(getApplicationContext(), "通過Natvie傳遞的Toast:" + message, Toast.LENGTH_LONG).show(); } }
這也就是很多同學(xué)在高target上,addJavascriptInterface()函數(shù)無效的主要原因。
注意:雖然在target 17以后,已經(jīng)修復(fù)了這個安全問題,但目前大多數(shù)APP都還是target 17以前的,所以大家可以嘗試著找一些APP來演示下這個漏洞哦……
三、JAVA調(diào)用JS代碼
1、JAVA調(diào)用JS代碼
前面給大家演示了如何通過JS調(diào)用Java代碼,這里就反過來看看,如何在Native中調(diào)用JS的代碼
本例的效果圖如下:
在點(diǎn)擊“加載URL”按鈕時,調(diào)用webview中的JavaScript求和函數(shù),將結(jié)果顯示在標(biāo)題中。
先看html代碼:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <h1 id="h">歡迎光臨啟艦的blog</h1> <input type="button" value="js調(diào)native" onclick="ok()"> </head> <body> <script type="text/javascript"> function sum(i,m) { document.getElementById("h").innerHTML= (i+m); } </script> </body> </html>
在這里,我們寫了一個求和函數(shù)sum(i,m)
結(jié)果中就是把h1標(biāo)簽的內(nèi)容改為求和后的結(jié)果值。
再來看看JAVA的調(diào)用代碼:
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)"); } }); } }
在點(diǎn)擊按鈕時調(diào)用JS函數(shù):
mWebView.loadUrl("javascript:sum(3,8)");
這里也就是在JAVA中調(diào)用JS函數(shù)的方法:
String url = "javascript:methodName(params……);"
webView.loadUrl(url);
javascript:偽協(xié)議讓我們可以通過一個鏈接來調(diào)用JavaScript函數(shù)
中間methodName是JavaScript中實(shí)現(xiàn)的函數(shù)
jsonParams是傳入的參數(shù)列表
使用起來難度不大,就不再多講了
源碼在文章底部給出
2、JAVA中如何得到JS中的返回值(Android4.4以前)
現(xiàn)在我們再考慮一下,如果我們要在JAVA中需要得到JS的結(jié)果返回值要怎么辦?比如在上面的例子中,我們需要在JAVA中得到在計(jì)算后的結(jié)果值
Android在4.4之前并沒有提供直接調(diào)用js函數(shù)并獲取值的方法,也就是說,我們只能調(diào)用JS中的函數(shù),并不能得到該函數(shù)的返回值,想得到返回值我們就得想其它辦法,所以在此之前,常用的思路是 java調(diào)用js方法,js方法執(zhí)行完畢,再次調(diào)用java代碼將值返回。
1).Java調(diào)用js代碼
webView.addJavascriptInterface(this, "android"); mWebView.loadUrl("javascript:sum(3,8)");
注意,這里通過addJavascriptInterface將MyActiviy所對應(yīng)的對象注入到WebView中了。
2).js函數(shù)處理,并將結(jié)果通過調(diào)用java方法返回
function sum(i,m){ var result = i+m; document.getElementById("h").innerHTML= result; android.onSumResult(result) }
3)..Java在回調(diào)方法中獲取js函數(shù)返回值
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調(diào)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得到結(jié)果,最后通過android.onSumResult(result);將結(jié)果傳給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訪問其中的函數(shù)
mWebView.addJavascriptInterface(this, "android");
第二:供JS調(diào)用,以返回結(jié)果的函數(shù)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方法調(diào)用:
private void testEvaluateJavascript(WebView webView) { webView.evaluateJavascript("getGreetings()", new ValueCallback() { @Override public void onReceiveValue(String value) { Log.i(LOGTAG, "onReceiveValue value=" + value); } }); }
從上面的用法中很明顯看到,通過evaluateJavascript調(diào)用JS中的方法,可以向其中添加結(jié)果回調(diào),來接收J(rèn)S的return值。
注意:
上面限定了結(jié)果返回結(jié)果為String,對于簡單的類型會嘗試轉(zhuǎn)換成字符串返回,對于復(fù)雜的數(shù)據(jù)類型,建議以字符串形式的json返回。
evaluateJavascript方法必須在UI線程(主線程)調(diào)用,因此onReceiveValue也執(zhí)行在主線程。
好了,這篇文章中有關(guān)WebView的知識就先到這,下篇繼續(xù)講。下面給大家講講有關(guān)jadx-gui反編譯的知識。
四、jadx-gui反編譯
在遇到j(luò)adx-gui反編譯之前,都是使用apktools進(jìn)行反編譯的,apktools有些是反編譯不出來的,而且……難用……
想知道jadx-gui有多強(qiáng)?它都可以反編譯淘寶、微信代碼,是不是夠強(qiáng)了。下面我們就來看看它是如何反編譯的;
1)、下載、配置
jadx-gui是開源的,項(xiàng)目地址:《https://github.com/skylot/jadx》
mac電腦:
打開終端,切到某個路徑下,輸入以下命令:
git clone https://github.com/skylot/jadx.git cd jadx ./gradlew dist
其實(shí)這里只是做了兩個動作:
第一,使用git命令將 項(xiàng)目clone下來(這里需要配置git環(huán)境,如果沒有,請先搜資料配置git環(huán)境,然后再來)
然后,執(zhí)行jadx目錄 下gradlew腳本,這個是shell腳本
windows電腦:
git clone https://github.com/skylot/jadx.git cd jadx gradlew.bat dist
在windows電腦中,步驟與mac是一樣的,只是最后一步中,已經(jīng)不再是./gradlew所對應(yīng)的shell腳本了,而是windows平臺上的bat腳本。
可見作者有多牛X,shell腳本和bat腳本都會寫,真是屌的不能直視
整個編譯過程是比較慢的,這里需要耐心等待下。因?yàn)槟壳皶褂胓radle 2.7來編譯項(xiàng)目,如果沒有在環(huán)境變量中環(huán)境gradle 2.7的環(huán)境變量,會自己下載gradle 2.7
編譯成功后會打出BUILD SUCCESS字樣,如下圖所示:
在編譯成功后,在jadx目錄下,會生成一個build目錄,其中包含jadx目錄和一個jadx-xxx-dev.zip的打包文件。在build/jadx目錄下,就是源碼編譯出的jadx工具及所用jar包。jadx-xxx-dev.zip解壓后的內(nèi)容與build/jadx內(nèi)容一樣,只是將其打包了一下而已,方便移值,可見作者有多用心。build目錄結(jié)構(gòu)如下圖所示:
2)、開始反編譯
等完畢后,可以開始了,我就介紹個最簡單最常用的用法
(1)、把a(bǔ)pk改成zip
(2)、解壓zip獲取class.dex文件
(3)、將class.dex文件放到j(luò)adx目錄下
cd build/jadx/ bin/jadx -d out classes.dex # 反編譯后放入out文件夾下(如果out不存在它會自動創(chuàng)建) #or bin/jadx-gui classes.dex # 會反編譯,并且使用gui打開
目錄結(jié)構(gòu)圖如下:
在使用jadx-gui反編譯時,左下角會顯示當(dāng)前反編譯的進(jìn)度:
在反編譯完成后,在左側(cè)就可以看到目錄結(jié)構(gòu)和對應(yīng)的代碼,目錄結(jié)構(gòu)中顯示的a,b,c,d這些字母是由于在生成apk時使用proguard混淆造成的,proguard混淆的類名是沒辦法反編譯出對應(yīng)的原類名的,這也是反編譯代碼中比較蛋疼的地方,下面給大家演示下結(jié)果:(還可以通過點(diǎn)擊搜索按鈕,搜索其中的代碼)
有關(guān)jadx-gui工具的更多用法,只有靠大家自己去研究啦,到這里我們的源碼就已經(jīng)反編譯出來了。
反編譯工具和微信的Classes文件,會在源碼中給出大家,源碼下載地址:Android WebView使用
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助。
- 三步搞定:Vue.js調(diào)用Android原生操作
- DCloud的native.js調(diào)用系統(tǒng)分享實(shí)例Android版代碼
- Android中實(shí)現(xiàn)WebView和JavaScript的互相調(diào)用詳解
- Android WebView的使用方法及與JS 相互調(diào)用
- Android編程使用WebView實(shí)現(xiàn)與Javascript交互的方法【相互調(diào)用參數(shù)、傳值】
- JS調(diào)用Android、Ios原生控件
- Android總結(jié)之WebView與Javascript交互(互相調(diào)用)
- Android與JS之間跨平臺異步調(diào)用實(shí)例詳解
- Android webview和js互相調(diào)用實(shí)現(xiàn)方法
- Android與JS相互調(diào)用的方法
相關(guān)文章
Android實(shí)戰(zhàn)教程第三篇之簡單實(shí)現(xiàn)撥打電話功能
這篇文章主要為大家詳細(xì)介紹了Android實(shí)戰(zhàn)教程第三篇之簡單實(shí)現(xiàn)撥打電話功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-11-11Android開發(fā)注解排列組合出啟動任務(wù)ksp
這篇文章主要為大家介紹了Android開發(fā)注解排列組合出啟動任務(wù)ksp示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Android開發(fā)使用RecyclerView添加點(diǎn)擊事件實(shí)例詳解
這篇文章主要為大家介紹了Android開發(fā)使用RecyclerView添加點(diǎn)擊事件實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08android屏蔽按鈕連續(xù)點(diǎn)擊的示例代碼
這篇文章主要介紹了android屏蔽按鈕連續(xù)點(diǎn)擊的示例代碼,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-12-12Android編程滑動效果之Gallery仿圖像集瀏覽實(shí)現(xiàn)方法
這篇文章主要介紹了Android編程滑動效果之Gallery仿圖像集瀏覽實(shí)現(xiàn)方法,結(jié)合實(shí)例形式詳細(xì)分析了Gallery瀏覽圖片的原理、步驟與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-02-02Android開發(fā)之登錄驗(yàn)證實(shí)例教程
這篇文章主要介紹了Android開發(fā)之登錄驗(yàn)證實(shí)現(xiàn)方法,包括發(fā)送數(shù)據(jù)、服務(wù)器端驗(yàn)證、配置文件等,需要的朋友可以參考下2014-08-08Android之ProgressBar即時顯示下載進(jìn)度詳解
這篇文章主要為大家詳細(xì)介紹了Android之ProgressBar即時顯示下載進(jìn)度,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-09-09