详解JS与APP原生控件交互

“热更新”、“热部署”相信对于混合式开发的童鞋一定不陌生,那么APP怎么避免每次升级都要在APP应用商店发布呢?这里就用到了混合式开发的概念,对于电商网站尤其显得重要,不可能每次发布一个活动,都要发布一个现版本,当然这样对于Android还算可以,但是对于Ios呢?苹果应用商店每次审核的时间基本都在1~2周,这对于一个促销活动来说审核时间实在太长。而混合式开发正好可以解决这个问题,基本的原理时,通过原生控件实现APP的主体结构,借助H5开发对应的页面,这样每次发布活动,只需要在服务器端,将活动发布,便可以达到所有安装用户不升级便可查阅最新活动的效果。今天就为大家分享一下,如何实现JavaScript与APP原生控件交互。

一、首先为大家介绍的是JS与Android交互,首先让大家看一下Android工程的目录结构:

JSObject.java文件封装了JS调用Android原生控件的方法;MainActivity.java是调用WebView控件实现网页页面加载,以及进行控件调用JS方法的封装;test.html是我们加载的HTML页面。接下来我们具体看一下实现:

MainActivity.java

package com.chinaonenet.mywebview;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.widget.Button;
/**
 * SuppressLint一定要加上去!!!
 * 低版本可能没问题,高版本JS铁定调不了Android里面的方法
 */
@SuppressLint("SetJavaScriptEnabled")
public class MainActivity extends Activity {
 private Button button1,button2;
 private WebView mWebView;
 private MyWebViewClient WVClient;
 private WebSettings webSettings;
 private MyWebChromeClient chromeClient;
 //封装接收js调用Android的方法类
 private JSObject jsobject;
 //异步请求
 private Handler mHandler = new Handler();
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_main);
 init();
 initView();
 setButton();
 }
 private void setButton() {
 //无参调用
 button1.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
  mHandler.post(new Runnable() {
   @Override
   public void run() {
   mWebView.loadUrl("javascript:showNoMessage()");
   }
  });
  }
 });
 //有参调用
 button2.setOnClickListener(new OnClickListener() {
  @Override
  public void onClick(View v) {
  mHandler.post(new Runnable() {
   @Override
   public void run() {
   mWebView.loadUrl("javascript:showMessage('顺带给JS传个参数')");
   }
  });
  }
 });
 }
 private void init() {
 mWebView = (WebView) findViewById(R.id.webview);
 button1 = (Button)findViewById(R.id.button1);
 button2 = (Button)findViewById(R.id.button2);
 WVClient = new MyWebViewClient();
 chromeClient = new MyWebChromeClient();
 jsobject = new JSObject(MainActivity.this);
 }
 private void initView() {
 webSettings = mWebView.getSettings();
 webSettings.setJavaScriptEnabled(true);
 webSettings.setBuiltInZoomControls(true);
 webSettings.setSavePassword(false);
 //支持多种分辨率,需要js网页支持
 webSettings.setUserAgentString("mac os");
 webSettings.setDefaultTextEncodingName("utf-8");
 //显示本地js网页
 mWebView.loadUrl("file:///android_asset/test.html");
 mWebView.setWebViewClient(WVClient);
 mWebView.setWebChromeClient(chromeClient);
 //注意第二个参数android,这个是JS网页调用Android方法的一个类似ID的东西
 mWebView.addJavascriptInterface(jsobject, "android");
 }
}

页面的配置文件(activity_main.xml)

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 android:orientation="vertical"
tools:context="com.chinaonenet.mywebview.MainActivity" >
 <Button
 android:id="@+id/button1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignParentLeft="true"
 android:text="无参" />
 <Button
 android:id="@+id/button2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignParentRight="true"
 android:text="有参" />
 <WebView
 android:id="@+id/webview"
 android:layout_width="wrap_content"
 android:layout_height="match_parent"
 android:layout_alignParentLeft="true"
 android:layout_alignParentRight="true"
 android:layout_below="@+id/button2" />
 <TextView
 android:id="@+id/textView1"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_alignBaseline="@+id/button1"
 android:layout_alignBottom="@+id/button1"
 android:layout_centerHorizontal="true"
 android:text="js与android交互" />
</RelativeLayout>

JSObject.java

package com.chinaonenet.mywebview;
import org.json.JSONArray;
import org.json.JSONException;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.webkit.JavascriptInterface;
import android.widget.Toast;
/**
 * JS调用android的方法
 * @JavascriptInterface仍然必不可少
 */
public class JSObject {
 private Context context;
 public JSObject(Context context){
 this.context = context;
 }
 //js调用无参方法
 @JavascriptInterface
 public void callNull(){
 Toast.makeText(context, "JsCallAndroid", Toast.LENGTH_SHORT).show();
 }
 //js调用有参方法
 @JavascriptInterface
 public void callMessage(String data){
 Toast.makeText(context, data, Toast.LENGTH_SHORT).show();
 }
 //js调用有参方法,参数类型:JSON
 @JavascriptInterface
 public void callJson(String data) throws JSONException{
 JSONArray jsonArray = new JSONArray(data);
 Toast.makeText(context, jsonArray.toString(), Toast.LENGTH_SHORT).show();
 }
 //js调用有参方法,参数类型:JSON,获取电话号码拨打
 @JavascriptInterface
 public void callPhone(String data){
 context.startActivity(new Intent(Intent.ACTION_CALL, Uri.parse("tel:" + data)));
 }
}

加载的HTML页面:

<style>
 .main-wrap ul {
 width: 100%;
 display: inline-block;
 padding-top: 20px;
 }
 .main-wrap ul li {
  float: left;
  width: 100%;
  height: 40px;
  line-height: 40px;
  font-size: 14px;
  margin-bottom: 20px;
  background-color: #00D000;
  color: #fff;
  text-align: center;
  cursor: pointer;
 }
  .main-wrap ul li:active {
  opacity: 0.8;
  }
</style>
<div class="main-wrap">
 <ul class="postAndroid">
 <li onclick="jsCallAndroid('1')">不传参数调用原生控件</li>
 <li onclick="jsCallAndroid('2')">传参数调用原生控件</li>
 <li onclick="jsCallAndroid('3')">以JSON格式传参数调用原生控件</li>
 <li onclick="jsCallAndroid('4')">调用打电话服务</li>
 </ul>
</div>
<script>
 function jsCallAndroid(rel) {
 switch(rel){
  case "1":
  android.callNull();
  break;
  case "2":
  android.callMessage("javaScript操作Android原生");
  break;
  case "3":
  var json = "[{\"name\":\"满艺网\", \"phone\":\"4008366069\"}]";
  android.callJson(json);
  break;
  case "4":
  android.callPhone("4008366069");
  break;
 }
 }
 function showNoMessage() {
 alert("Android无参调用");
 }
 function showMessage(data) {
 alert("Android有参调用-data:" + data);
 }
</script>

这里因为需要实现一个拨打电话的功能,所以需要在AndroidManifest.xml文件中添加拨打电话的权限:

<uses-permission android:name="android.permission.CALL_PHONE" />

当然这里加载的页面是本地页面,当加载网络页面时需要添加请求网络权限:

<uses-permission android:name="android.permission.INTERNET" />

好了关于JS与Android原生的控件进行相互调用的知识就介绍完了,最后附上DEML下载地址:http://pan.baidu.com/s/1eSza8Pc

二、JS与IOS原生交互,这里的开发语言选用的是Swift语言,版本是2.2。首先上工程目录结构:

ViewController.swift

import UIKit
import JavaScriptCore
class ViewController: UIViewController {
 var context = JSContext()
 var jsContext: JSContext?
 @IBOutlet weak var webView: UIWebView!
 override func viewDidLoad() {
 super.viewDidLoad()
 webView.delegate = self//初始化webView
 loadJS()
 }
 /**
 *加载html页面
 */
 func loadJS() {
 let str = NSBundle.mainBundle().pathForResource("test", ofType: "html")
 let request = NSURLRequest(URL: NSURL(string: str!)!)
 webView.loadRequest(request)
 }
 //Swift 调用JS 方法 (无参数)
 @IBAction func swift_js_pargram(sender: AnyObject) {
 self.context.evaluateScript("Swift_JS1()")
 //self.webView.stringByEvaluatingJavaScriptFromString("Swift_JS1()") //此方法也可行
 }
 //Swift 调用JS 方法 (有参数)
 @IBAction func swift_js_nopargam(sender: AnyObject) {
 self.context.evaluateScript("Swift_JS2('Ios' ,'Swift')")
 //self.webView.stringByEvaluatingJavaScriptFromString("Swift_JS2('oc','swift')") //此方法也可行
 }
 //js调用Swift方法(无参)
 func menthod1() {
 print("JS调用了无参数swift方法")
 let title = "操作提示";
 let msg = "JS调用了无参数swift方法";
 //创建提示信息
 let alert = UIAlertController(title: title, message: msg, preferredStyle: .Alert)
 //确定按钮
 alert.addAction(UIAlertAction(title: "ok", style: .Default, handler: nil))
 //显示提示框
 self.presentViewController(alert, animated: true, completion: nil)
 }
 //js调用Swift方法(有参数)
 func menthod2(str1: String, str2: String) {
 print("JS调用了有参数swift方法:参数为\(str1),\(str2)")
 //创建提示信息
 let alert = UIAlertController(title: str1, message: str2, preferredStyle: .Alert)
 //确定按钮
 alert.addAction(UIAlertAction(title: "ok", style: .Default, handler: nil))
 //显示提示框
 self.presentViewController(alert, animated: true, completion: nil)
 }
 func webView(webView: UIWebView, didFailLoadWithError error: NSError) {
 print(error)
 }
}
//js调用Swift方法注入
extension ViewController: UIWebViewDelegate {
 func webView(webView: UIWebView, shouldStartLoadWithRequest request: NSURLRequest, navigationType: UIWebViewNavigationType) -> Bool {
 let str = NSBundle.mainBundle().pathForResource("test", ofType: "html")
 let request = NSURLRequest(URL: NSURL(string: str!)!)
 let connecntion = NSURLConnection(request: request, delegate: self)
 connecntion?.start()
 return true
 }
 func webViewDidStartLoad(webView: UIWebView) {
 print("webViewDidStartLoad----")
 }
 func webViewDidFinishLoad(webView: UIWebView) {
 self.context = webView.valueForKeyPath("documentView.webView.mainFrame.javaScriptContext") as! JSContext
 //JS调用了无参数swift方法---menthod1
 let temp1: @convention(block) () ->() = {
  self.menthod1()
 }
 //forKeyedSubscript:参数为JS调用方法名
 self.context.setObject(unsafeBitCast(temp1, AnyObject.self), forKeyedSubscript: "test1")
 //JS调用了有参数swift方法---menthod2
 let temp2: @convention(block) () ->() = {
  let array = JSContext.currentArguments()//这里接到的array中的内容是JSValue类型
  for object in array {
  print("参数:" + object.toString())
  }
  self.menthod2(array[0].toString(), str2: array[1].toString())
 }
 //forKeyedSubscript:参数为JS调用方法名
 self.context.setObject(unsafeBitCast(temp2, AnyObject.self), forKeyedSubscript: "test2")
 //模型注入的方法
 let model = JSObjCModel()
 model.controller = self
 model.jsContext = context
 self.jsContext = context
 //这一步是将OCModel这个模型注入到JS中,JS就可以通过OCModel调用我们公暴露的方法了。
 self.jsContext?.setObject(model, forKeyedSubscript: "OCModel")
 let url = NSBundle.mainBundle().URLForResource("test", withExtension: "html")
 self.jsContext?.evaluateScript(try? String(contentsOfURL: url!, encoding: NSUTF8StringEncoding));
 self.jsContext?.exceptionHandler = {
  (context, exception) in
  print("exception @", exception)
 }
 }
}
@objc protocol JavaScriptSwiftDelegate: JSExport {
 func callSystemCamera()
 func showAlert(title: String, msg: String)
 func callWithDict(dict: [String: AnyObject])
 func jsCallObjcAndObjcCallJsWithDict(dict: [String: AnyObject])
}
//js调用Swift模型方法
@objc class JSObjCModel: NSObject, JavaScriptSwiftDelegate {
 weak var controller: UIViewController?
 weak var jsContext: JSContext?
 //JS无参调用Swift方法并返回处理结果
 func callSystemCamera() {
 print("js call objc method: callSystemCamera");
 let jsFunc = self.jsContext?.objectForKeyedSubscript("jsFunc");
 jsFunc?.callWithArguments([]);
 }
 //JS有参调用Swift方法
 func showAlert(title: String, msg: String) {
 print("js call objc method: showAlert, title: %@", title, " msg: %@", msg)
 dispatch_async(dispatch_get_main_queue()) { () -> Void in
  let alert = UIAlertController(title: title, message: msg, preferredStyle: .Alert)
  alert.addAction(UIAlertAction(title: "ok", style: .Default, handler: nil))
  self.controller?.presentViewController(alert, animated: true, completion: nil)
 }
 }
 //JS有参调用Swift方法
 func callWithDict(dict: [String : AnyObject]) {
 print("js call objc method: callWithDict, args: %@", dict)
 let alert = UIAlertController(title: "消息提示", message: "查看控制台打印信息", preferredStyle: .Alert)
 alert.addAction(UIAlertAction(title: "ok", style: .Default, handler: nil))
 self.controller?.presentViewController(alert, animated: true, completion: nil)
 }
 //JS有参调用Swift方法并返回处理结果
 func jsCallObjcAndObjcCallJsWithDict(dict: [String : AnyObject]) {
 print("js call objc method: jsCallObjcAndObjcCallJsWithDict, args: %@", dict)
 let jsParamFunc = self.jsContext?.objectForKeyedSubscript("jsParamFunc");
 let dict = NSDictionary(dictionary: ["age": 2, "height": 178, "name": "满艺网"])
 jsParamFunc?.callWithArguments([dict])
 }
}
extension ViewController: NSURLConnectionDelegate,NSURLConnectionDataDelegate {
 func connection(connection: NSURLConnection, didReceiveData data: NSData) {
 print("didReceiveData\(data)")
 }
 func connection(connection: NSURLConnection, willSendRequest request: NSURLRequest, redirectResponse response: NSURLResponse?) -> NSURLRequest? {
 print("request:\(request)response:\(response)")
 return request
 }
 func connection(connection: NSURLConnection, didFailWithError error: NSError) {

 }
}
//MARK: - allowsAnyHTTPSCertificateForHost
extension NSURLRequest {
 static func allowsAnyHTTPSCertificateForHost(host: String) -> Bool {
 return true
 }
}

test.html

<html>
 <head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
 <title>JSAndIos</title>
 </head>
 <style>
 .main-wrap ul {
  width: 100%;
  display: inline-block;
  padding-top: 20px;
 }
 .main-wrap ul li {
  float: left;
  width: 100%;
  height: 40px;
  line-height: 40px;
  font-size: 14px;
  margin-bottom: 20px;
  background-color: #00D000;
  color: #fff;
  text-align: center;
  cursor: pointer;
 }
 .main-wrap ul li:active {
  opacity: 0.8;
 }
 </style>
 <body bgcolor="#dddd">
  <div class="main-wrap">
  <ul class="postAndroid">
   <li onclick="JS_Swift1()">不传参数调用原生控件(常规方式)</li>
   <li onclick="JS_Swift2()">传参数调用原生控件(常规方式)</li>
   <li onclick="JS_Swift3()">不传参数调用原生控件,带返回结果处理(模型注入)</li>
   <li onclick="JS_Swift4()">传参数调用原生控件(模型注入)</li>
   <li onclick="JS_Swift5()">传对象格式参数调用原生控件(模型注入)</li>
   <li onclick="JS_Swift6()">传对象格式参数调用原生控件,带返回结果处理(模型注入)</li>
  </ul>
  </div>
  <script>
  function Swift_JS1() {
  alert("Swift调用Js无参方法");
  }
  function Swift_JS2(name,msg) {
  alert("Swift调用Js有参方法,name:"+name+";mes:"+msg);
  }
  function JS_Swift1() {
  test1();
  }
  function JS_Swift2() {
  test2('JsCallSwift', 'Js调用Swift方法');
  }
  function JS_Swift3() {
  OCModel.callSystemCamera();
  }
  //js调用Swift方法,处理返回结果
  function jsFunc() {
  alert('JS调用Swift方法,无返回值结果处理');
  }
  function JS_Swift4() {
  OCModel.showAlertMsg('js send title', 'js send message');
  }
  function JS_Swift5() {
  OCModel.callWithDict({ 'name': 'testname', 'age': 10, 'height': 170 });
  }
  function JS_Swift6() {
OCModel.jsCallObjcAndObjcCallJsWithDict({ 'name': 'testname', 'age': 10, 'height': 170 });
  }
  //注意哦,如果JS写错,可能在OC调用JS方法时,都会出错哦。
  function jsParamFunc (argument) {
  alert("JS调用Swift方法,有返回值处理结果---name:"+argument['name'] + " age:" + argument['age']);
  }
  </script>
 </body>
</html>

好了,对于JS与Ios原生控件之间进行相互调用的主要内容就分享完毕了,DEML下载地址:https://pan.baidu.com/s/1gfJXr83

到这里关于JS与Android、IOS原生控件相互之间进行调用的知识就为大家分享完毕,欢迎留言讨论,相互学习。

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持我们!

(0)

相关推荐

  • ActiveX控件与Javascript之间的交互示例

    1.ActiveX向Javascript传参 复制代码 代码如下: <script language="javascript" for="objectname" event="fun1(arg)"> fun2(arg); </script> objectname为ActiveX控件名,通过<object>标签里的id属性设定,如下: 复制代码 代码如下: <object id="objectna

  • android中webview控件和javascript交互实例

    当我们要实现丰富的图文混排效果的时候,我们一般会使用webview,这是一个功能十分强大的的控件,来看看官方的解释: 复制代码 代码如下: A View that displays web pages. This class is the basis upon which you can roll your own web browser or simply display some online content within your Activity. It uses the WebKit

  • 详解JS与APP原生控件交互

    "热更新"."热部署"相信对于混合式开发的童鞋一定不陌生,那么APP怎么避免每次升级都要在APP应用商店发布呢?这里就用到了混合式开发的概念,对于电商网站尤其显得重要,不可能每次发布一个活动,都要发布一个现版本,当然这样对于Android还算可以,但是对于Ios呢?苹果应用商店每次审核的时间基本都在1~2周,这对于一个促销活动来说审核时间实在太长.而混合式开发正好可以解决这个问题,基本的原理时,通过原生控件实现APP的主体结构,借助H5开发对应的页面,这样每次发布活

  • 详解WPF如何在基础控件上显示Loading等待动画

    WPF 如何在基础控件上显示 Loading 等待动画 框架使用.NET4 至 .NET6: Visual Studio 2022; 使用方式需引入命名空间后设置控件的附加属性 wd:Loading.IsShow="true",即可显示默认等待动画效果如下: 如需自定义 Loading 一定要 先设置 wd:Loading.Child 在设置 IsShow="true" . 显示不同 Loading 内容需 wd:Loading.Child ={x:Static w

  • 详解iOS开发中UItableview控件的数据刷新功能的实现

    实现UItableview控件数据刷新 一.项目文件结构和plist文件 二.实现效果 1.说明:这是一个英雄展示界面,点击选中行,可以修改改行英雄的名称(完成数据刷新的操作). 运行界面: 点击选中行: 修改数据后自动刷新: 三.代码示例 数据模型部分: YYheros.h文件 复制代码 代码如下: // //  YYheros.h //  10-英雄展示(数据刷新) // //  Created by apple on 14-5-29. //  Copyright (c) 2014年 itc

  • 详解iOS开发中UIPickerView控件的使用方法

    UIPickerView控件在给用户选择某些特定的数据时经常使用到,这里演示一个简单的选择数据,显示在UITextField输入框里,把UIPickerView作为输入View,用Toolbar作为选定数据的按钮.和其他UITableView控件相似,UIPickerView也需要数据源. 我们要实现的效果如下: 下面开始使用的步骤. 1.打开XCode 4.3.2,新建一个Single View Application ,命名为PickerViewDemo,Company Identifier

  • 详解如何实现Element树形控件Tree在懒加载模式下的动态更新

    Element提供的Tree树形控件,可以用清晰的层级结构展示信息,还可以展开或折叠.Tree支持两种加载模式:一次性加载全部树节点和懒加载模式.所谓懒加载模式,是指当需要展开父节点时才渲染子节点.懒加载模式的应用场景适合树节点数据量大的情形,在一定程度上可以优化图形用户界面的响应效率以及提升用户体验.但是,懒加载模式对数据动态刷新应用需求的支持不尽如意.树形控件节点一旦展开就缓存在本地,后续不会再继续更新和刷新节点数据.本文将介绍如何实现Element树形控件Tree在懒加载模式下的动态更新.

  • 详解bootstrap-fileinput文件上传控件的亲身实践

    经理让我帮服务器开发人员开发一个上传文件功能界面,我就想着以前使用过bootstrap-fileinput插件进行文件上传,很不错.赶紧就撸起来了. 1.下载压缩包.插件地址https://github.com/kartik-v/bootstrap-fileinput/ ,下载压缩包解压之后,拿出fileinput.min.js.fileinput.min.css.和中文需要引用的插件zh.js,因为这款插件默认的语言是英语.把这几个文件引入进页面 2.文件的引入顺序 引入bootstrap.m

  • JS调用Android、Ios原生控件

    在上一篇博客(详解JS与APP原生控件交互)中已经和大家聊了,关于JS与Android.Ios原生控件之间相互通信的详细代码实现,今天我们一起聊一下JS调用Android.Ios通信的相同点和不同点,以便帮助我们在进行混合式开发时,提高代码质量,实现两者在网页端代码的统一. 首先我们先看一下Ios调用JS的方法实现: //无参调用 function SwiftCallJs1(){} //有参调用 function SwiftCallJs2(name, message){} 紧接着我们看一下And

  • 详解JS浏览器事件模型

    什么是事件 我想你很可能听说过事件驱动, 但是事件驱动到底是什么?为什么说浏览器是事件驱动的呢? 事件驱动通俗地来说就是什么都抽象为事件. 一次点击是一个事件 键盘按下是一个事件 一个网络请求成功是一个事件 页面加载是一个事件 页面报错是一个事件 浏览器依靠事件来驱动APP运行下去,如果没有了事件驱动,那么APP会直接从头到尾运行完,然后结束,事件驱动是浏览器的基石. 一个简单的例子 其实现实中的红绿灯就是一种事件,它告诉我们现在是红灯状态,绿灯状态,还是黄灯状态. 我们需要根据这个事件自己去完

  • Qiankun原理详解JS沙箱是如何做隔离

    目录 前言 复习一下沙箱 SanpshotSandbox LegacySandbox ProxySandbox 隔离原理 XXX is undefined 总结 前言 相信大家也知道 qiankun 有 SnapshotSandbox, LegacySandbox 和 ProxySandbox 这些沙箱,而它们又可以分为单例和多例两种模式,网上也有很多文章对其进行介绍. 但这些文章的关注点都是沙箱的环境恢复做的事,那 JS 的隔离到底是怎么做到的呢? 换个问法,当我写 window.a = 1

  • 详解js中的几种常用设计模式

    工厂模式 function createPerson(name, age){ var o = new Object(); // 创建一个对象 o.name = name; o.age = age; o.sayName = function(){ console.log(this.name) } return o; // 返回这个对象 } var person1 = createPerson('ccc', 18) var person2 = createPerson('www', 18) 工厂函数

随机推荐