1.概述
首先,需要提出一个概念,那就是hybrid,主要意思就是native原生Android和h5混合开发。为什么要这样做呢?大家可以想象一下针对于同一个活动,如果使用纯native的开发方式,Android和iOS两边都要维护同一套界面甚至是逻辑,这样开发和维护的成本会很大,而使用hybrid的开发方式的话,让前端的同学去写一套界面和逻辑,对于native端来说只要使用对应的容器去展示就可以了(对于Android来说这个容器当然就是WebView)。那为什么不所有的页面都使用这种方式开发呢?因为使用h5来展示界面的话用户体验始终是不如native的,所以在这两者之间我们需要一个权衡。
介绍完了何为hybrid,我们来思考下面几个场景: 场景1:前端那边的页面有一个按钮,点击这个按钮需要显示一个native的组件(比如一个toast),或者点击这个按钮需要去在native端执行一个耗时的任务。 场景2:还是前端页面有一个按钮,点击这个按钮的逻辑是:如果登录了,则跳转到相应的界面,如果没有登录,则跳转到登录界面。而这个登录界面是我们native维护的。 看完上面两个场景,相信大家也发现了一个问题,hybrid这样的开发方式有一个问题需要解决,那就是前端和本地的通信。 下面将会给大家介绍active原生Android和h5之间的通信方式。
2.如何使用WebView
使用WebView控件 与其他控件的使用方法相同 在layout中使用一个”WebView”标签 WebView不包括导航栏,地址栏等完整浏览器功能,只用于显示一个网页 在WebView中加载Web页面,使用loadUrl()
这里写图片描述
注意在manifest文件中加入访问互联网的权限:
1. <uses-permission android:name="android.permission.INTERNET"/>
但是,在Android中点击一个链接,默认是调用手机上已经安装的浏览器程序来启动,因此想要通过WebView代为处理这个动作 ,那么需要通过WebViewClient
这里写图片描述
当然,我们也可以写一个类继承WebViewClient来对WebViewClient对象进行扩展
这里写图片描述
然后只需要将setWebViewClient的内容进行修改即可
这里写图片描述
另外出于用户习惯上的考虑 需要将WebView表现得更像一个浏览器,也就是需要可以回退历史记录,因此需要覆盖系统的回退键 goBack,goForward可向前向后浏览历史页面
这里写图片描述
例子1:WebViewClient的使用 布局代码activity_main.xml:
1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"2. xmlns:tools="http://schemas.android.com/tools"3. android:layout_width="match_parent"4. android:layout_height="match_parent"5. android:paddingBottom="@dimen/activity_vertical_margin"6. android:paddingLeft="@dimen/activity_horizontal_margin"7. android:paddingRight="@dimen/activity_horizontal_margin"8. android:paddingTop="@dimen/activity_vertical_margin"9. tools:context="com.example.hybirddemo.MainActivity" >10. 11. <WebView12. android:layout_width="match_parent"13. android:layout_height="match_parent"14. android:id="@+id/webView" />15. 16. </RelativeLayout>
MainActivity代码:
public class MainActivity extends Activity {2. 3. private WebView webView;4. 5. @Override6. protected void onCreate(Bundle savedInstanceState) {7. super.onCreate(savedInstanceState);8. setContentView(R.layout.activity_main);9. 10. // 获取webview控件11. webView = (WebView) findViewById(R.id.webView);12. //设置WebViewClient13. /*webView.setWebViewClient(new MyWebViewClient());*/14. //使用webview加载页面15. webView.loadUrl("http://www.baidu.com");16. 17. webView.setWebViewClient(new WebViewClient() {18. 19. @Override20. public boolean shouldOverrideUrlLoading(WebView view, String url) {21. view.loadUrl(url);22. return true;23. }24. 25. @Override26. public void onPageStarted(WebView view, String url, Bitmap favicon) {27. // TODO Auto-generated method stub28. super.onPageStarted(view, url, favicon);29. }30. 31. @Override32. public void onPageFinished(WebView view, String url) {33. // TODO Aut (view, url);34. }35. 36. });37. 38. } 39. 40. @Override41. //覆盖系统的回退键42. public boolean onKeyDown(int keyCode, KeyEvent event) {43. if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {44. webView.goBack();45. return true;46. }47. 48. return super.onKeyDown(keyCode, event);49. }50. 51. }
3.JavaScript和java的相互调用
WebSetting用处非常大,通过WebSetting可以使用Android原生的JavascriptInterface来进行js和java的通信。 例子2:JavaScript和java的相互调用
首先我们写一段html代码:
1. <!DOCTYPE html>2. <html>3. <head>4. <title>MyHtml.html</title>5. 6. <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">7. <meta http-equiv="description" content="this is my page">8. <meta http-equiv="content-type" content="text/html; charset=UTF-8">9. 10. <!--<link rel="stylesheet" type="text/css" href="./styles.css">-->11. <script type="text/javascript">12. function showToast(toast) {13. javascript:control.showToast(toast);14. }15. function log(msg) {16. consolse.log(msg);17. }18. </script>19. </head>20. 21. <body>22. <input type="button" value="toast" onclick="showToast('hello world!')">23. </body>24. </html>
这是一个很简单的html5页面,里面有一个button,点击这个button就执行js脚本中的showToast方法。
那么这个showToast方法做了什么呢?
可以看到control.showToast,这个是什么我们后面再说,下面看我们Android工程中的java代码。
编写布局文件activity_main.xml 布局的内容很简单,就是嵌套一个WebView控件
这里写图片描述
编写MainActivity.java代码
1. package com.example.hybirddemo;2. 3. import android.annotation.SuppressLint;4. import android.app.Activity;5. import android.os.Bundle;6. import android.util.Log;7. import android.view.Menu;8. import android.view.MenuItem;9. import android.webkit.JavascriptInterface;10. import android.webkit.WebSettings;11. import android.webkit.WebView;12. import android.widget.Toast;13. 14. public class MainActivity extends Activity {15. 16. private WebView webView;17. 18. @Override19. protected void onCreate(Bundle savedInstanceState) {20. super.onCreate(savedInstanceState);21. setContentView(R.layout.activity_main);22. // 获取webview控件23. webView = (WebView) findViewById(R.id.webView);24. // 获取WebView的设置25. WebSettings webSettings = webView.getSettings();26. // 将JavaScript设置为可用,这一句话是必须的,不然所做一切都是徒劳的27. webSettings.setJavaScriptEnabled(true);28. //给webview添加JavaScript接口29. webView.addJavascriptInterface(new JsInterface(), "control");30. //通过webview加载html页面31. webView.loadUrl("file:///android_asset/MyHtml.html");32. 33. }34. public class JsInterface{35. @JavascriptInterface36. public void showToast(String toast) {37. Toast.makeText(MainActivity.this,toast , Toast.LENGTH_SHORT).show();38. Log.d("html", "show toast success");39. }40. public void log(final String msg) {41. webView.post(new Runnable() {42. 43. @Override44. public void run() {45. webView.loadUrl("javascript log("+"'"+msg+"'"+")");46. 47. }48. });49. }50. }51. }
上面的代码主要做了以下的步骤: a) 获取webview控件 b) 获取webview的设置,将JavaScript设置为可用,打开JavaScript的通道
这里写图片描述
c) 在Android程序中建立接口 ,并编写相关逻辑 再去看之前js脚本中的那个showToast()方法
这里写图片描述
这里的control就是我们的那个interface,调用了interface的showToast方法,很明显这里是js调用了Android的代码,输出了一个Toast
可以看到这个interface我们给它取名叫control,最后通过loadUrl加载页面。
可以看到先显示一个toast,然后调用log()方法,log()方法里调用了js脚本的log()方法, js的log()方法做的事就是在控制台输出msg,这里明显是Android调用了js的方法。
d) 给webview添加我们自己编写的JavaScript接口 通过WebView的addJavascriptInterface方法去注入一个我们自己写的interface。
e) 使用webview控件加载我们之前编写的html文件
在真实手机上运行程序,在控制台成功输出内容:
这样我们就完成了js和java的互调,是不是很简单。
4.Android中处理JS的警告,对话框等
在Android中处理JS的警告,对话框等需要对WebView设置WebChromeClient对象,并复写其中的onJsAlert,onJsConfirm,onJsPrompt方法可以处理javascript的常用对话框 例子3:在Android中处理javascript的对话框 1) 编写html页面布局
1. <%@LANGUAGE="JAVASCRIPT" CODEPAGE="936"%>2. <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">3. <html xmlns="http://www.w3.org/1999/xhtml">4. <head>5. <meta http-equiv="Content-Type" content="text/html; charset=UTF-8 " />6. <title>分别测试javascript的三种对话框</title>7. <script language="javascript">8. function ale()9. {10. alert("这是一个警告对话框!");11. }12. function firm()13. {14. if(confirm("更多信息请到我的博客去?"))15. {16. location.href="http://yarin.javaeye.com";17. }18. else19. {20. alert("你选择了不去!");21. }22. }23. function prom()24. {25. var str=prompt("演示一个带输入的对话框","这里输入你的信息");26. if(str)27. {28. alert("谢谢使用,你输入的是:"+ str)29. }30. }31. </script>32. </head>33. <body>34. <p>下面我们演示3种对话框</p>35. <p>警告、提醒对话框</p>36. <p>37. <input type="submit" name="Submit" value="提交" onclick="ale()" />38. </p>39. <p>带选择的对话框</p>40. <p>41. <input type="submit" name="Submit2" value="提交" onclick="firm()" />42. </p>43. <p>要求用户输入的对话框</p>44. <p>45. <input type="submit" name="Submit3" value="提交" onclick="prom()" />46. </p>47. </body>48. </html>
页面效果:
2) Android中布局的编写
1. <?xml version="1.0" encoding="utf-8"?>2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"3. android:orientation="vertical"4. android:layout_width="fill_parent"5. android:layout_height="fill_parent"6. >7. <LinearLayout 8. android:orientation="horizontal"9. android:layout_width="fill_parent" 10. android:layout_height="fill_parent" 11. android:animationCache="true" 12. android:layout_weight="9">13. <EditText 14. android:id="@+id/EditText01" 15. android:layout_width="wrap_content"16. android:layout_weight="9" 17. android:layout_height="wrap_content" 18. android:text="请输入网址"/>19. <Button android:id="@+id/Button01" 20. android:layout_width="wrap_content"21. android:layout_weight="1" 22. android:layout_height="wrap_content"23. android:text="连接" />24. </LinearLayout>25. <WebView 26. android:id="@+id/WebView01" 27. android:layout_width="fill_parent" 28. android:layout_height="fill_parent" 29. android:layout_weight="1"30. /> 31. </LinearLayout>
3) 编写自定义对话框的布局 新建prom_dialog.xml文件,在其中自定义一个带输入的对话框由TextView和EditText构成
1. <?xml version="1.0" encoding="utf-8"?>2. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"3. android:gravity="center_horizontal"4. android:orientation="vertical"5. android:layout_width="fill_parent"6. android:layout_height="wrap_content"7. >8. <TextView 9. android:id="@+id/TextView_PROM"10. android:layout_width="fill_parent"11. android:layout_height="wrap_content"/>12. <EditText 13. android:id="@+id/EditText_PROM"14. android:layout_width="fill_parent"15. android:layout_height="wrap_content"16. android:selectAllOnFocus="true"17. android:scrollHorizontally="true"/>18. </LinearLayout>
4) 获取WebView控件,并进行相关的设置
5) 复写onKeyDown方法,当用户按返回键时,返回上一个加载的页面
6) 给WebView设置setWebChromeClient,并复写其中的方法
1. // 设置WebChromeClient2. mWebView.setWebChromeClient(new WebChromeClient() {3. 4. @Override5. // 处理javascript中的alert6. public boolean onJsAlert(WebView view, String url, String message,7. final JsResult result) {8. // 构建一个Builder来显示网页中的对话框9. Builder builder = new Builder(MainActivity.this);10. builder.setTitle("提示对话框");11. builder.setMessage(message);12. builder.setPositiveButton(android.R.string.ok,13. new AlertDialog.OnClickListener() {14. 15. @Override16. public void onClick(DialogInterface dialog,17. int which) {18. // TODO Auto-generated method stub19. // 点击确定按钮之后,继续执行网页中的操作20. result.confirm();21. }22. });23. builder.setNegativeButton(android.R.string.cancel,24. new OnClickListener() {25. 26. @Override27. public void onClick(DialogInterface dialog,28. int which) {29. result.cancel();30. 31. }32. });33. builder.setCancelable(false);34. builder.create();35. builder.show();36. 37. return true;38. 39. }40. 41. @Override42. //处理javascript中的confirm43. public boolean onJsConfirm(WebView view, String url,44. String message, final JsResult result) {45. Builder builder = new Builder(MainActivity.this);46. builder.setTitle("带选择的对话框");47. builder.setMessage(message);48. builder.setPositiveButton(android.R.string.ok,49. new AlertDialog.OnClickListener() {50. public void onClick(DialogInterface dialog, int which) {51. result.confirm();52. }53. });54. builder.setNegativeButton(android.R.string.cancel,55. new DialogInterface.OnClickListener() {56. public void onClick(DialogInterface dialog, int which) {57. result.cancel();58. }59. });60. builder.setCancelable(false);61. builder.create();62. builder.show();63. return true;64. }65. 66. @Override67. // 处理javascript中的prompt68. // message为网页中对话框的提示内容69. // defaultValue在没有输入时,默认显示的内容70. public boolean onJsPrompt(WebView view, String url, String message,71. String defaultValue, final JsPromptResult result) {72. // 自定义一个带输入的对话框由TextView和EditText构成73. LayoutInflater layoutInflater = LayoutInflater74. .from(MainActivity.this);75. final View dialogView = layoutInflater.inflate(76. R.layout.prom_dialog, null);77. 78. // 设置TextView对应网页中的提示信息79. ((TextView) dialogView.findViewById(R.id.TextView_PROM))80. .setText(message);81. // 设置EditText对应网页中的输入框82. ((EditText) dialogView.findViewById(R.id.EditText_PROM))83. .setText(defaultValue);84. //构建一个Builder来显示网页中的对话框 85. Builder builder = new Builder(MainActivity.this);86. //设置弹出框标题87. builder.setTitle("带输入的对话框");88. //设置弹出框的布局89. builder.setView(dialogView);90. //设置按键的监听91. builder.setPositiveButton(android.R.string.ok,92. new AlertDialog.OnClickListener() {93. 94. @Override95. public void onClick(DialogInterface dialog,96. int which) {97. 98. // 点击确定之后,取得输入的值,传给网页处理99. String value = ((EditText) dialogView100. .findViewById(R.id.EditText_PROM))101. .getText().toString();102. result.confirm(value);103. }104. 105. });106. 107. builder.setNegativeButton(android.R.string.cancel,108. new OnClickListener() {109. 110. @Override111. public void onClick(DialogInterface dialog,112. int which) {113. // TODO Auto-generated method stub114. result.cancel();115. }116. });117. 118. builder.setOnCancelListener(new DialogInterface.OnCancelListener() {119. public void onCancel(DialogInterface dialog) {120. result.cancel();121. }122. });123. builder.show();124. return true;125. }126. 127. @Override128. //设置网页加载的进度条129. public void onProgressChanged(WebView view, int newProgress) {130. MainActivity.this.getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress *100);131. super.onProgressChanged(view, newProgress);132. }133. 134. @Override135. public void onReceivedTitle(WebView view, String title) {136. MainActivity.this.setTitle(title);137. super.onReceivedTitle(view, title);138. }139. 140. });141. mButton.setOnClickListener(new View.OnClickListener() {142. 143. @Override144. public void onClick(View v) {145. //取得编辑框中我们输入的内容146. String url = mEditText.getText().toString().trim();147. //判断输入的内容是不是网址148. if(URLUtil.isNetworkUrl(url)){149. //装载网址150. mWebView.loadUrl(url);151. }else{152. mEditText.setText("输入网址错误,请重新输入");153. }154. }155. });156. 157. }
图1 dialog.html页面
图2 javascript的警告对话框
图3 javascript的confirm对话框
图4 javascript的prompt对话框
总结:在这个项目中,使用setWebChromeClient方法来为WebView设置一个WebChromeClient对象,来辅助WebView来处理Javascript的对话框等,图4是我们自定义的对话框,图2和图3我们都只需要监听按钮的点击事件,然后通过confirm和cancel方法将我们的操作传递给Javascript进行处理。当你在图1的界面,点击第一个按钮时,会打开图2的对话框,点击第二个按钮时,会打开图3的对话框,同时在这里点击确定,会跳转到另一个页面,当点击第三个按钮时,会打开图4对话框,并且可以输入内容。
更多Android进阶指南 可以扫码 解锁 《Android十大板块文档》
1.Android车载应用开发系统学习指南(附项目实战)
2.Android Framework学习指南,助力成为系统级开发高手
3.2023最新Android中高级面试题汇总+解析,告别零offer
4.企业级Android音视频开发学习路线+项目实战(附源码)
5.Android Jetpack从入门到精通,构建高质量UI界面
6.Flutter技术解析与实战,跨平台首要之选
7.Kotlin从入门到实战,全方面提升架构基础
8.高级Android插件化与组件化(含实战教程和源码)
9.Android 性能优化实战+360°全方面性能调优
10.Android零基础入门到精通,高手进阶之路
敲代码不易,关注一下吧。ღ( ´・ᴗ・` ) ?