詳解JS與APP原生控件交互
“熱更新”、“熱部署”相信對于混合式開發(fā)的童鞋一定不陌生,那么APP怎么避免每次升級都要在APP應(yīng)用商店發(fā)布呢?這里就用到了混合式開發(fā)的概念,對于電商網(wǎng)站尤其顯得重要,不可能每次發(fā)布一個活動,都要發(fā)布一個現(xiàn)版本,當(dāng)然這樣對于Android還算可以,但是對于Ios呢?蘋果應(yīng)用商店每次審核的時間基本都在1~2周,這對于一個促銷活動來說審核時間實在太長。而混合式開發(fā)正好可以解決這個問題,基本的原理時,通過原生控件實現(xiàn)APP的主體結(jié)構(gòu),借助H5開發(fā)對應(yīng)的頁面,這樣每次發(fā)布活動,只需要在服務(wù)器端,將活動發(fā)布,便可以達到所有安裝用戶不升級便可查閱最新活動的效果。今天就為大家分享一下,如何實現(xiàn)JavaScript與APP原生控件交互。
一、首先為大家介紹的是JS與Android交互,首先讓大家看一下Android工程的目錄結(jié)構(gòu):

JSObject.java文件封裝了JS調(diào)用Android原生控件的方法;MainActivity.java是調(diào)用WebView控件實現(xiàn)網(wǎng)頁頁面加載,以及進行控件調(diào)用JS方法的封裝;test.html是我們加載的HTML頁面。接下來我們具體看一下實現(xiàn):
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鐵定調(diào)不了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調(diào)用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() {
//無參調(diào)用
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mHandler.post(new Runnable() {
@Override
public void run() {
mWebView.loadUrl("javascript:showNoMessage()");
}
});
}
});
//有參調(diào)用
button2.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mHandler.post(new Runnable() {
@Override
public void run() {
mWebView.loadUrl("javascript:showMessage('順帶給JS傳個參數(shù)')");
}
});
}
});
}
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網(wǎng)頁支持
webSettings.setUserAgentString("mac os");
webSettings.setDefaultTextEncodingName("utf-8");
//顯示本地js網(wǎng)頁
mWebView.loadUrl("file:///android_asset/test.html");
mWebView.setWebViewClient(WVClient);
mWebView.setWebChromeClient(chromeClient);
//注意第二個參數(shù)android,這個是JS網(wǎng)頁調(diào)用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調(diào)用android的方法
* @JavascriptInterface仍然必不可少
*/
public class JSObject {
private Context context;
public JSObject(Context context){
this.context = context;
}
//js調(diào)用無參方法
@JavascriptInterface
public void callNull(){
Toast.makeText(context, "JsCallAndroid", Toast.LENGTH_SHORT).show();
}
//js調(diào)用有參方法
@JavascriptInterface
public void callMessage(String data){
Toast.makeText(context, data, Toast.LENGTH_SHORT).show();
}
//js調(diào)用有參方法,參數(shù)類型:JSON
@JavascriptInterface
public void callJson(String data) throws JSONException{
JSONArray jsonArray = new JSONArray(data);
Toast.makeText(context, jsonArray.toString(), Toast.LENGTH_SHORT).show();
}
//js調(diào)用有參方法,參數(shù)類型: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')">不傳參數(shù)調(diào)用原生控件</li>
<li onclick="jsCallAndroid('2')">傳參數(shù)調(diào)用原生控件</li>
<li onclick="jsCallAndroid('3')">以JSON格式傳參數(shù)調(diào)用原生控件</li>
<li onclick="jsCallAndroid('4')">調(diào)用打電話服務(wù)</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\":\"滿藝網(wǎng)\", \"phone\":\"4008366069\"}]";
android.callJson(json);
break;
case "4":
android.callPhone("4008366069");
break;
}
}
function showNoMessage() {
alert("Android無參調(diào)用");
}
function showMessage(data) {
alert("Android有參調(diào)用-data:" + data);
}
</script>
這里因為需要實現(xiàn)一個撥打電話的功能,所以需要在AndroidManifest.xml文件中添加撥打電話的權(quán)限:
<uses-permission android:name="android.permission.CALL_PHONE" />
當(dāng)然這里加載的頁面是本地頁面,當(dāng)加載網(wǎng)絡(luò)頁面時需要添加請求網(wǎng)絡(luò)權(quán)限:
<uses-permission android:name="android.permission.INTERNET" />
好了關(guān)于JS與Android原生的控件進行相互調(diào)用的知識就介紹完了,最后附上DEML下載地址:http://pan.baidu.com/s/1eSza8Pc
二、JS與IOS原生交互,這里的開發(fā)語言選用的是Swift語言,版本是2.2。首先上工程目錄結(jié)構(gòu):

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 調(diào)用JS 方法 (無參數(shù))
@IBAction func swift_js_pargram(sender: AnyObject) {
self.context.evaluateScript("Swift_JS1()")
//self.webView.stringByEvaluatingJavaScriptFromString("Swift_JS1()") //此方法也可行
}
//Swift 調(diào)用JS 方法 (有參數(shù))
@IBAction func swift_js_nopargam(sender: AnyObject) {
self.context.evaluateScript("Swift_JS2('Ios' ,'Swift')")
//self.webView.stringByEvaluatingJavaScriptFromString("Swift_JS2('oc','swift')") //此方法也可行
}
//js調(diào)用Swift方法(無參)
func menthod1() {
print("JS調(diào)用了無參數(shù)swift方法")
let title = "操作提示";
let msg = "JS調(diào)用了無參數(shù)swift方法";
//創(chuàng)建提示信息
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調(diào)用Swift方法(有參數(shù))
func menthod2(str1: String, str2: String) {
print("JS調(diào)用了有參數(shù)swift方法:參數(shù)為\(str1),\(str2)")
//創(chuàng)建提示信息
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調(diào)用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調(diào)用了無參數(shù)swift方法---menthod1
let temp1: @convention(block) () ->() = {
self.menthod1()
}
//forKeyedSubscript:參數(shù)為JS調(diào)用方法名
self.context.setObject(unsafeBitCast(temp1, AnyObject.self), forKeyedSubscript: "test1")
//JS調(diào)用了有參數(shù)swift方法---menthod2
let temp2: @convention(block) () ->() = {
let array = JSContext.currentArguments()//這里接到的array中的內(nèi)容是JSValue類型
for object in array {
print("參數(shù):" + object.toString())
}
self.menthod2(array[0].toString(), str2: array[1].toString())
}
//forKeyedSubscript:參數(shù)為JS調(diào)用方法名
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調(diào)用我們公暴露的方法了。
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調(diào)用Swift模型方法
@objc class JSObjCModel: NSObject, JavaScriptSwiftDelegate {
weak var controller: UIViewController?
weak var jsContext: JSContext?
//JS無參調(diào)用Swift方法并返回處理結(jié)果
func callSystemCamera() {
print("js call objc method: callSystemCamera");
let jsFunc = self.jsContext?.objectForKeyedSubscript("jsFunc");
jsFunc?.callWithArguments([]);
}
//JS有參調(diào)用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有參調(diào)用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有參調(diào)用Swift方法并返回處理結(jié)果
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": "滿藝網(wǎng)"])
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()">不傳參數(shù)調(diào)用原生控件(常規(guī)方式)</li>
<li onclick="JS_Swift2()">傳參數(shù)調(diào)用原生控件(常規(guī)方式)</li>
<li onclick="JS_Swift3()">不傳參數(shù)調(diào)用原生控件,帶返回結(jié)果處理(模型注入)</li>
<li onclick="JS_Swift4()">傳參數(shù)調(diào)用原生控件(模型注入)</li>
<li onclick="JS_Swift5()">傳對象格式參數(shù)調(diào)用原生控件(模型注入)</li>
<li onclick="JS_Swift6()">傳對象格式參數(shù)調(diào)用原生控件,帶返回結(jié)果處理(模型注入)</li>
</ul>
</div>
<script>
function Swift_JS1() {
alert("Swift調(diào)用Js無參方法");
}
function Swift_JS2(name,msg) {
alert("Swift調(diào)用Js有參方法,name:"+name+";mes:"+msg);
}
function JS_Swift1() {
test1();
}
function JS_Swift2() {
test2('JsCallSwift', 'Js調(diào)用Swift方法');
}
function JS_Swift3() {
OCModel.callSystemCamera();
}
//js調(diào)用Swift方法,處理返回結(jié)果
function jsFunc() {
alert('JS調(diào)用Swift方法,無返回值結(jié)果處理');
}
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調(diào)用JS方法時,都會出錯哦。
function jsParamFunc (argument) {
alert("JS調(diào)用Swift方法,有返回值處理結(jié)果---name:"+argument['name'] + " age:" + argument['age']);
}
</script>
</body>
</html>
好了,對于JS與Ios原生控件之間進行相互調(diào)用的主要內(nèi)容就分享完畢了,DEML下載地址:https://pan.baidu.com/s/1gfJXr83
到這里關(guān)于JS與Android、IOS原生控件相互之間進行調(diào)用的知識就為大家分享完畢,歡迎留言討論,相互學(xué)習(xí)。
以上就是本文的全部內(nèi)容,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作能帶來一定的幫助,同時也希望多多支持腳本之家!
相關(guān)文章
Android中Gallery和ImageSwitcher的使用實例
今天小編就為大家分享一篇關(guān)于Android中Gallery和ImageSwitcher的使用實例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧2019-03-03
Android實現(xiàn)圖片壓縮(bitmap的六種壓縮方式)
Android中圖片是以bitmap形式存在的,這篇文章主要介紹了Android實現(xiàn)圖片壓縮(bitmap的六種壓縮方式),有興趣的可以了解一下。2017-02-02
深入淺出學(xué)習(xí)Android ListView基礎(chǔ)
這篇文章主要介紹了深入淺出的帶領(lǐng)大家學(xué)習(xí)Android ListView基礎(chǔ),ListView是安卓里常用的控件,本文介紹一下常用用法,以及優(yōu)化等方法,感興趣的小伙伴們可以參考一下2016-01-01
Android SharedPreferences實現(xiàn)記住密碼和自動登錄界面
本篇文章主要介紹了Android記住密碼和自動登錄界面的實現(xiàn)(SharedPreferences),具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-02-02
Android開發(fā)之機頂盒上gridview和ScrollView的使用詳解
這篇文章主要介紹了Android開發(fā)之機頂盒上gridview和ScrollView的使用詳解的相關(guān)資料,需要的朋友可以參考下2016-02-02
Android設(shè)置項目為系統(tǒng)APP方法
大家好,本篇文章講的是Android設(shè)置項目為系統(tǒng)APP介紹,感興趣的同學(xué)趕快來看一看吧,希望本篇文章對你起到幫助2021-11-11

