详解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原生控件相互之间进行调用的知识就为大家分享完毕,欢迎留言讨论,相互学习。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持!