JS與APP原生控件交互

  「熱更新」、「熱部署」相信對於混合式開發的童鞋必定不陌生,那麼APP怎麼避免每次升級都要在APP應用商店發佈呢?這裏就用到了混合式開發的概念,對於電商網站尤爲顯得重要,不可能每次發佈一個活動,都要發佈一個現版本,固然這樣對於Android還算能夠,可是對於Ios呢?蘋果應用商店每次審覈的時間基本都在1~2周,這對於一個促銷活動來講審覈時間實在太長。而混合式開發正好能夠解決這個問題,基本的原理時,經過原生控件實現APP的主體結構,藉助H5開發對應的頁面,這樣每次發佈活動,只須要在服務器端,將活動發佈,即可以達到全部安裝用戶不升級即可查閱最新活動的效果。今天就爲你們分享一下,如何實現JavaScript與APP原生控件交互。javascript

  1、首先爲你們介紹的是JS與Android交互,首先讓你們看一下Android工程的目錄結構:html

  

  JSObject.java文件封裝了JS調用Android原生控件的方法;MainActivity.java是調用WebView控件實現網頁頁面加載,以及進行控件調用JS方法的封裝;test.html是咱們加載的HTML頁面。接下來咱們具體看一下實現:java

  MainActivity.javaandroid

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)web

<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.javachrome

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頁面:json

<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文件中添加撥打電話的權限:swift

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

  固然這裏加載的頁面是本地頁面,當加載網絡頁面時須要添加請求網絡權限:服務器

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

  好了關於JS與Android原生的控件進行相互調用的知識就介紹完了,最後附上DEML下載地址:http://pan.baidu.com/s/1eR6l7Fk  提取密碼:4st0網絡

  2、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/1bpEFzaR 密碼: y5ms

  到這裏關於JS與Android、IOS原生控件相互之間進行調用的知識就爲你們分享完畢,歡迎留言討論,相互學習。

相關文章
相關標籤/搜索