欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

详解JS与APP原生控件交互

程序员文章站 2024-03-31 10:56:34
 “热更新”、“热部署”相信对于混合式开发的童鞋一定不陌生,那么app怎么避免每次升级都要在app应用商店发布呢?这里就用到了混合式开发的概念,对于电商网站尤其显...

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

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

详解JS与APP原生控件交互

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。首先上工程目录结构:

详解JS与APP原生控件交互

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原生控件相互之间进行调用的知识就为大家分享完毕,欢迎留言讨论,相互学习。

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