前言:念念不忘,必有回响,永远坚持你所坚持的!
相关文章:
1、《WebView使用详解(一)——Native与JS相互调用(附JadX反编译)》
2、《WebView使用详解(二)——WebViewClient与常用事件监听》
一直在用WebView,还没有系统的总结过它的用法,下面就系统的总结下,分享给大家
void loadUrl(String url)这个函数主要加载url所对应的网页地址,或者用于调用网页中的指定的JS方法(调用js方法的用法,后面会讲),但有一点必须注意的是:loadUrl()必须在主线程中执行!!!否则就会报错!!!。
<uses-permission android:name="android.permission.INTERNET" />本示例效果为:
<?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是没有自动补全协议功能的,所以如果我们不加,它将识别不出来网址类型,也就加载不出来了。
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;
web.html的内容为:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <h1>欢迎光临启舰的blog</h1> </head> <body> </body> </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有两点不同:
所以对于加载URL的总结就是:
1、如果是在线网址记得添加网络访问权限
2、在线网址中,如果要使用webview打开,记得设置WebViewClient
3、打开本地html文件时,是不需要设置WebViewClient,对应的asstes目录的url为:file:///android_asset/xxxxx
/** * 是否支持缩放,配合方法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)下面我们就举个例子来看下用法
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); } }
webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);不使用缓存:
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
WebSettings webSettings = mWebView .getSettings(); webSettings.setUseWideViewPort(true);//设置此属性,可任意比例缩放 webSettings.setLoadWithOverviewMode(true);效果图如下:(所使用的网址为:http://www.w3school.com.cn/)
注意:对于我们自己写的网页代码,不必利用这处函数来做页面缩放适配,因为对于有些手机存在适配问题,只需要在HTML中做宽度100%自适应屏幕就行了
WebSettings webSettings = mWebView.getSettings(); //开启javascript支持 webSettings.setJavaScriptEnabled(true); // 设置可以支持缩放 webSettings.setSupportZoom(true); // 设置出现缩放工具 webSettings.setBuiltInZoomControls(true);
webview.requestFocusFromTouch();其它的设置就靠大家自己去尝试啦,这里就不再一一缀述了,大家只需要记着,如果需要设置webview就从WebSettings里面去找就可以啦。
public void addJavascriptInterface(Object obj, String interfaceName)这个函数有两个参数:
在原来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
<!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方法。
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代码是无需更改的。效果图与上节一样,就不再帖出了。
mWebView.addJavascriptInterface(new JSBridge(), "android");在注入时,我们已经把对象传给了JS,在JS中当然可以通过反射得到APP中的各种类的实例!现在反编译Android代码可不是什么难事(本文结尾附jadx反编译方法),很容易拿到你有哪些类,有哪些函数,通过这些就可以想执行哪个执行哪个了,有没有细思极恐……
public class JSBridge { @JavascriptInterface public void toastMessage(String message) { Toast.makeText(getApplicationContext(), "通过Natvie传递的Toast:" + message, Toast.LENGTH_LONG).show(); } }这也就是很多同学在高target上,addJavascriptInterface()函数无效的主要原因。
在点击“加载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)
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函数
1.Java调用js代码
webView.addJavascriptInterface(this, "android"); mWebView.loadUrl("javascript:sum(3,8)");注意,这里通过addJavascriptInterface将MyActiviy所对应的对象注入到WebView中了。
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
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(); } }这里主要做了两件事:
mWebView.addJavascriptInterface(this, "android");第二:供JS调用,以返回结果的函数onSumResult():
public void onSumResult(int result) { Toast.makeText(this,"received result:"+result,Toast.LENGTH_SHORT).show(); }
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值。
git clone https://github.com/skylot/jadx.git cd jadx ./gradlew dist其实这里只是做了两个动作:
git clone https://github.com/skylot/jadx.git cd jadx gradlew.bat dist在windows电脑中,步骤与mac是一样的,只是最后一步中,已经不再是./gradlew所对应的shell脚本了,而是windows平台上的bat脚本。
在编译成功后,在jadx目录下,会生成一个build目录,其中包含jadx目录和一个jadx-xxx-dev.zip的打包文件。在build/jadx目录下,就是源码编译出的jadx工具及所用jar包。jadx-xxx-dev.zip解压后的内容与build/jadx内容一样,只是将其打包了一下而已,方便移值,可见作者有多用心。build目录结构如下图所示:
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 谢谢
如果你喜欢我的文章,那么你将会更喜欢我的微信公众号,将定期推送博主最新文章与收集干货分享给大家(一周一次)