├── .gitignore ├── Android ├── .gitignore ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── ganwenpeng │ │ │ └── jsinterface │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ ├── intercept.html │ │ │ ├── jsbridge.html │ │ │ └── jsinterface.html │ │ ├── java │ │ │ └── com │ │ │ │ └── ganwenpeng │ │ │ │ └── jsinterface │ │ │ │ ├── JSApplication.java │ │ │ │ ├── activity │ │ │ │ ├── BridgeWebViewActivity.java │ │ │ │ └── InterceptWebViewActivity.java │ │ │ │ ├── model │ │ │ │ ├── bean │ │ │ │ │ ├── JsBridgeResponse.java │ │ │ │ │ └── LoginBean.java │ │ │ │ ├── home │ │ │ │ │ └── bean │ │ │ │ │ │ └── HomeItemBean.java │ │ │ │ └── jsinterface │ │ │ │ │ ├── JsInterfaceLogic.java │ │ │ │ │ └── bean │ │ │ │ │ └── User.java │ │ │ │ └── page │ │ │ │ ├── base │ │ │ │ ├── BaseActivity.java │ │ │ │ ├── BaseFragment.java │ │ │ │ ├── BasePresenter.java │ │ │ │ ├── BasePresenterInterface.java │ │ │ │ └── BaseView.java │ │ │ │ ├── home │ │ │ │ ├── HomeActivity.java │ │ │ │ ├── HomeContract.java │ │ │ │ ├── HomeFragment.java │ │ │ │ └── HomePresenter.java │ │ │ │ ├── intercept │ │ │ │ ├── InterceptActivity.java │ │ │ │ ├── InterceptContract.java │ │ │ │ ├── InterceptFragment.java │ │ │ │ └── InterceptPresenter.java │ │ │ │ ├── jsbridge │ │ │ │ ├── JsBridgeActivity.java │ │ │ │ ├── JsBridgeContract.java │ │ │ │ ├── JsBridgeFragment.java │ │ │ │ └── JsBridgePresenter.java │ │ │ │ └── jsinterface │ │ │ │ ├── JsInterfaceActivity.java │ │ │ │ ├── JsInterfaceContract.java │ │ │ │ ├── JsInterfaceFragment.java │ │ │ │ └── JsInterfacePresenter.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ ├── common │ │ │ │ ├── drawable-xhdpi │ │ │ │ │ └── back.png │ │ │ │ └── layout │ │ │ │ │ ├── activity_blank.xml │ │ │ │ │ └── layout_toolbar.xml │ │ │ ├── home │ │ │ │ └── layout │ │ │ │ │ ├── fragment_home.xml │ │ │ │ │ └── layout_home_item.xml │ │ │ ├── intercept │ │ │ │ └── layout │ │ │ │ │ └── fragment_intercept_webview.xml │ │ │ └── jsbridge │ │ │ │ └── layout │ │ │ │ └── fragment_bridge_webview.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── ganwenpeng │ │ └── jsinterface │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── LICENSE ├── README.md ├── android-home.png ├── iOS ├── JsInterface.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ └── contents.xcworkspacedata ├── JsInterface.xcworkspace │ └── contents.xcworkspacedata ├── JsInterface │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── Class │ │ ├── AppDelegate │ │ │ ├── AppDelegate.h │ │ │ └── AppDelegate.m │ │ ├── Category │ │ │ ├── NSDictionary+Json.h │ │ │ ├── NSDictionary+Json.m │ │ │ ├── NSURL+Params.h │ │ │ └── NSURL+Params.m │ │ ├── Model │ │ │ ├── JSContextModel.h │ │ │ ├── JSContextModel.m │ │ │ ├── JSHomeModel.h │ │ │ └── JSHomeModel.m │ │ └── Pages │ │ │ ├── Home │ │ │ ├── Controller │ │ │ │ ├── JSHomeViewController.h │ │ │ │ └── JSHomeViewController.m │ │ │ └── View │ │ │ │ ├── JSHomeTableViewCell.h │ │ │ │ ├── JSHomeTableViewCell.m │ │ │ │ └── JSHomeTableViewCell.xib │ │ │ ├── UIWebView │ │ │ └── Controller │ │ │ │ ├── JSUIBridgeViewController.h │ │ │ │ ├── JSUIBridgeViewController.m │ │ │ │ ├── JSUIInterceptViewController.h │ │ │ │ ├── JSUIInterceptViewController.m │ │ │ │ ├── JSUIJsContextViewController.h │ │ │ │ ├── JSUIJsContextViewController.m │ │ │ │ ├── JSUIWebViewController.h │ │ │ │ └── JSUIWebViewController.m │ │ │ └── WKWebView │ │ │ └── Controller │ │ │ ├── JSWKBridgeViewController.h │ │ │ ├── JSWKBridgeViewController.m │ │ │ ├── JSWKInterceptViewController.h │ │ │ ├── JSWKInterceptViewController.m │ │ │ ├── JSWKWebKitViewController.h │ │ │ ├── JSWKWebKitViewController.m │ │ │ ├── JSWKWebViewController.h │ │ │ └── JSWKWebViewController.m │ ├── Info.plist │ ├── Launch Screen.storyboard │ ├── Resource │ │ └── html │ │ │ ├── intercept.html │ │ │ ├── jsbridge.html │ │ │ ├── jscontext.html │ │ │ └── webkit.html │ └── main.m └── Podfile └── ios-home.png /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xccheckout 23 | *.xcscmblueprint 24 | 25 | 26 | iOSInjectionProject/ 27 | iOS/.idea 28 | iOS/Pods 29 | iOS/Podfile.lock 30 | 31 | Android/.idea 32 | -------------------------------------------------------------------------------- /Android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /Android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | defaultConfig { 6 | applicationId "com.ganwenpeng.jsbridge" 7 | minSdkVersion 19 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | 20 | sourceSets { 21 | main { 22 | jniLibs.srcDirs = ['libs'] 23 | res.srcDirs = [ 24 | 'src/main/res', 25 | 'src/main/res/layout', 26 | 'src/main/res/layout/common', 27 | 'src/main/res/layout/home', 28 | 'src/main/res/layout/intercept', 29 | 'src/main/res/layout/jsbridge' 30 | ] 31 | } 32 | } 33 | } 34 | 35 | dependencies { 36 | implementation fileTree(dir: 'libs', include: ['*.jar']) 37 | 38 | implementation 'com.android.support:support-v4:26.1.0' 39 | implementation 'com.android.support:palette-v7:26.1.0' 40 | implementation 'com.android.support:cardview-v7:26.1.0' 41 | implementation 'com.android.support:gridlayout-v7:26.1.0' 42 | implementation 'com.android.support:appcompat-v7:26.1.0' 43 | implementation 'com.android.support:support-v13:26.1.0' 44 | implementation 'com.android.support:design:26.1.0' 45 | implementation 'com.android.support:multidex:1.0.2' 46 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 47 | 48 | 49 | testImplementation 'junit:junit:4.12' 50 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 51 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 52 | 53 | // View层:Butter Knife 54 | implementation 'com.jakewharton:butterknife:8.8.1' 55 | annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1' 56 | 57 | // 支持JsBridge的WebView 58 | implementation 'com.github.lzyzsd:jsbridge:1.0.4' 59 | 60 | // gson 61 | implementation 'com.google.code.gson:gson:2.8.2' 62 | 63 | // base quick adapter 64 | implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.34' 65 | 66 | // Logger 67 | implementation 'com.orhanobut:logger:2.1.1' 68 | } 69 | -------------------------------------------------------------------------------- /Android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /Android/app/src/androidTest/java/com/ganwenpeng/jsinterface/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.ganwenpeng.jsinterface; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.ganwenpeng.jsbridge", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 32 | 33 | 34 | 37 | 38 | 39 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /Android/app/src/main/assets/intercept.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 拦截跳转实现通信 5 | 6 | 8 | 9 | 10 | 31 | 32 | 33 |

嵌入页区域

34 |

以拦截跳转链接的方式实现

35 | 36 | 37 | 38 | 39 |

40 | 
61 | 
62 | 
63 | 


--------------------------------------------------------------------------------
/Android/app/src/main/assets/jsbridge.html:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  3 | 
  4 |     JSBridge Test
  5 |     
  6 |     
  8 |     
  9 | 
 10 |     
 31 | 
 32 | 
 33 | 

嵌入页区域

34 |

以JSBridge的方式实现

35 | 36 | 37 | 38 |

 39 | 
 40 | 
140 | 
141 | 
142 | 


--------------------------------------------------------------------------------
/Android/app/src/main/assets/jsinterface.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 |     拦截跳转实现通信
 5 |     
 6 |     
 8 |     
 9 | 
10 |     
31 | 
32 | 
33 | 

嵌入页区域

34 |

以JavaScriptInterface的方式实现

35 | 36 | 37 | 38 | 39 | 40 | 41 |

42 | 
67 | 
68 | 
69 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/JSApplication.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface;
 2 | 
 3 | import android.app.Application;
 4 | 
 5 | import com.orhanobut.logger.AndroidLogAdapter;
 6 | import com.orhanobut.logger.FormatStrategy;
 7 | import com.orhanobut.logger.Logger;
 8 | import com.orhanobut.logger.PrettyFormatStrategy;
 9 | 
10 | 
11 | public class JSApplication extends Application {
12 |     @Override
13 |     public void onCreate() {
14 |         super.onCreate();
15 | 
16 |         initLogger();
17 |     }
18 | 
19 |     /**
20 |      * 初始化日志框架
21 |      */
22 |     private void initLogger() {
23 |         // 格式化log,添加全局tag【tugou-log】,查看日志时筛选 tugou-log即可
24 |         FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
25 |                 .tag("jsinterface-log")
26 |                 .build();
27 | 
28 |         Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy) {
29 |             @Override
30 |             public boolean isLoggable(int priority, String tag) {
31 |                 return BuildConfig.DEBUG;
32 |             }
33 |         });
34 |         Logger.d("initing");
35 |     }
36 | }
37 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/activity/BridgeWebViewActivity.java:
--------------------------------------------------------------------------------
  1 | package com.ganwenpeng.jsinterface.activity;
  2 | 
  3 | import android.net.Uri;
  4 | import android.os.Bundle;
  5 | import android.support.v7.app.AppCompatActivity;
  6 | import android.text.TextUtils;
  7 | import android.webkit.WebResourceRequest;
  8 | import android.webkit.WebView;
  9 | import android.webkit.WebViewClient;
 10 | 
 11 | import com.ganwenpeng.jsinterface.R;
 12 | import com.ganwenpeng.jsinterface.model.bean.JsBridgeResponse;
 13 | import com.ganwenpeng.jsinterface.model.bean.LoginBean;
 14 | import com.github.lzyzsd.jsbridge.BridgeHandler;
 15 | import com.github.lzyzsd.jsbridge.BridgeWebView;
 16 | import com.github.lzyzsd.jsbridge.CallBackFunction;
 17 | import com.google.gson.Gson;
 18 | import com.google.gson.JsonSyntaxException;
 19 | 
 20 | import java.util.HashMap;
 21 | import java.lang.String;
 22 | 
 23 | import butterknife.BindView;
 24 | import butterknife.ButterKnife;
 25 | import butterknife.Unbinder;
 26 | 
 27 | public class BridgeWebViewActivity extends AppCompatActivity {
 28 | 
 29 |     @BindView(R.id.web)
 30 |     BridgeWebView mWebView;
 31 |     private Unbinder mUnbinder;
 32 | 
 33 |     @Override
 34 |     protected void onCreate(Bundle savedInstanceState) {
 35 |         super.onCreate(savedInstanceState);
 36 |         setContentView(R.layout.fragment_bridge_webview);
 37 |         mUnbinder = ButterKnife.bind(this);
 38 | 
 39 |         initWebView();
 40 | 
 41 |         mWebView.loadUrl("file:///android_asset/jsbridge-test.html");
 42 |     }
 43 | 
 44 |     private void initWebView() {
 45 |         mWebView.registerHandler("getOS", new BridgeHandler() {
 46 |             @Override
 47 |             public void handler(String data, CallBackFunction function) {
 48 |                 HashMap dataMap = new HashMap<>();
 49 |                 dataMap.put("os", "android");
 50 | 
 51 |                 JsBridgeResponse response = new JsBridgeResponse(0, "", dataMap);
 52 |                 function.onCallBack(new Gson().toJson(response));
 53 |             }
 54 |         });
 55 | 
 56 |         mWebView.registerHandler("login", new BridgeHandler() {
 57 |             @Override
 58 |             public void handler(String data, CallBackFunction function) {
 59 |                 Gson gson = new Gson();
 60 | 
 61 |                 if (TextUtils.isEmpty(data)) {
 62 |                     JsBridgeResponse response = new JsBridgeResponse(-1, "调用参数有误", null);
 63 |                     function.onCallBack(gson.toJson(response));
 64 |                     return;
 65 |                 }
 66 | 
 67 |                 LoginBean loginBean;
 68 |                 try {
 69 |                     loginBean = gson.fromJson(data, LoginBean.class);
 70 |                 } catch (JsonSyntaxException e) {
 71 |                     return;
 72 |                 }
 73 |                 if (loginBean == null) {
 74 |                     JsBridgeResponse response = new JsBridgeResponse(-1, "调用参数有误", null);
 75 |                     function.onCallBack(gson.toJson(response));
 76 |                     return;
 77 |                 }
 78 | 
 79 |                 JsBridgeResponse response = new JsBridgeResponse(0, "登录成功", String.format("执行登录操作,账号为:%s、密码为:%s", loginBean.getAccount(), loginBean.getPassword()));
 80 |                 function.onCallBack(gson.toJson(response));
 81 |             }
 82 |         });
 83 | 
 84 |         WebViewClient webViewClient = new WebViewClient() {
 85 |             // 老方法
 86 |             @Override
 87 |             public boolean shouldOverrideUrlLoading(WebView view, String url) {
 88 |                 if (url.contains("app://login")) {
 89 |                     Uri uri = Uri.parse(url);
 90 |                     String account = uri.getQueryParameter("account");
 91 |                     String password = uri.getQueryParameter("password");
 92 | 
 93 |                     // 这里写执行登录的代码
 94 | 
 95 |                     // 完了之后返回true,告诉WebView该链接已经被处理了,不要继续加载这个URL
 96 |                     return true;
 97 |                 }
 98 |                 return super.shouldOverrideUrlLoading(view, url);
 99 |             }
100 | 
101 |             // 新方法,API21之后支持
102 |             @Override
103 |             public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
104 |                 Uri uri = request.getUrl();
105 |                 if (uri.getScheme().contentEquals("app")) {
106 | 
107 |                     if (uri.getHost().contentEquals("login")) {
108 | 
109 |                         String account = uri.getQueryParameter("account");
110 |                         String password = uri.getQueryParameter("password");
111 | 
112 |                         // 这里写执行登录的代码
113 | 
114 |                         // 完了之后返回true,告诉WebView该链接已经被处理了,不要继续加载这个URL
115 |                         return true;
116 |                     }
117 | 
118 |                     return true;
119 |                 }
120 | 
121 |                 return super.shouldOverrideUrlLoading(view, request);
122 |             }
123 |         };
124 | 
125 |         mWebView.setWebViewClient(webViewClient);
126 |     }
127 | 
128 | 
129 |     @Override
130 |     protected void onDestroy() {
131 |         super.onDestroy();
132 | 
133 |         if (mUnbinder != null) {
134 |             mUnbinder.unbind();
135 |             mUnbinder = null;
136 |         }
137 |     }
138 | }
139 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/activity/InterceptWebViewActivity.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.activity;
 2 | 
 3 | import android.support.v7.app.AppCompatActivity;
 4 | 
 5 | /**
 6 |  * @author gangan
 7 |  */
 8 | public class InterceptWebViewActivity extends AppCompatActivity {
 9 | 
10 | }
11 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/model/bean/JsBridgeResponse.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.model.bean;
 2 | 
 3 | public class JsBridgeResponse {
 4 |     private int error;
 5 |     private String message;
 6 |     private Object data;
 7 | 
 8 |     public JsBridgeResponse() {
 9 |     }
10 | 
11 |     public JsBridgeResponse(int error, String message, Object data) {
12 |         this.error = error;
13 |         this.message = message;
14 |         this.data = data;
15 |     }
16 | 
17 |     public int getError() {
18 |         return error;
19 |     }
20 | 
21 |     public void setError(int error) {
22 |         this.error = error;
23 |     }
24 | 
25 |     public String getMessage() {
26 |         return message;
27 |     }
28 | 
29 |     public void setMessage(String message) {
30 |         this.message = message;
31 |     }
32 | 
33 |     public Object getData() {
34 |         return data;
35 |     }
36 | 
37 |     public void setData(Object data) {
38 |         this.data = data;
39 |     }
40 | 
41 |     @Override
42 |     public String toString() {
43 |         return "JsBridgeResponse{" +
44 |                 "error=" + error +
45 |                 ", message='" + message + '\'' +
46 |                 ", data=" + data +
47 |                 '}';
48 |     }
49 | }
50 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/model/bean/LoginBean.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.model.bean;
 2 | 
 3 | public class LoginBean {
 4 |     private String account;
 5 |     private String password;
 6 | 
 7 |     public String getAccount() {
 8 |         return account;
 9 |     }
10 | 
11 |     public void setAccount(String account) {
12 |         this.account = account;
13 |     }
14 | 
15 |     public String getPassword() {
16 |         return password;
17 |     }
18 | 
19 |     public void setPassword(String password) {
20 |         this.password = password;
21 |     }
22 | 
23 |     @Override
24 |     public String toString() {
25 |         return "LoginBean{" +
26 |                 "account='" + account + '\'' +
27 |                 ", password='" + password + '\'' +
28 |                 '}';
29 |     }
30 | }
31 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/model/home/bean/HomeItemBean.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.model.home.bean;
 2 | 
 3 | import com.ganwenpeng.jsinterface.page.base.BaseActivity;
 4 | 
 5 | public class HomeItemBean {
 6 |     /**
 7 |      * js调用原生的方式
 8 |      */
 9 |     private final String jsCallNative;
10 | 
11 |     /**
12 |      * 原生调用js的方式
13 |      */
14 |     private final String nativeCallJs;
15 |     /**
16 |      * 点击该行之后跳转的界面
17 |      */
18 |     private final Class targetClass;
19 | 
20 |     public String getJsCallNative() {
21 |         return jsCallNative;
22 |     }
23 | 
24 |     public String getNativeCallJs() {
25 |         return nativeCallJs;
26 |     }
27 | 
28 |     public Class getTargetClass() {
29 |         return targetClass;
30 |     }
31 | 
32 |     public HomeItemBean(String jsCallNative, String nativeCallJs, Class targetClass) {
33 |         this.jsCallNative = jsCallNative;
34 |         this.nativeCallJs = nativeCallJs;
35 |         this.targetClass = targetClass;
36 |     }
37 | }
38 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/model/jsinterface/JsInterfaceLogic.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.model.jsinterface;
 2 | 
 3 | import android.webkit.JavascriptInterface;
 4 | 
 5 | import com.ganwenpeng.jsinterface.page.base.BaseFragment;
 6 | 
 7 | import org.json.JSONObject;
 8 | 
 9 | import java.util.HashMap;
10 | 
11 | public class JsInterfaceLogic {
12 |     private BaseFragment mFragment;
13 | 
14 |     public JsInterfaceLogic(BaseFragment mFragment) {
15 |         this.mFragment = mFragment;
16 |     }
17 | 
18 |     @JavascriptInterface
19 |     public void login(String account, String password) {
20 |         mFragment.showNativeMessage(String.format("执行登录操作,账号为:%s,密码为:%s", account, password));
21 |     }
22 | 
23 |     @JavascriptInterface
24 |     public void logout() {
25 |         mFragment.showNativeMessage("执行【登出】操作");
26 |     }
27 | 
28 |     @JavascriptInterface
29 |     public String getLoginUser() {
30 |         return new JSONObject(new HashMap(4) {{
31 |             put("user_id", 666);
32 |             put("username", "你就说6不6");
33 |             put("sex", "未知");
34 |             put("isStudent", false);
35 |         }}).toString();
36 |     }
37 | }
38 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/model/jsinterface/bean/User.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.model.jsinterface.bean;
 2 | 
 3 | public class User {
 4 |     private String account;
 5 |     private String password;
 6 | 
 7 |     public String getAccount() {
 8 |         return account;
 9 |     }
10 | 
11 |     public void setAccount(String account) {
12 |         this.account = account;
13 |     }
14 | 
15 |     public String getPassword() {
16 |         return password;
17 |     }
18 | 
19 |     public void setPassword(String password) {
20 |         this.password = password;
21 |     }
22 | }


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/base/BaseActivity.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.base;
 2 | 
 3 | import android.support.annotation.NonNull;
 4 | import android.support.v7.app.AppCompatActivity;
 5 | 
 6 | /**
 7 |  * @author gangan
 8 |  */
 9 | public class BaseActivity extends AppCompatActivity {
10 |     public void addFragment(@NonNull android.app.Fragment fragment, int frameId) {
11 |         android.app.FragmentManager fragmentManager = getFragmentManager();
12 |         android.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
13 |         fragmentTransaction.add(frameId, fragment);
14 |         fragmentTransaction.commit();
15 |     }
16 | }
17 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/base/BaseFragment.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.base;
 2 | 
 3 | 
 4 | import android.app.Activity;
 5 | import android.app.Fragment;
 6 | import android.content.Intent;
 7 | import android.support.annotation.NonNull;
 8 | import android.support.v7.app.AlertDialog;
 9 | import android.webkit.WebView;
10 | 
11 | import butterknife.Unbinder;
12 | 
13 | /**
14 |  * @author gangan
15 |  */
16 | public class BaseFragment extends Fragment {
17 |     static {
18 |         WebView.setWebContentsDebuggingEnabled(true);
19 |     }
20 | 
21 |     protected T mPresenter;
22 |     protected Unbinder mUnbinder;
23 | 
24 |     @Override
25 |     public void onStart() {
26 |         super.onStart();
27 | 
28 |         if (mPresenter != null) {
29 |             mPresenter.start();
30 |         }
31 |     }
32 | 
33 |     @Override
34 |     public void onDestroy() {
35 |         if (mPresenter != null) {
36 |             mPresenter.destroy();
37 |             mPresenter = null;
38 |         }
39 |         super.onDestroy();
40 |     }
41 | 
42 |     public void setPresenter(@NonNull T presenter) {
43 |         mPresenter = presenter;
44 |     }
45 | 
46 |     public void startActivity(Class activity) {
47 |         Intent intent = new Intent(getActivity(), activity);
48 |         startActivity(intent);
49 |     }
50 | 
51 |     public void showNativeMessage(String message){
52 |         AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
53 |         builder.setTitle("原生弹窗");
54 |         builder.setMessage(message);
55 |         builder.create().show();
56 |     }
57 | }
58 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/base/BasePresenter.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.base;
 2 | 
 3 | /**
 4 |  * @author gangan
 5 |  */
 6 | public abstract class BasePresenter implements BasePresenterInterface {
 7 |     private boolean mIsFirstStart = true;
 8 | 
 9 |     @Override
10 |     public final void start() {
11 |         start(mIsFirstStart);
12 |         mIsFirstStart = false;
13 |     }
14 | 
15 |     protected abstract void start(boolean isFirstStart);
16 | 
17 |     @Override
18 |     public void destroy() {
19 | 
20 |     }
21 | }
22 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/base/BasePresenterInterface.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.base;
 2 | 
 3 | /**
 4 |  * @author gangan
 5 |  */
 6 | public interface BasePresenterInterface {
 7 |     void start();
 8 | 
 9 |     void destroy();
10 | }
11 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/base/BaseView.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.base;
 2 | 
 3 | import android.app.Activity;
 4 | import android.support.annotation.NonNull;
 5 | 
 6 | public interface BaseView {
 7 | 
 8 |     void setPresenter(@NonNull T presenter);
 9 | 
10 |     void startActivity(Class activity);
11 | }
12 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/home/HomeActivity.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.home;
 2 | 
 3 | import android.os.Bundle;
 4 | import android.support.annotation.Nullable;
 5 | 
 6 | import com.ganwenpeng.jsinterface.R;
 7 | import com.ganwenpeng.jsinterface.page.base.BaseActivity;
 8 | 
 9 | public class HomeActivity extends BaseActivity {
10 |     @Override
11 |     protected void onCreate(@Nullable Bundle savedInstanceState) {
12 |         super.onCreate(savedInstanceState);
13 |         setContentView(R.layout.activity_blank);
14 | 
15 |         HomeFragment fragment = (HomeFragment) getFragmentManager().findFragmentById(R.id.content_frame);
16 |         if (fragment == null) {
17 |             fragment = new HomeFragment();
18 |             HomePresenter presenter = new HomePresenter(fragment);
19 |             fragment.setPresenter(presenter);
20 |             addFragment(fragment, R.id.content_frame);
21 |         }
22 |     }
23 | 
24 | }
25 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/home/HomeContract.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.home;
 2 | 
 3 | import com.ganwenpeng.jsinterface.model.home.bean.HomeItemBean;
 4 | import com.ganwenpeng.jsinterface.page.base.BasePresenterInterface;
 5 | import com.ganwenpeng.jsinterface.page.base.BaseView;
 6 | 
 7 | import java.util.ArrayList;
 8 | 
 9 | interface HomeContract {
10 |     interface View extends BaseView {
11 |         /**
12 |          * 渲染界面
13 |          * @param list 首页列表的每一行对应的bean
14 |          */
15 |         void render(final ArrayList list);
16 |     }
17 | 
18 |     interface Presenter extends BasePresenterInterface {
19 |         /**
20 |          * 点击了某一行,通知presenter
21 |          * @param position 点击了哪里行?从0开始
22 |          */
23 |         void clickItem(final int position);
24 |     }
25 | }
26 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/home/HomeFragment.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.home;
 2 | 
 3 | import android.os.Bundle;
 4 | import android.support.annotation.Nullable;
 5 | import android.support.v7.widget.LinearLayoutManager;
 6 | import android.support.v7.widget.RecyclerView;
 7 | import android.view.LayoutInflater;
 8 | import android.view.View;
 9 | import android.view.ViewGroup;
10 | import android.widget.ImageView;
11 | import android.widget.TextView;
12 | 
13 | import com.chad.library.adapter.base.BaseQuickAdapter;
14 | import com.chad.library.adapter.base.BaseViewHolder;
15 | import com.ganwenpeng.jsinterface.R;
16 | import com.ganwenpeng.jsinterface.model.home.bean.HomeItemBean;
17 | import com.ganwenpeng.jsinterface.page.base.BaseFragment;
18 | 
19 | import java.util.ArrayList;
20 | 
21 | import butterknife.BindView;
22 | import butterknife.ButterKnife;
23 | import butterknife.OnClick;
24 | 
25 | /**
26 |  * @author gangan
27 |  */
28 | public class HomeFragment extends BaseFragment implements HomeContract.View {
29 |     @BindView(R.id.tv_toolbar_title)
30 |     TextView mTvToolbarTitle;
31 |     @BindView(R.id.img_toolbar_back)
32 |     ImageView mImgToolbarBack;
33 |     @BindView(R.id.recycle_content)
34 |     RecyclerView mRecycleContent;
35 | 
36 |     private HomeTableAdapter mHomeTableAdapter;
37 | 
38 |     @Nullable
39 |     @Override
40 |     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
41 |         View view = inflater.inflate(R.layout.fragment_home, container, false);
42 |         mUnbinder = ButterKnife.bind(this, view);
43 | 
44 |         setupUI();
45 |         return view;
46 |     }
47 | 
48 |     private void setupUI() {
49 |         mTvToolbarTitle.setText("Android & H5 交互");
50 | 
51 |         mRecycleContent.setLayoutManager(new LinearLayoutManager(getActivity()));
52 |         mHomeTableAdapter = new HomeTableAdapter();
53 |         mRecycleContent.setAdapter(mHomeTableAdapter);
54 |         mHomeTableAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
55 |             @Override
56 |             public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
57 |                 mPresenter.clickItem(position);
58 |             }
59 |         });
60 |     }
61 | 
62 |     @Override
63 |     public void render(ArrayList list) {
64 |         mHomeTableAdapter.setNewData(list);
65 |         mHomeTableAdapter.notifyDataSetChanged();
66 |     }
67 | 
68 |     private class HomeTableAdapter extends BaseQuickAdapter {
69 |         HomeTableAdapter() {
70 |             super(R.layout.layout_home_item);
71 |         }
72 | 
73 |         @Override
74 |         protected void convert(BaseViewHolder helper, HomeItemBean item) {
75 |             helper.setText(R.id.tv_jscallnative_value, item.getJsCallNative())
76 |                     .setText(R.id.tv_nativecalljs_value, item.getNativeCallJs());
77 |         }
78 |     }
79 | 
80 | }
81 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/home/HomePresenter.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.home;
 2 | 
 3 | import com.ganwenpeng.jsinterface.model.home.bean.HomeItemBean;
 4 | import com.ganwenpeng.jsinterface.page.base.BasePresenter;
 5 | import com.ganwenpeng.jsinterface.page.intercept.InterceptActivity;
 6 | import com.ganwenpeng.jsinterface.page.jsbridge.JsBridgeActivity;
 7 | import com.ganwenpeng.jsinterface.page.jsinterface.JsInterfaceActivity;
 8 | 
 9 | import java.util.ArrayList;
10 | 
11 | class HomePresenter extends BasePresenter implements HomeContract.Presenter {
12 |     private final HomeContract.View mView;
13 |     private final ArrayList mItemList;
14 | 
15 |     HomePresenter(HomeContract.View view) {
16 |         mView = view;
17 | 
18 |         mItemList = new ArrayList(){{
19 |             add(new HomeItemBean("拦截跳转", "webView.loadUrl", InterceptActivity.class));
20 |             add(new HomeItemBean("JavaScriptInterface", "webView.loadUrl", JsInterfaceActivity.class));
21 |             add(new HomeItemBean("WebViewJavascriptBridge", "callHandler", JsBridgeActivity.class));
22 |         }};
23 |     }
24 | 
25 |     @Override
26 |     protected void start(boolean isFirstStart) {
27 |         if (isFirstStart) {
28 |             mView.render(mItemList);
29 |         }
30 |     }
31 | 
32 |     @Override
33 |     public void clickItem(int position) {
34 |         if (position >= mItemList.size()) {
35 |             return;
36 |         }
37 | 
38 |         mView.startActivity(mItemList.get(position).getTargetClass());
39 |     }
40 | }
41 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/intercept/InterceptActivity.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.intercept;
 2 | 
 3 | import android.os.Bundle;
 4 | import android.support.annotation.Nullable;
 5 | 
 6 | import com.ganwenpeng.jsinterface.R;
 7 | import com.ganwenpeng.jsinterface.page.base.BaseActivity;
 8 | 
 9 | /**
10 |  * @author gangan
11 |  */
12 | public class InterceptActivity extends BaseActivity {
13 |     @Override
14 |     protected void onCreate(@Nullable Bundle savedInstanceState) {
15 |         super.onCreate(savedInstanceState);
16 |         setContentView(R.layout.activity_blank);
17 | 
18 |         InterceptFragment fragment = (InterceptFragment) getFragmentManager().findFragmentById(R.id.content_frame);
19 |         if (fragment == null) {
20 |             fragment = new InterceptFragment();
21 |             InterceptPresenter presenter = new InterceptPresenter(fragment);
22 |             fragment.setPresenter(presenter);
23 |             addFragment(fragment, R.id.content_frame);
24 |         }
25 |     }
26 | }
27 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/intercept/InterceptContract.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.intercept;
 2 | 
 3 | import android.support.annotation.NonNull;
 4 | 
 5 | import com.ganwenpeng.jsinterface.page.base.BasePresenterInterface;
 6 | import com.ganwenpeng.jsinterface.page.base.BaseView;
 7 | 
 8 | interface InterceptContract {
 9 |     interface View extends BaseView {
10 |         /**
11 |          * 渲染页面
12 |          *
13 |          * @param url HTML链接
14 |          */
15 |         void renderUrl(@NonNull final String url);
16 | 
17 |         /**
18 |          * 执行JS
19 |          * @param js js代码
20 |          */
21 |         void execJavaScript(@NonNull final String js);
22 |     }
23 | 
24 |     interface Presenter extends BasePresenterInterface {
25 |         /**
26 |          * 点击按钮1
27 |          */
28 |         void clickBtn1();
29 | 
30 |         /**
31 |          * 点击按钮2
32 |          */
33 |         void clickBtn2();
34 |     }
35 | }
36 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/intercept/InterceptFragment.java:
--------------------------------------------------------------------------------
  1 | package com.ganwenpeng.jsinterface.page.intercept;
  2 | 
  3 | import android.net.Uri;
  4 | import android.os.Build;
  5 | import android.os.Bundle;
  6 | import android.support.annotation.NonNull;
  7 | import android.support.annotation.Nullable;
  8 | import android.view.LayoutInflater;
  9 | import android.view.View;
 10 | import android.view.ViewGroup;
 11 | import android.webkit.ValueCallback;
 12 | import android.webkit.WebResourceRequest;
 13 | import android.webkit.WebView;
 14 | import android.webkit.WebViewClient;
 15 | import android.widget.ImageView;
 16 | import android.widget.TextView;
 17 | 
 18 | import com.ganwenpeng.jsinterface.R;
 19 | import com.ganwenpeng.jsinterface.page.base.BaseFragment;
 20 | 
 21 | import butterknife.BindView;
 22 | import butterknife.ButterKnife;
 23 | import butterknife.OnClick;
 24 | 
 25 | /**
 26 |  * @author gangan
 27 |  */
 28 | public class InterceptFragment extends BaseFragment implements InterceptContract.View {
 29 | 
 30 |     @BindView(R.id.tv_toolbar_title)
 31 |     TextView mTvToolbarTitle;
 32 |     @BindView(R.id.img_toolbar_back)
 33 |     ImageView mImgToolbarBack;
 34 |     @BindView(R.id.web)
 35 |     WebView mWebView;
 36 | 
 37 |     @Nullable
 38 |     @Override
 39 |     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
 40 |         View view = inflater.inflate(R.layout.fragment_intercept_webview, container, false);
 41 |         mUnbinder = ButterKnife.bind(this, view);
 42 | 
 43 |         setupUI();
 44 |         return view;
 45 |     }
 46 | 
 47 |     private void setupUI() {
 48 |         mTvToolbarTitle.setText("拦截跳转");
 49 |         mImgToolbarBack.setVisibility(View.VISIBLE);
 50 | 
 51 |         mWebView.getSettings().setJavaScriptEnabled(true);
 52 |         WebViewClient webViewClient = new WebViewClient() {
 53 |             // 老方法
 54 |             @Override
 55 |             public boolean shouldOverrideUrlLoading(WebView view, String url) {
 56 |                 Uri uri = Uri.parse(url);
 57 | 
 58 |                 if (uri.getScheme().contentEquals("app")) {
 59 | 
 60 |                     callNative(uri);
 61 | 
 62 |                     return true;
 63 |                 }
 64 | 
 65 | 
 66 |                 return super.shouldOverrideUrlLoading(view, url);
 67 |             }
 68 | 
 69 |             // 新方法,API21之后支持
 70 |             @Override
 71 |             public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
 72 |                 if (Build.VERSION.SDK_INT < 21) {
 73 |                     return super.shouldOverrideUrlLoading(view, request);
 74 |                 }
 75 | 
 76 |                 Uri uri = request.getUrl();
 77 |                 if (uri.getScheme().contentEquals("app")) {
 78 | 
 79 |                     callNative(uri);
 80 | 
 81 |                     return true;
 82 |                 }
 83 | 
 84 |                 return super.shouldOverrideUrlLoading(view, request);
 85 |             }
 86 |         };
 87 | 
 88 |         mWebView.setWebViewClient(webViewClient);
 89 |     }
 90 | 
 91 |     /**
 92 |      * js 调用原生方法时的特殊跳转链接
 93 |      * @param uri 特殊的跳转链接
 94 |      */
 95 |     private void callNative(Uri uri){
 96 |         String host = uri.getHost();
 97 | 
 98 |         if (host.contentEquals("login")) {
 99 | 
100 |             String account = uri.getQueryParameter("account");
101 |             String password = uri.getQueryParameter("password");
102 | 
103 |             showNativeMessage(String.format("执行登录操作,账号为:%s,密码为:%s", account, password));
104 | 
105 |         } else if (host.contentEquals("share")) {
106 |             String title = uri.getQueryParameter("title");
107 |             String desc = uri.getQueryParameter("desc");
108 |             showNativeMessage(String.format("执行分享操作,title为:【%s】,desc为:【%s】", title, desc));
109 |         }
110 |     }
111 | 
112 |     @OnClick(R.id.img_toolbar_back)
113 |     public void onBackClick() {
114 |         getActivity().onBackPressed();
115 |     }
116 | 
117 |     @OnClick(R.id.tv_btn1)
118 |     public void onBtn1Click(){
119 |         mPresenter.clickBtn1();
120 |     }
121 | 
122 |     @OnClick(R.id.tv_btn2)
123 |     public void onBtn2Click(){
124 |         mPresenter.clickBtn2();
125 |     }
126 | 
127 |     @Override
128 |     public void renderUrl(@NonNull String url) {
129 |         mWebView.loadUrl(url);
130 |     }
131 | 
132 |     @Override
133 |     public void execJavaScript(@NonNull String js) {
134 |         mWebView.evaluateJavascript(js, new ValueCallback() {
135 |             @Override
136 |             public void onReceiveValue(String s) {
137 |                 showNativeMessage("调用JS方法后得到的返回值是:" + s);
138 |             }
139 |         });
140 |     }
141 | }
142 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/intercept/InterceptPresenter.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.intercept;
 2 | 
 3 | import com.ganwenpeng.jsinterface.page.base.BasePresenter;
 4 | 
 5 | class InterceptPresenter extends BasePresenter implements InterceptContract.Presenter {
 6 |     private final InterceptContract.View mView;
 7 | 
 8 |     InterceptPresenter(InterceptContract.View mView) {
 9 |         this.mView = mView;
10 |     }
11 | 
12 |     @Override
13 |     protected void start(boolean isFirstStart) {
14 |         if (isFirstStart) {
15 |             mView.renderUrl("file:///android_asset/intercept.html");
16 |         }
17 |     }
18 | 
19 |     @Override
20 |     public void clickBtn1() {
21 |         mView.execJavaScript("showResponse('点击了按钮111111111111')");
22 |     }
23 | 
24 |     @Override
25 |     public void clickBtn2() {
26 |         mView.execJavaScript("showResponse('点击了按钮22222222222')");
27 |     }
28 | }
29 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/jsbridge/JsBridgeActivity.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.jsbridge;
 2 | 
 3 | import android.os.Bundle;
 4 | import android.support.annotation.Nullable;
 5 | 
 6 | import com.ganwenpeng.jsinterface.R;
 7 | import com.ganwenpeng.jsinterface.page.base.BaseActivity;
 8 | 
 9 | /**
10 |  * @author gangan
11 |  */
12 | public class JsBridgeActivity extends BaseActivity {
13 |     @Override
14 |     protected void onCreate(@Nullable Bundle savedInstanceState) {
15 |         super.onCreate(savedInstanceState);
16 |         setContentView(R.layout.activity_blank);
17 | 
18 |         JsBridgeFragment fragment = (JsBridgeFragment) getFragmentManager().findFragmentById(R.id.content_frame);
19 |         if (fragment == null) {
20 |             fragment = new JsBridgeFragment();
21 |             JsBridgePresenter presenter = new JsBridgePresenter(fragment);
22 |             fragment.setPresenter(presenter);
23 |             addFragment(fragment, R.id.content_frame);
24 |         }
25 |     }
26 | }
27 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/jsbridge/JsBridgeContract.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.jsbridge;
 2 | 
 3 | import android.support.annotation.NonNull;
 4 | 
 5 | import com.ganwenpeng.jsinterface.page.base.BasePresenterInterface;
 6 | import com.ganwenpeng.jsinterface.page.base.BaseView;
 7 | 
 8 | interface JsBridgeContract {
 9 |     interface View extends BaseView {
10 |         /**
11 |          * 渲染页面
12 |          *
13 |          * @param url HTML链接
14 |          */
15 |         void renderUrl(@NonNull final String url);
16 | 
17 |         /**
18 |          * 以桥接的方式发送消息到嵌入页
19 |          * @param message 被发送的消息
20 |          */
21 |         void bridgeShowMessage(@NonNull final String message);
22 | 
23 |         /**
24 |          * 以桥接的方式从嵌入页获取消息
25 |          * @param message 获取消息时传给嵌入页的参数
26 |          */
27 |         void bridgeGetMessage(@NonNull final String message);
28 |     }
29 | 
30 |     interface Presenter extends BasePresenterInterface {
31 |         /**
32 |          * 点击按钮1
33 |          */
34 |         void clickBtn1();
35 | 
36 |         /**
37 |          * 点击按钮2
38 |          */
39 |         void clickBtn2();
40 |     }
41 | }
42 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/jsbridge/JsBridgeFragment.java:
--------------------------------------------------------------------------------
  1 | package com.ganwenpeng.jsinterface.page.jsbridge;
  2 | 
  3 | import android.os.Bundle;
  4 | import android.support.annotation.NonNull;
  5 | import android.support.annotation.Nullable;
  6 | import android.view.LayoutInflater;
  7 | import android.view.View;
  8 | import android.view.ViewGroup;
  9 | import android.widget.ImageView;
 10 | import android.widget.TextView;
 11 | 
 12 | import com.ganwenpeng.jsinterface.R;
 13 | import com.ganwenpeng.jsinterface.model.jsinterface.bean.User;
 14 | import com.ganwenpeng.jsinterface.page.base.BaseFragment;
 15 | import com.github.lzyzsd.jsbridge.BridgeHandler;
 16 | import com.github.lzyzsd.jsbridge.BridgeWebView;
 17 | import com.github.lzyzsd.jsbridge.CallBackFunction;
 18 | import com.google.gson.Gson;
 19 | 
 20 | import java.util.HashMap;
 21 | 
 22 | import butterknife.BindView;
 23 | import butterknife.ButterKnife;
 24 | import butterknife.OnClick;
 25 | 
 26 | /**
 27 |  * @author gangan
 28 |  */
 29 | public class JsBridgeFragment extends BaseFragment implements JsBridgeContract.View {
 30 |     @BindView(R.id.tv_toolbar_title)
 31 |     TextView mTvToolbarTitle;
 32 |     @BindView(R.id.web)
 33 |     BridgeWebView mWeb;
 34 |     @BindView(R.id.img_toolbar_back)
 35 |     ImageView mImgToolbarBack;
 36 | 
 37 | 
 38 |     @Nullable
 39 |     @Override
 40 |     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
 41 |         View view = inflater.inflate(R.layout.fragment_bridge_webview, container, false);
 42 |         mUnbinder = ButterKnife.bind(this, view);
 43 | 
 44 |         setupUI();
 45 |         return view;
 46 |     }
 47 | 
 48 |     private void setupUI() {
 49 |         mTvToolbarTitle.setText("JSBridge");
 50 |         mImgToolbarBack.setVisibility(View.VISIBLE);
 51 | 
 52 |         mWeb.registerHandler("getOS", new BridgeHandler() {
 53 |             @Override
 54 |             public void handler(String data, CallBackFunction function) {
 55 |                 HashMap response = new HashMap(){{
 56 |                    put("error", 0);
 57 |                    put("message", "");
 58 |                    put("data", new HashMap(){{
 59 |                        put("os", "android");
 60 |                    }});
 61 |                 }};
 62 |                 function.onCallBack(response.toString());
 63 |             }
 64 |         });
 65 | 
 66 |         mWeb.registerHandler("login", new BridgeHandler() {
 67 |             @Override
 68 |             public void handler(String data, CallBackFunction function) {
 69 | 
 70 | 
 71 |                 Gson gson = new Gson();
 72 |                 final User user = gson.fromJson(data, User.class);
 73 |                 HashMap response = new HashMap(){{
 74 |                     put("error", 0);
 75 |                     put("message", "");
 76 |                     put("data", String.format("执行登录操作,账号为:%s、密码为:%s", user.getAccount(), user.getPassword()));
 77 |                 }};
 78 | 
 79 |                 function.onCallBack(response.toString());
 80 |             }
 81 |         });
 82 |     }
 83 | 
 84 |     @OnClick(R.id.img_toolbar_back)
 85 |     public void onBackClick() {
 86 |         getActivity().onBackPressed();
 87 |     }
 88 | 
 89 |     @OnClick(R.id.tv_btn1)
 90 |     public void onBtn1Click(){
 91 |         mPresenter.clickBtn1();
 92 |     }
 93 | 
 94 |     @OnClick(R.id.tv_btn2)
 95 |     public void onBtn2Click(){
 96 |         mPresenter.clickBtn2();
 97 |     }
 98 | 
 99 |     @Override
100 |     public void renderUrl(@NonNull String url) {
101 |         mWeb.loadUrl(url);
102 |     }
103 | 
104 |     @Override
105 |     public void bridgeShowMessage(@NonNull String message) {
106 |         mWeb.callHandler("jsbridge_showMessage", message, null);
107 |     }
108 | 
109 |     @Override
110 |     public void bridgeGetMessage(@NonNull String message) {
111 |         mWeb.callHandler("jsbridge_getJsMessage", message, new CallBackFunction() {
112 |             @Override
113 |             public void onCallBack(String data) {
114 |                 showNativeMessage(String.format("原生调用JsBridge方法后,Js方法的返回值为:【%s】", data));
115 |             }
116 |         });
117 |     }
118 | }
119 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/jsbridge/JsBridgePresenter.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.jsbridge;
 2 | 
 3 | import com.ganwenpeng.jsinterface.page.base.BasePresenter;
 4 | 
 5 | /**
 6 |  * @author gangan
 7 |  */
 8 | public class JsBridgePresenter extends BasePresenter implements JsBridgeContract.Presenter {
 9 |     private final JsBridgeContract.View mView;
10 | 
11 |     JsBridgePresenter(JsBridgeContract.View mView) {
12 |         this.mView = mView;
13 |     }
14 | 
15 |     @Override
16 |     protected void start(boolean isFirstStart) {
17 |         if (isFirstStart) {
18 |             mView.renderUrl("file:///android_asset/jsbridge.html");
19 |         }
20 |     }
21 | 
22 |     @Override
23 |     public void clickBtn1() {
24 |         mView.bridgeShowMessage("点击了按钮111111111111");
25 |     }
26 | 
27 |     @Override
28 |     public void clickBtn2() {
29 |         mView.bridgeGetMessage("点击了按钮2222222222222");
30 |     }
31 | }
32 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/jsinterface/JsInterfaceActivity.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.jsinterface;
 2 | 
 3 | import android.os.Bundle;
 4 | import android.support.annotation.Nullable;
 5 | 
 6 | import com.ganwenpeng.jsinterface.R;
 7 | import com.ganwenpeng.jsinterface.page.base.BaseActivity;
 8 | 
 9 | /**
10 |  * @author gangan
11 |  */
12 | public class JsInterfaceActivity extends BaseActivity {
13 |     @Override
14 |     protected void onCreate(@Nullable Bundle savedInstanceState) {
15 |         super.onCreate(savedInstanceState);
16 |         setContentView(R.layout.activity_blank);
17 | 
18 |         JsInterfaceFragment fragment = (JsInterfaceFragment) getFragmentManager().findFragmentById(R.id.content_frame);
19 |         if (fragment == null) {
20 |             fragment = new JsInterfaceFragment();
21 |             JsInterfacePresenter presenter = new JsInterfacePresenter(fragment);
22 |             fragment.setPresenter(presenter);
23 |             addFragment(fragment, R.id.content_frame);
24 |         }
25 |     }
26 | }
27 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/jsinterface/JsInterfaceContract.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.jsinterface;
 2 | 
 3 | import android.support.annotation.NonNull;
 4 | 
 5 | import com.ganwenpeng.jsinterface.page.base.BasePresenterInterface;
 6 | import com.ganwenpeng.jsinterface.page.base.BaseView;
 7 | 
 8 | interface JsInterfaceContract {
 9 |     interface View extends BaseView {
10 |         /**
11 |          * 渲染页面
12 |          *
13 |          * @param url HTML链接
14 |          */
15 |         void renderUrl(@NonNull final String url);
16 | 
17 |         /**
18 |          * 执行JS
19 |          * @param js js代码
20 |          */
21 |         void execJavaScript(@NonNull final String js);
22 |     }
23 | 
24 |     interface Presenter extends BasePresenterInterface {
25 |         /**
26 |          * 点击按钮1
27 |          */
28 |         void clickBtn1();
29 | 
30 |         /**
31 |          * 点击按钮2
32 |          */
33 |         void clickBtn2();
34 |     }
35 | }
36 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/jsinterface/JsInterfaceFragment.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.jsinterface;
 2 | 
 3 | import android.net.Uri;
 4 | import android.os.Build;
 5 | import android.os.Bundle;
 6 | import android.support.annotation.NonNull;
 7 | import android.support.annotation.Nullable;
 8 | import android.support.v7.app.AlertDialog;
 9 | import android.view.LayoutInflater;
10 | import android.view.View;
11 | import android.view.ViewGroup;
12 | import android.webkit.ValueCallback;
13 | import android.webkit.WebResourceRequest;
14 | import android.webkit.WebView;
15 | import android.webkit.WebViewClient;
16 | import android.widget.ImageView;
17 | import android.widget.TextView;
18 | 
19 | import com.ganwenpeng.jsinterface.R;
20 | import com.ganwenpeng.jsinterface.model.jsinterface.JsInterfaceLogic;
21 | import com.ganwenpeng.jsinterface.page.base.BaseFragment;
22 | 
23 | import butterknife.BindView;
24 | import butterknife.ButterKnife;
25 | import butterknife.OnClick;
26 | 
27 | /**
28 |  * @author gangan
29 |  */
30 | public class JsInterfaceFragment extends BaseFragment implements JsInterfaceContract.View {
31 |     @BindView(R.id.tv_toolbar_title)
32 |     TextView mTvToolbarTitle;
33 |     @BindView(R.id.img_toolbar_back)
34 |     ImageView mImgToolbarBack;
35 |     @BindView(R.id.web)
36 |     WebView mWebView;
37 | 
38 |     @Nullable
39 |     @Override
40 |     public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, Bundle savedInstanceState) {
41 |         View view = inflater.inflate(R.layout.fragment_intercept_webview, container, false);
42 |         mUnbinder = ButterKnife.bind(this, view);
43 | 
44 |         setupUI();
45 |         return view;
46 |     }
47 | 
48 |     @SuppressWarnings("all")
49 |     private void setupUI() {
50 |         mTvToolbarTitle.setText("拦截跳转");
51 |         mImgToolbarBack.setVisibility(View.VISIBLE);
52 |         mWebView.getSettings().setJavaScriptEnabled(true);
53 | 
54 |         mWebView.addJavascriptInterface(new JsInterfaceLogic(this), "app");
55 |     }
56 | 
57 |     @OnClick(R.id.img_toolbar_back)
58 |     public void onBackClick() {
59 |         getActivity().onBackPressed();
60 |     }
61 | 
62 |     @OnClick(R.id.tv_btn1)
63 |     public void onBtn1Click(){
64 |         mPresenter.clickBtn1();
65 |     }
66 | 
67 |     @OnClick(R.id.tv_btn2)
68 |     public void onBtn2Click(){
69 |         mPresenter.clickBtn2();
70 |     }
71 | 
72 |     @Override
73 |     public void renderUrl(@NonNull String url) {
74 |         mWebView.loadUrl(url);
75 |     }
76 | 
77 |     @Override
78 |     public void execJavaScript(@NonNull String js) {
79 |         mWebView.evaluateJavascript(js, new ValueCallback() {
80 |             @Override
81 |             public void onReceiveValue(String s) {
82 |                 showNativeMessage("调用JS方法后得到的返回值是:" + s);
83 |             }
84 |         });
85 |     }
86 | }
87 | 


--------------------------------------------------------------------------------
/Android/app/src/main/java/com/ganwenpeng/jsinterface/page/jsinterface/JsInterfacePresenter.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface.page.jsinterface;
 2 | 
 3 | import com.ganwenpeng.jsinterface.page.base.BasePresenter;
 4 | 
 5 | public class JsInterfacePresenter extends BasePresenter implements JsInterfaceContract.Presenter {
 6 |     private final JsInterfaceContract.View mView;
 7 | 
 8 |     JsInterfacePresenter(JsInterfaceContract.View mView) {
 9 |         this.mView = mView;
10 |     }
11 | 
12 |     @Override
13 |     protected void start(boolean isFirstStart) {
14 |         if (isFirstStart) {
15 |             mView.renderUrl("file:///android_asset/jsinterface.html");
16 |         }
17 |     }
18 | 
19 |     @Override
20 |     public void clickBtn1() {
21 |         mView.execJavaScript("showResponse('点击了按钮111111111111')");
22 |     }
23 | 
24 |     @Override
25 |     public void clickBtn2() {
26 |         mView.execJavaScript("showResponse('点击了按钮22222222222')");
27 |     }
28 | }
29 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
 1 | 
 7 |     
12 |         
13 |             
19 |                 
22 |                 
25 |             
26 |         
27 |     
28 |     
34 | 
35 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  7 |     
 10 |     
 15 |     
 20 |     
 25 |     
 30 |     
 35 |     
 40 |     
 45 |     
 50 |     
 55 |     
 60 |     
 65 |     
 70 |     
 75 |     
 80 |     
 85 |     
 90 |     
 95 |     
100 |     
105 |     
110 |     
115 |     
120 |     
125 |     
130 |     
135 |     
140 |     
145 |     
150 |     
155 |     
160 |     
165 |     
170 | 
171 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/common/drawable-xhdpi/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/layout/common/drawable-xhdpi/back.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/common/layout/activity_blank.xml:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 6 | 
 7 |     
 8 |     
12 | 
13 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/common/layout/layout_toolbar.xml:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 8 | 
 9 |     
16 | 
17 |     
26 | 
27 | 
28 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/home/layout/fragment_home.xml:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 7 | 
 8 |     
 9 | 
10 |     
15 | 
16 | 
17 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/home/layout/layout_home_item.xml:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 8 | 
 9 |     
13 | 
14 |     
17 | 
18 |         
24 | 
25 |         
33 |     
34 | 
35 |     
38 | 
39 |         
45 | 
46 |         
54 |     
55 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/intercept/layout/fragment_intercept_webview.xml:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 9 | 
10 |     
11 | 
12 |     
19 |     
24 |         
33 | 
34 |         
43 | 
44 |     
45 | 
46 |     
50 | 
51 |     
56 | 
57 | 
58 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/layout/jsbridge/layout/fragment_bridge_webview.xml:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 9 | 
10 |     
11 | 
12 |     
19 |     
24 |         
33 | 
34 |         
43 | 
44 |     
45 | 
46 |     
50 | 
51 |     
56 | 
57 | 
58 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |     
4 |     
5 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |     
4 |     
5 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/mipmap-hdpi/ic_launcher.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/mipmap-mdpi/ic_launcher.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/mipmap-xhdpi/ic_launcher.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png


--------------------------------------------------------------------------------
/Android/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 | 
2 | 
3 |     #3F51B5
4 |     #303F9F
5 |     #FF4081
6 |     #FFFFFF
7 | 
8 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 | 
2 |     JsBridge
3 | 
4 | 


--------------------------------------------------------------------------------
/Android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |     
 4 |     
10 | 
11 | 
12 | 


--------------------------------------------------------------------------------
/Android/app/src/test/java/com/ganwenpeng/jsinterface/ExampleUnitTest.java:
--------------------------------------------------------------------------------
 1 | package com.ganwenpeng.jsinterface;
 2 | 
 3 | import org.junit.Test;
 4 | 
 5 | import static org.junit.Assert.*;
 6 | 
 7 | /**
 8 |  * Example local unit test, which will execute on the development machine (host).
 9 |  *
10 |  * @see Testing documentation
11 |  */
12 | public class ExampleUnitTest {
13 |     @Test
14 |     public void addition_isCorrect() throws Exception {
15 |         assertEquals(4, 2 + 2);
16 |     }
17 | }


--------------------------------------------------------------------------------
/Android/build.gradle:
--------------------------------------------------------------------------------
 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
 2 | 
 3 | buildscript {
 4 |     
 5 |     repositories {
 6 |         google()
 7 |         jcenter()
 8 |     }
 9 |     dependencies {
10 |         classpath 'com.android.tools.build:gradle:3.0.1'
11 |         
12 | 
13 |         // NOTE: Do not place your application dependencies here; they belong
14 |         // in the individual module build.gradle files
15 |     }
16 | }
17 | 
18 | allprojects {
19 |     repositories {
20 |         google()
21 |         jcenter()
22 |         maven { url "https://jitpack.io" }
23 |     }
24 | }
25 | 
26 | task clean(type: Delete) {
27 |     delete rootProject.buildDir
28 | }
29 | 


--------------------------------------------------------------------------------
/Android/gradle.properties:
--------------------------------------------------------------------------------
 1 | # Project-wide Gradle settings.
 2 | 
 3 | # IDE (e.g. Android Studio) users:
 4 | # Gradle settings configured through the IDE *will override*
 5 | # any settings specified in this file.
 6 | 
 7 | # For more details on how to configure your build environment visit
 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
 9 | 
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 | 
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 | 


--------------------------------------------------------------------------------
/Android/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/Android/gradle/wrapper/gradle-wrapper.jar


--------------------------------------------------------------------------------
/Android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 25 13:28:31 CST 2017
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 | 


--------------------------------------------------------------------------------
/Android/gradlew:
--------------------------------------------------------------------------------
  1 | #!/usr/bin/env bash
  2 | 
  3 | ##############################################################################
  4 | ##
  5 | ##  Gradle start up script for UN*X
  6 | ##
  7 | ##############################################################################
  8 | 
  9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
 10 | DEFAULT_JVM_OPTS=""
 11 | 
 12 | APP_NAME="Gradle"
 13 | APP_BASE_NAME=`basename "$0"`
 14 | 
 15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
 16 | MAX_FD="maximum"
 17 | 
 18 | warn ( ) {
 19 |     echo "$*"
 20 | }
 21 | 
 22 | die ( ) {
 23 |     echo
 24 |     echo "$*"
 25 |     echo
 26 |     exit 1
 27 | }
 28 | 
 29 | # OS specific support (must be 'true' or 'false').
 30 | cygwin=false
 31 | msys=false
 32 | darwin=false
 33 | case "`uname`" in
 34 |   CYGWIN* )
 35 |     cygwin=true
 36 |     ;;
 37 |   Darwin* )
 38 |     darwin=true
 39 |     ;;
 40 |   MINGW* )
 41 |     msys=true
 42 |     ;;
 43 | esac
 44 | 
 45 | # Attempt to set APP_HOME
 46 | # Resolve links: $0 may be a link
 47 | PRG="$0"
 48 | # Need this for relative symlinks.
 49 | while [ -h "$PRG" ] ; do
 50 |     ls=`ls -ld "$PRG"`
 51 |     link=`expr "$ls" : '.*-> \(.*\)$'`
 52 |     if expr "$link" : '/.*' > /dev/null; then
 53 |         PRG="$link"
 54 |     else
 55 |         PRG=`dirname "$PRG"`"/$link"
 56 |     fi
 57 | done
 58 | SAVED="`pwd`"
 59 | cd "`dirname \"$PRG\"`/" >/dev/null
 60 | APP_HOME="`pwd -P`"
 61 | cd "$SAVED" >/dev/null
 62 | 
 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
 64 | 
 65 | # Determine the Java command to use to start the JVM.
 66 | if [ -n "$JAVA_HOME" ] ; then
 67 |     if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
 68 |         # IBM's JDK on AIX uses strange locations for the executables
 69 |         JAVACMD="$JAVA_HOME/jre/sh/java"
 70 |     else
 71 |         JAVACMD="$JAVA_HOME/bin/java"
 72 |     fi
 73 |     if [ ! -x "$JAVACMD" ] ; then
 74 |         die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
 75 | 
 76 | Please set the JAVA_HOME variable in your environment to match the
 77 | location of your Java installation."
 78 |     fi
 79 | else
 80 |     JAVACMD="java"
 81 |     which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
 82 | 
 83 | Please set the JAVA_HOME variable in your environment to match the
 84 | location of your Java installation."
 85 | fi
 86 | 
 87 | # Increase the maximum file descriptors if we can.
 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
 89 |     MAX_FD_LIMIT=`ulimit -H -n`
 90 |     if [ $? -eq 0 ] ; then
 91 |         if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
 92 |             MAX_FD="$MAX_FD_LIMIT"
 93 |         fi
 94 |         ulimit -n $MAX_FD
 95 |         if [ $? -ne 0 ] ; then
 96 |             warn "Could not set maximum file descriptor limit: $MAX_FD"
 97 |         fi
 98 |     else
 99 |         warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 |     fi
101 | fi
102 | 
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 |     GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 | 
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 |     APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 |     CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 |     JAVACMD=`cygpath --unix "$JAVACMD"`
113 | 
114 |     # We build the pattern for arguments to be converted via cygpath
115 |     ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 |     SEP=""
117 |     for dir in $ROOTDIRSRAW ; do
118 |         ROOTDIRS="$ROOTDIRS$SEP$dir"
119 |         SEP="|"
120 |     done
121 |     OURCYGPATTERN="(^($ROOTDIRS))"
122 |     # Add a user-defined pattern to the cygpath arguments
123 |     if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 |         OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 |     fi
126 |     # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 |     i=0
128 |     for arg in "$@" ; do
129 |         CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 |         CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
131 | 
132 |         if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
133 |             eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 |         else
135 |             eval `echo args$i`="\"$arg\""
136 |         fi
137 |         i=$((i+1))
138 |     done
139 |     case $i in
140 |         (0) set -- ;;
141 |         (1) set -- "$args0" ;;
142 |         (2) set -- "$args0" "$args1" ;;
143 |         (3) set -- "$args0" "$args1" "$args2" ;;
144 |         (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 |         (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 |         (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 |         (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 |         (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 |         (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 |     esac
151 | fi
152 | 
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 |     JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 | 
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 | 


--------------------------------------------------------------------------------
/Android/gradlew.bat:
--------------------------------------------------------------------------------
 1 | @if "%DEBUG%" == "" @echo off
 2 | @rem ##########################################################################
 3 | @rem
 4 | @rem  Gradle startup script for Windows
 5 | @rem
 6 | @rem ##########################################################################
 7 | 
 8 | @rem Set local scope for the variables with windows NT shell
 9 | if "%OS%"=="Windows_NT" setlocal
10 | 
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 | 
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 | 
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 | 
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 | 
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 | 
32 | goto fail
33 | 
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 | 
38 | if exist "%JAVA_EXE%" goto init
39 | 
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 | 
46 | goto fail
47 | 
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 | 
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 | 
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 | 
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 | 
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 | 
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 | 
69 | :execute
70 | @rem Setup the command line
71 | 
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 | 
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 | 
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 | 
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 | 
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 | 
90 | :omega
91 | 


--------------------------------------------------------------------------------
/Android/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
 1 | MIT License
 2 | 
 3 | Copyright (c) 2017 gwpp
 4 | 
 5 | Permission is hereby granted, free of charge, to any person obtaining a copy
 6 | of this software and associated documentation files (the "Software"), to deal
 7 | in the Software without restriction, including without limitation the rights
 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 | 
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 | 
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 | 


--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
 1 | # jsinterface
 2 | 
 3 | #### 说明
 4 | 本项目是为我们系列博客做的Demo,Demo中包括了Android <=> Js 、iOS <=> Js 交互的所有常见方式,包括拦截跳转、JSContext、@JavaScriptInterface、JSBridge等等。有兴趣的朋友可以下载下来看看
 5 | 
 6 | #### 博客地址:
 7 | - [《App与Js交互(一)iOS》](https://www.jianshu.com/p/13b65557c27f)
 8 | - [《App与Js交互(二)Android》](https://www.jianshu.com/p/5a511e03a7fa)
 9 | - [《App与Js交互(三)Android、iOS通用解决方案推荐》](https://www.jianshu.com/p/6224f429ce87)
10 | 
11 | #### Demo 预览
12 | ![iOS Demo](ios-home.png) ![Android Demo](android-home.png)


--------------------------------------------------------------------------------
/android-home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/android-home.png


--------------------------------------------------------------------------------
/iOS/JsInterface.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
  1 | // !$*UTF8*$!
  2 | {
  3 | 	archiveVersion = 1;
  4 | 	classes = {
  5 | 	};
  6 | 	objectVersion = 48;
  7 | 	objects = {
  8 | 
  9 | /* Begin PBXBuildFile section */
 10 | 		00A1806C621D3EB9E35C5C7A /* libPods-JsInterface.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7F8F24BA95BC4A8D4709671E /* libPods-JsInterface.a */; };
 11 | 		2B2A0043200875E500AC8FA8 /* JSHomeTableViewCell.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B2A0042200875E500AC8FA8 /* JSHomeTableViewCell.m */; };
 12 | 		2B2A0045200875F200AC8FA8 /* JSHomeTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 2B2A0044200875F200AC8FA8 /* JSHomeTableViewCell.xib */; };
 13 | 		2B39E35E1FF40165009238A1 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 2B39E35D1FF40165009238A1 /* Launch Screen.storyboard */; };
 14 | 		2B39E3621FF40251009238A1 /* JSHomeModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B39E3611FF40251009238A1 /* JSHomeModel.m */; };
 15 | 		2B4D6180202A197600D7183A /* JSWKWebKitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B4D617F202A197600D7183A /* JSWKWebKitViewController.m */; };
 16 | 		2B65FC5F1FFBDAA0000322A1 /* intercept.html in Resources */ = {isa = PBXBuildFile; fileRef = 2B65FC5E1FFBDAA0000322A1 /* intercept.html */; };
 17 | 		2B65FC631FFBDAE9000322A1 /* jscontext.html in Resources */ = {isa = PBXBuildFile; fileRef = 2B65FC621FFBDAE9000322A1 /* jscontext.html */; };
 18 | 		2B65FC681FFBE624000322A1 /* JSUIInterceptViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B65FC671FFBE624000322A1 /* JSUIInterceptViewController.m */; };
 19 | 		2B65FC6B1FFBE661000322A1 /* JSUIJsContextViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B65FC6A1FFBE661000322A1 /* JSUIJsContextViewController.m */; };
 20 | 		2B65FC6E1FFBE72D000322A1 /* JSUIBridgeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B65FC6D1FFBE72D000322A1 /* JSUIBridgeViewController.m */; };
 21 | 		2B65FC741FFBE840000322A1 /* NSURL+Params.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B65FC731FFBE840000322A1 /* NSURL+Params.m */; };
 22 | 		2B69BA7C1FFD2F91008C28A5 /* JSWKInterceptViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B69BA7B1FFD2F91008C28A5 /* JSWKInterceptViewController.m */; };
 23 | 		2B69BA7F1FFD2FA6008C28A5 /* JSWKBridgeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B69BA7E1FFD2FA6008C28A5 /* JSWKBridgeViewController.m */; };
 24 | 		2B69BA851FFD34A4008C28A5 /* JSContextModel.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B69BA841FFD34A4008C28A5 /* JSContextModel.m */; };
 25 | 		2B89FA291FEAB1EC001349A7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B89FA281FEAB1EC001349A7 /* AppDelegate.m */; };
 26 | 		2B89FA311FEAB1EC001349A7 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2B89FA301FEAB1EC001349A7 /* Assets.xcassets */; };
 27 | 		2B89FA371FEAB1EC001349A7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B89FA361FEAB1EC001349A7 /* main.m */; };
 28 | 		2B89FA3F1FEAB7FD001349A7 /* JSHomeViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B89FA3E1FEAB7FD001349A7 /* JSHomeViewController.m */; };
 29 | 		2B89FA431FEAB826001349A7 /* JSUIWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B89FA421FEAB826001349A7 /* JSUIWebViewController.m */; };
 30 | 		2B89FA461FEAB833001349A7 /* JSWKWebViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 2B89FA451FEAB833001349A7 /* JSWKWebViewController.m */; };
 31 | 		2B89FA481FEABCE6001349A7 /* jsbridge.html in Resources */ = {isa = PBXBuildFile; fileRef = 2B89FA471FEABCE6001349A7 /* jsbridge.html */; };
 32 | 		2BEB86331FEC0D090015F8E0 /* NSDictionary+Json.m in Sources */ = {isa = PBXBuildFile; fileRef = 2BEB86321FEC0D090015F8E0 /* NSDictionary+Json.m */; };
 33 | 		B54D907C4B6FA64364AAF23E /* webkit.html in Resources */ = {isa = PBXBuildFile; fileRef = B54D9BF70A906DA37BB0711A /* webkit.html */; };
 34 | /* End PBXBuildFile section */
 35 | 
 36 | /* Begin PBXFileReference section */
 37 | 		2B2A0041200875E500AC8FA8 /* JSHomeTableViewCell.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSHomeTableViewCell.h; sourceTree = ""; };
 38 | 		2B2A0042200875E500AC8FA8 /* JSHomeTableViewCell.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSHomeTableViewCell.m; sourceTree = ""; };
 39 | 		2B2A0044200875F200AC8FA8 /* JSHomeTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = JSHomeTableViewCell.xib; sourceTree = ""; };
 40 | 		2B39E35D1FF40165009238A1 /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = ""; };
 41 | 		2B39E3601FF40251009238A1 /* JSHomeModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSHomeModel.h; sourceTree = ""; };
 42 | 		2B39E3611FF40251009238A1 /* JSHomeModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSHomeModel.m; sourceTree = ""; };
 43 | 		2B4D617E202A197600D7183A /* JSWKWebKitViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSWKWebKitViewController.h; sourceTree = ""; };
 44 | 		2B4D617F202A197600D7183A /* JSWKWebKitViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSWKWebKitViewController.m; sourceTree = ""; };
 45 | 		2B65FC5E1FFBDAA0000322A1 /* intercept.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = intercept.html; sourceTree = ""; };
 46 | 		2B65FC621FFBDAE9000322A1 /* jscontext.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = jscontext.html; sourceTree = ""; };
 47 | 		2B65FC661FFBE624000322A1 /* JSUIInterceptViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSUIInterceptViewController.h; sourceTree = ""; };
 48 | 		2B65FC671FFBE624000322A1 /* JSUIInterceptViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSUIInterceptViewController.m; sourceTree = ""; };
 49 | 		2B65FC691FFBE661000322A1 /* JSUIJsContextViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSUIJsContextViewController.h; sourceTree = ""; };
 50 | 		2B65FC6A1FFBE661000322A1 /* JSUIJsContextViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSUIJsContextViewController.m; sourceTree = ""; };
 51 | 		2B65FC6C1FFBE72D000322A1 /* JSUIBridgeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSUIBridgeViewController.h; sourceTree = ""; };
 52 | 		2B65FC6D1FFBE72D000322A1 /* JSUIBridgeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSUIBridgeViewController.m; sourceTree = ""; };
 53 | 		2B65FC721FFBE840000322A1 /* NSURL+Params.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSURL+Params.h"; sourceTree = ""; };
 54 | 		2B65FC731FFBE840000322A1 /* NSURL+Params.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSURL+Params.m"; sourceTree = ""; };
 55 | 		2B69BA7A1FFD2F91008C28A5 /* JSWKInterceptViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSWKInterceptViewController.h; sourceTree = ""; };
 56 | 		2B69BA7B1FFD2F91008C28A5 /* JSWKInterceptViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSWKInterceptViewController.m; sourceTree = ""; };
 57 | 		2B69BA7D1FFD2FA6008C28A5 /* JSWKBridgeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSWKBridgeViewController.h; sourceTree = ""; };
 58 | 		2B69BA7E1FFD2FA6008C28A5 /* JSWKBridgeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSWKBridgeViewController.m; sourceTree = ""; };
 59 | 		2B69BA831FFD34A4008C28A5 /* JSContextModel.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSContextModel.h; sourceTree = ""; };
 60 | 		2B69BA841FFD34A4008C28A5 /* JSContextModel.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSContextModel.m; sourceTree = ""; };
 61 | 		2B89FA241FEAB1EC001349A7 /* JsInterface.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = JsInterface.app; sourceTree = BUILT_PRODUCTS_DIR; };
 62 | 		2B89FA271FEAB1EC001349A7 /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; };
 63 | 		2B89FA281FEAB1EC001349A7 /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; };
 64 | 		2B89FA301FEAB1EC001349A7 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
 65 | 		2B89FA351FEAB1EC001349A7 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
 66 | 		2B89FA361FEAB1EC001349A7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; };
 67 | 		2B89FA3D1FEAB7FD001349A7 /* JSHomeViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSHomeViewController.h; sourceTree = ""; };
 68 | 		2B89FA3E1FEAB7FD001349A7 /* JSHomeViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSHomeViewController.m; sourceTree = ""; };
 69 | 		2B89FA411FEAB826001349A7 /* JSUIWebViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSUIWebViewController.h; sourceTree = ""; };
 70 | 		2B89FA421FEAB826001349A7 /* JSUIWebViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSUIWebViewController.m; sourceTree = ""; };
 71 | 		2B89FA441FEAB833001349A7 /* JSWKWebViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JSWKWebViewController.h; sourceTree = ""; };
 72 | 		2B89FA451FEAB833001349A7 /* JSWKWebViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JSWKWebViewController.m; sourceTree = ""; };
 73 | 		2B89FA471FEABCE6001349A7 /* jsbridge.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = jsbridge.html; sourceTree = ""; };
 74 | 		2BEB86311FEC0D090015F8E0 /* NSDictionary+Json.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "NSDictionary+Json.h"; sourceTree = ""; };
 75 | 		2BEB86321FEC0D090015F8E0 /* NSDictionary+Json.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = "NSDictionary+Json.m"; sourceTree = ""; };
 76 | 		3D4F34A4DDAE73D3185F77E2 /* Pods-JsInterface.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JsInterface.release.xcconfig"; path = "Pods/Target Support Files/Pods-JsInterface/Pods-JsInterface.release.xcconfig"; sourceTree = ""; };
 77 | 		7F8F24BA95BC4A8D4709671E /* libPods-JsInterface.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-JsInterface.a"; sourceTree = BUILT_PRODUCTS_DIR; };
 78 | 		A15400B2138DCEB842EA3E71 /* Pods-JsInterface.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-JsInterface.debug.xcconfig"; path = "Pods/Target Support Files/Pods-JsInterface/Pods-JsInterface.debug.xcconfig"; sourceTree = ""; };
 79 | 		B54D9BF70A906DA37BB0711A /* webkit.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = webkit.html; sourceTree = ""; };
 80 | /* End PBXFileReference section */
 81 | 
 82 | /* Begin PBXFrameworksBuildPhase section */
 83 | 		2B89FA211FEAB1EC001349A7 /* Frameworks */ = {
 84 | 			isa = PBXFrameworksBuildPhase;
 85 | 			buildActionMask = 2147483647;
 86 | 			files = (
 87 | 				00A1806C621D3EB9E35C5C7A /* libPods-JsInterface.a in Frameworks */,
 88 | 			);
 89 | 			runOnlyForDeploymentPostprocessing = 0;
 90 | 		};
 91 | /* End PBXFrameworksBuildPhase section */
 92 | 
 93 | /* Begin PBXGroup section */
 94 | 		002C85708F37FA6F48137D9F /* Frameworks */ = {
 95 | 			isa = PBXGroup;
 96 | 			children = (
 97 | 				7F8F24BA95BC4A8D4709671E /* libPods-JsInterface.a */,
 98 | 			);
 99 | 			name = Frameworks;
100 | 			sourceTree = "";
101 | 		};
102 | 		2B39E34B1FF3FE6F009238A1 /* Resource */ = {
103 | 			isa = PBXGroup;
104 | 			children = (
105 | 				2B39E34C1FF3FEA3009238A1 /* html */,
106 | 			);
107 | 			path = Resource;
108 | 			sourceTree = "";
109 | 		};
110 | 		2B39E34C1FF3FEA3009238A1 /* html */ = {
111 | 			isa = PBXGroup;
112 | 			children = (
113 | 				2B89FA471FEABCE6001349A7 /* jsbridge.html */,
114 | 				2B65FC5E1FFBDAA0000322A1 /* intercept.html */,
115 | 				2B65FC621FFBDAE9000322A1 /* jscontext.html */,
116 | 				B54D9BF70A906DA37BB0711A /* webkit.html */,
117 | 			);
118 | 			path = html;
119 | 			sourceTree = "";
120 | 		};
121 | 		2B39E34D1FF3FEBC009238A1 /* AppDelegate */ = {
122 | 			isa = PBXGroup;
123 | 			children = (
124 | 				2B89FA271FEAB1EC001349A7 /* AppDelegate.h */,
125 | 				2B89FA281FEAB1EC001349A7 /* AppDelegate.m */,
126 | 			);
127 | 			path = AppDelegate;
128 | 			sourceTree = "";
129 | 		};
130 | 		2B39E34E1FF3FECA009238A1 /* Pages */ = {
131 | 			isa = PBXGroup;
132 | 			children = (
133 | 				2B39E35C1FF4012B009238A1 /* Home */,
134 | 				2B39E3581FF400F9009238A1 /* UIWebView */,
135 | 				2B39E3551FF400F9009238A1 /* WKWebView */,
136 | 			);
137 | 			path = Pages;
138 | 			sourceTree = "";
139 | 		};
140 | 		2B39E34F1FF3FEDB009238A1 /* View */ = {
141 | 			isa = PBXGroup;
142 | 			children = (
143 | 				2B2A0041200875E500AC8FA8 /* JSHomeTableViewCell.h */,
144 | 				2B2A0042200875E500AC8FA8 /* JSHomeTableViewCell.m */,
145 | 				2B2A0044200875F200AC8FA8 /* JSHomeTableViewCell.xib */,
146 | 			);
147 | 			path = View;
148 | 			sourceTree = "";
149 | 		};
150 | 		2B39E3501FF3FEE7009238A1 /* Controller */ = {
151 | 			isa = PBXGroup;
152 | 			children = (
153 | 				2B89FA3D1FEAB7FD001349A7 /* JSHomeViewController.h */,
154 | 				2B89FA3E1FEAB7FD001349A7 /* JSHomeViewController.m */,
155 | 			);
156 | 			path = Controller;
157 | 			sourceTree = "";
158 | 		};
159 | 		2B39E3511FF3FEF0009238A1 /* Category */ = {
160 | 			isa = PBXGroup;
161 | 			children = (
162 | 				2BEB86311FEC0D090015F8E0 /* NSDictionary+Json.h */,
163 | 				2BEB86321FEC0D090015F8E0 /* NSDictionary+Json.m */,
164 | 				2B65FC721FFBE840000322A1 /* NSURL+Params.h */,
165 | 				2B65FC731FFBE840000322A1 /* NSURL+Params.m */,
166 | 			);
167 | 			path = Category;
168 | 			sourceTree = "";
169 | 		};
170 | 		2B39E3551FF400F9009238A1 /* WKWebView */ = {
171 | 			isa = PBXGroup;
172 | 			children = (
173 | 				2B39E3561FF400F9009238A1 /* Controller */,
174 | 				2B39E3571FF400F9009238A1 /* View */,
175 | 			);
176 | 			path = WKWebView;
177 | 			sourceTree = "";
178 | 		};
179 | 		2B39E3561FF400F9009238A1 /* Controller */ = {
180 | 			isa = PBXGroup;
181 | 			children = (
182 | 				2B89FA441FEAB833001349A7 /* JSWKWebViewController.h */,
183 | 				2B89FA451FEAB833001349A7 /* JSWKWebViewController.m */,
184 | 				2B69BA7A1FFD2F91008C28A5 /* JSWKInterceptViewController.h */,
185 | 				2B69BA7B1FFD2F91008C28A5 /* JSWKInterceptViewController.m */,
186 | 				2B69BA7D1FFD2FA6008C28A5 /* JSWKBridgeViewController.h */,
187 | 				2B69BA7E1FFD2FA6008C28A5 /* JSWKBridgeViewController.m */,
188 | 				2B4D617E202A197600D7183A /* JSWKWebKitViewController.h */,
189 | 				2B4D617F202A197600D7183A /* JSWKWebKitViewController.m */,
190 | 			);
191 | 			path = Controller;
192 | 			sourceTree = "";
193 | 		};
194 | 		2B39E3571FF400F9009238A1 /* View */ = {
195 | 			isa = PBXGroup;
196 | 			children = (
197 | 			);
198 | 			path = View;
199 | 			sourceTree = "";
200 | 		};
201 | 		2B39E3581FF400F9009238A1 /* UIWebView */ = {
202 | 			isa = PBXGroup;
203 | 			children = (
204 | 				2B39E3591FF400F9009238A1 /* Controller */,
205 | 				2B39E35A1FF400F9009238A1 /* View */,
206 | 			);
207 | 			path = UIWebView;
208 | 			sourceTree = "";
209 | 		};
210 | 		2B39E3591FF400F9009238A1 /* Controller */ = {
211 | 			isa = PBXGroup;
212 | 			children = (
213 | 				2B89FA411FEAB826001349A7 /* JSUIWebViewController.h */,
214 | 				2B89FA421FEAB826001349A7 /* JSUIWebViewController.m */,
215 | 				2B65FC661FFBE624000322A1 /* JSUIInterceptViewController.h */,
216 | 				2B65FC671FFBE624000322A1 /* JSUIInterceptViewController.m */,
217 | 				2B65FC691FFBE661000322A1 /* JSUIJsContextViewController.h */,
218 | 				2B65FC6A1FFBE661000322A1 /* JSUIJsContextViewController.m */,
219 | 				2B65FC6C1FFBE72D000322A1 /* JSUIBridgeViewController.h */,
220 | 				2B65FC6D1FFBE72D000322A1 /* JSUIBridgeViewController.m */,
221 | 			);
222 | 			path = Controller;
223 | 			sourceTree = "";
224 | 		};
225 | 		2B39E35A1FF400F9009238A1 /* View */ = {
226 | 			isa = PBXGroup;
227 | 			children = (
228 | 			);
229 | 			path = View;
230 | 			sourceTree = "";
231 | 		};
232 | 		2B39E35C1FF4012B009238A1 /* Home */ = {
233 | 			isa = PBXGroup;
234 | 			children = (
235 | 				2B39E3501FF3FEE7009238A1 /* Controller */,
236 | 				2B39E34F1FF3FEDB009238A1 /* View */,
237 | 			);
238 | 			path = Home;
239 | 			sourceTree = "";
240 | 		};
241 | 		2B39E35F1FF4023D009238A1 /* Model */ = {
242 | 			isa = PBXGroup;
243 | 			children = (
244 | 				2B39E3601FF40251009238A1 /* JSHomeModel.h */,
245 | 				2B39E3611FF40251009238A1 /* JSHomeModel.m */,
246 | 				2B69BA831FFD34A4008C28A5 /* JSContextModel.h */,
247 | 				2B69BA841FFD34A4008C28A5 /* JSContextModel.m */,
248 | 			);
249 | 			path = Model;
250 | 			sourceTree = "";
251 | 		};
252 | 		2B89FA1B1FEAB1EC001349A7 = {
253 | 			isa = PBXGroup;
254 | 			children = (
255 | 				2B89FA261FEAB1EC001349A7 /* JsInterface */,
256 | 				2B89FA251FEAB1EC001349A7 /* Products */,
257 | 				7270F82C0C33363EDDFB01A4 /* Pods */,
258 | 				002C85708F37FA6F48137D9F /* Frameworks */,
259 | 			);
260 | 			sourceTree = "";
261 | 		};
262 | 		2B89FA251FEAB1EC001349A7 /* Products */ = {
263 | 			isa = PBXGroup;
264 | 			children = (
265 | 				2B89FA241FEAB1EC001349A7 /* JsInterface.app */,
266 | 			);
267 | 			name = Products;
268 | 			sourceTree = "";
269 | 		};
270 | 		2B89FA261FEAB1EC001349A7 /* JsInterface */ = {
271 | 			isa = PBXGroup;
272 | 			children = (
273 | 				2B89FA401FEAB809001349A7 /* Class */,
274 | 				2B39E34B1FF3FE6F009238A1 /* Resource */,
275 | 				2B89FA361FEAB1EC001349A7 /* main.m */,
276 | 				2B89FA351FEAB1EC001349A7 /* Info.plist */,
277 | 				2B89FA301FEAB1EC001349A7 /* Assets.xcassets */,
278 | 				2B39E35D1FF40165009238A1 /* Launch Screen.storyboard */,
279 | 			);
280 | 			path = JsInterface;
281 | 			sourceTree = "";
282 | 		};
283 | 		2B89FA401FEAB809001349A7 /* Class */ = {
284 | 			isa = PBXGroup;
285 | 			children = (
286 | 				2B39E34D1FF3FEBC009238A1 /* AppDelegate */,
287 | 				2B39E3511FF3FEF0009238A1 /* Category */,
288 | 				2B39E35F1FF4023D009238A1 /* Model */,
289 | 				2B39E34E1FF3FECA009238A1 /* Pages */,
290 | 			);
291 | 			path = Class;
292 | 			sourceTree = "";
293 | 		};
294 | 		7270F82C0C33363EDDFB01A4 /* Pods */ = {
295 | 			isa = PBXGroup;
296 | 			children = (
297 | 				A15400B2138DCEB842EA3E71 /* Pods-JsInterface.debug.xcconfig */,
298 | 				3D4F34A4DDAE73D3185F77E2 /* Pods-JsInterface.release.xcconfig */,
299 | 			);
300 | 			name = Pods;
301 | 			sourceTree = "";
302 | 		};
303 | /* End PBXGroup section */
304 | 
305 | /* Begin PBXNativeTarget section */
306 | 		2B89FA231FEAB1EC001349A7 /* JsInterface */ = {
307 | 			isa = PBXNativeTarget;
308 | 			buildConfigurationList = 2B89FA3A1FEAB1EC001349A7 /* Build configuration list for PBXNativeTarget "JsInterface" */;
309 | 			buildPhases = (
310 | 				B6D09A500FBE3BE026806CC5 /* [CP] Check Pods Manifest.lock */,
311 | 				2B89FA201FEAB1EC001349A7 /* Sources */,
312 | 				2B89FA211FEAB1EC001349A7 /* Frameworks */,
313 | 				2B89FA221FEAB1EC001349A7 /* Resources */,
314 | 				D09F8F113DB5FCE7F7473DFE /* [CP] Embed Pods Frameworks */,
315 | 				4A850F872ECB370EFD15478C /* [CP] Copy Pods Resources */,
316 | 			);
317 | 			buildRules = (
318 | 			);
319 | 			dependencies = (
320 | 			);
321 | 			name = JsInterface;
322 | 			productName = JsBridge;
323 | 			productReference = 2B89FA241FEAB1EC001349A7 /* JsInterface.app */;
324 | 			productType = "com.apple.product-type.application";
325 | 		};
326 | /* End PBXNativeTarget section */
327 | 
328 | /* Begin PBXProject section */
329 | 		2B89FA1C1FEAB1EC001349A7 /* Project object */ = {
330 | 			isa = PBXProject;
331 | 			attributes = {
332 | 				CLASSPREFIX = JS;
333 | 				LastUpgradeCheck = 0920;
334 | 				ORGANIZATIONNAME = ganwenpeng.com;
335 | 				TargetAttributes = {
336 | 					2B89FA231FEAB1EC001349A7 = {
337 | 						CreatedOnToolsVersion = 9.2;
338 | 						ProvisioningStyle = Automatic;
339 | 					};
340 | 				};
341 | 			};
342 | 			buildConfigurationList = 2B89FA1F1FEAB1EC001349A7 /* Build configuration list for PBXProject "JsInterface" */;
343 | 			compatibilityVersion = "Xcode 8.0";
344 | 			developmentRegion = en;
345 | 			hasScannedForEncodings = 0;
346 | 			knownRegions = (
347 | 				en,
348 | 				Base,
349 | 			);
350 | 			mainGroup = 2B89FA1B1FEAB1EC001349A7;
351 | 			productRefGroup = 2B89FA251FEAB1EC001349A7 /* Products */;
352 | 			projectDirPath = "";
353 | 			projectRoot = "";
354 | 			targets = (
355 | 				2B89FA231FEAB1EC001349A7 /* JsInterface */,
356 | 			);
357 | 		};
358 | /* End PBXProject section */
359 | 
360 | /* Begin PBXResourcesBuildPhase section */
361 | 		2B89FA221FEAB1EC001349A7 /* Resources */ = {
362 | 			isa = PBXResourcesBuildPhase;
363 | 			buildActionMask = 2147483647;
364 | 			files = (
365 | 				2B39E35E1FF40165009238A1 /* Launch Screen.storyboard in Resources */,
366 | 				2B65FC5F1FFBDAA0000322A1 /* intercept.html in Resources */,
367 | 				2B89FA481FEABCE6001349A7 /* jsbridge.html in Resources */,
368 | 				2B2A0045200875F200AC8FA8 /* JSHomeTableViewCell.xib in Resources */,
369 | 				2B65FC631FFBDAE9000322A1 /* jscontext.html in Resources */,
370 | 				2B89FA311FEAB1EC001349A7 /* Assets.xcassets in Resources */,
371 | 				B54D907C4B6FA64364AAF23E /* webkit.html in Resources */,
372 | 			);
373 | 			runOnlyForDeploymentPostprocessing = 0;
374 | 		};
375 | /* End PBXResourcesBuildPhase section */
376 | 
377 | /* Begin PBXShellScriptBuildPhase section */
378 | 		4A850F872ECB370EFD15478C /* [CP] Copy Pods Resources */ = {
379 | 			isa = PBXShellScriptBuildPhase;
380 | 			buildActionMask = 2147483647;
381 | 			files = (
382 | 			);
383 | 			inputPaths = (
384 | 			);
385 | 			name = "[CP] Copy Pods Resources";
386 | 			outputPaths = (
387 | 			);
388 | 			runOnlyForDeploymentPostprocessing = 0;
389 | 			shellPath = /bin/sh;
390 | 			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-JsInterface/Pods-JsInterface-resources.sh\"\n";
391 | 			showEnvVarsInLog = 0;
392 | 		};
393 | 		B6D09A500FBE3BE026806CC5 /* [CP] Check Pods Manifest.lock */ = {
394 | 			isa = PBXShellScriptBuildPhase;
395 | 			buildActionMask = 2147483647;
396 | 			files = (
397 | 			);
398 | 			inputPaths = (
399 | 				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
400 | 				"${PODS_ROOT}/Manifest.lock",
401 | 			);
402 | 			name = "[CP] Check Pods Manifest.lock";
403 | 			outputPaths = (
404 | 				"$(DERIVED_FILE_DIR)/Pods-JsInterface-checkManifestLockResult.txt",
405 | 			);
406 | 			runOnlyForDeploymentPostprocessing = 0;
407 | 			shellPath = /bin/sh;
408 | 			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
409 | 			showEnvVarsInLog = 0;
410 | 		};
411 | 		D09F8F113DB5FCE7F7473DFE /* [CP] Embed Pods Frameworks */ = {
412 | 			isa = PBXShellScriptBuildPhase;
413 | 			buildActionMask = 2147483647;
414 | 			files = (
415 | 			);
416 | 			inputPaths = (
417 | 			);
418 | 			name = "[CP] Embed Pods Frameworks";
419 | 			outputPaths = (
420 | 			);
421 | 			runOnlyForDeploymentPostprocessing = 0;
422 | 			shellPath = /bin/sh;
423 | 			shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-JsInterface/Pods-JsInterface-frameworks.sh\"\n";
424 | 			showEnvVarsInLog = 0;
425 | 		};
426 | /* End PBXShellScriptBuildPhase section */
427 | 
428 | /* Begin PBXSourcesBuildPhase section */
429 | 		2B89FA201FEAB1EC001349A7 /* Sources */ = {
430 | 			isa = PBXSourcesBuildPhase;
431 | 			buildActionMask = 2147483647;
432 | 			files = (
433 | 				2B65FC681FFBE624000322A1 /* JSUIInterceptViewController.m in Sources */,
434 | 				2B89FA431FEAB826001349A7 /* JSUIWebViewController.m in Sources */,
435 | 				2B65FC6E1FFBE72D000322A1 /* JSUIBridgeViewController.m in Sources */,
436 | 				2B2A0043200875E500AC8FA8 /* JSHomeTableViewCell.m in Sources */,
437 | 				2B39E3621FF40251009238A1 /* JSHomeModel.m in Sources */,
438 | 				2B89FA461FEAB833001349A7 /* JSWKWebViewController.m in Sources */,
439 | 				2B69BA7F1FFD2FA6008C28A5 /* JSWKBridgeViewController.m in Sources */,
440 | 				2BEB86331FEC0D090015F8E0 /* NSDictionary+Json.m in Sources */,
441 | 				2B89FA371FEAB1EC001349A7 /* main.m in Sources */,
442 | 				2B4D6180202A197600D7183A /* JSWKWebKitViewController.m in Sources */,
443 | 				2B65FC741FFBE840000322A1 /* NSURL+Params.m in Sources */,
444 | 				2B65FC6B1FFBE661000322A1 /* JSUIJsContextViewController.m in Sources */,
445 | 				2B89FA3F1FEAB7FD001349A7 /* JSHomeViewController.m in Sources */,
446 | 				2B89FA291FEAB1EC001349A7 /* AppDelegate.m in Sources */,
447 | 				2B69BA851FFD34A4008C28A5 /* JSContextModel.m in Sources */,
448 | 				2B69BA7C1FFD2F91008C28A5 /* JSWKInterceptViewController.m in Sources */,
449 | 			);
450 | 			runOnlyForDeploymentPostprocessing = 0;
451 | 		};
452 | /* End PBXSourcesBuildPhase section */
453 | 
454 | /* Begin XCBuildConfiguration section */
455 | 		2B89FA381FEAB1EC001349A7 /* Debug */ = {
456 | 			isa = XCBuildConfiguration;
457 | 			buildSettings = {
458 | 				ALWAYS_SEARCH_USER_PATHS = NO;
459 | 				CLANG_ANALYZER_NONNULL = YES;
460 | 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
461 | 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
462 | 				CLANG_CXX_LIBRARY = "libc++";
463 | 				CLANG_ENABLE_MODULES = YES;
464 | 				CLANG_ENABLE_OBJC_ARC = YES;
465 | 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
466 | 				CLANG_WARN_BOOL_CONVERSION = YES;
467 | 				CLANG_WARN_COMMA = YES;
468 | 				CLANG_WARN_CONSTANT_CONVERSION = YES;
469 | 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
470 | 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
471 | 				CLANG_WARN_EMPTY_BODY = YES;
472 | 				CLANG_WARN_ENUM_CONVERSION = YES;
473 | 				CLANG_WARN_INFINITE_RECURSION = YES;
474 | 				CLANG_WARN_INT_CONVERSION = YES;
475 | 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
476 | 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
477 | 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
478 | 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
479 | 				CLANG_WARN_STRICT_PROTOTYPES = YES;
480 | 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
481 | 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
482 | 				CLANG_WARN_UNREACHABLE_CODE = YES;
483 | 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
484 | 				CODE_SIGN_IDENTITY = "iPhone Developer";
485 | 				COPY_PHASE_STRIP = NO;
486 | 				DEBUG_INFORMATION_FORMAT = dwarf;
487 | 				ENABLE_STRICT_OBJC_MSGSEND = YES;
488 | 				ENABLE_TESTABILITY = YES;
489 | 				GCC_C_LANGUAGE_STANDARD = gnu11;
490 | 				GCC_DYNAMIC_NO_PIC = NO;
491 | 				GCC_NO_COMMON_BLOCKS = YES;
492 | 				GCC_OPTIMIZATION_LEVEL = 0;
493 | 				GCC_PREPROCESSOR_DEFINITIONS = (
494 | 					"DEBUG=1",
495 | 					"$(inherited)",
496 | 				);
497 | 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
498 | 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
499 | 				GCC_WARN_UNDECLARED_SELECTOR = YES;
500 | 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
501 | 				GCC_WARN_UNUSED_FUNCTION = YES;
502 | 				GCC_WARN_UNUSED_VARIABLE = YES;
503 | 				IPHONEOS_DEPLOYMENT_TARGET = 11.2;
504 | 				MTL_ENABLE_DEBUG_INFO = YES;
505 | 				ONLY_ACTIVE_ARCH = YES;
506 | 				SDKROOT = iphoneos;
507 | 			};
508 | 			name = Debug;
509 | 		};
510 | 		2B89FA391FEAB1EC001349A7 /* Release */ = {
511 | 			isa = XCBuildConfiguration;
512 | 			buildSettings = {
513 | 				ALWAYS_SEARCH_USER_PATHS = NO;
514 | 				CLANG_ANALYZER_NONNULL = YES;
515 | 				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
516 | 				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
517 | 				CLANG_CXX_LIBRARY = "libc++";
518 | 				CLANG_ENABLE_MODULES = YES;
519 | 				CLANG_ENABLE_OBJC_ARC = YES;
520 | 				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
521 | 				CLANG_WARN_BOOL_CONVERSION = YES;
522 | 				CLANG_WARN_COMMA = YES;
523 | 				CLANG_WARN_CONSTANT_CONVERSION = YES;
524 | 				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
525 | 				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
526 | 				CLANG_WARN_EMPTY_BODY = YES;
527 | 				CLANG_WARN_ENUM_CONVERSION = YES;
528 | 				CLANG_WARN_INFINITE_RECURSION = YES;
529 | 				CLANG_WARN_INT_CONVERSION = YES;
530 | 				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
531 | 				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
532 | 				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
533 | 				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
534 | 				CLANG_WARN_STRICT_PROTOTYPES = YES;
535 | 				CLANG_WARN_SUSPICIOUS_MOVE = YES;
536 | 				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
537 | 				CLANG_WARN_UNREACHABLE_CODE = YES;
538 | 				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
539 | 				CODE_SIGN_IDENTITY = "iPhone Developer";
540 | 				COPY_PHASE_STRIP = NO;
541 | 				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
542 | 				ENABLE_NS_ASSERTIONS = NO;
543 | 				ENABLE_STRICT_OBJC_MSGSEND = YES;
544 | 				GCC_C_LANGUAGE_STANDARD = gnu11;
545 | 				GCC_NO_COMMON_BLOCKS = YES;
546 | 				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
547 | 				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
548 | 				GCC_WARN_UNDECLARED_SELECTOR = YES;
549 | 				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
550 | 				GCC_WARN_UNUSED_FUNCTION = YES;
551 | 				GCC_WARN_UNUSED_VARIABLE = YES;
552 | 				IPHONEOS_DEPLOYMENT_TARGET = 11.2;
553 | 				MTL_ENABLE_DEBUG_INFO = NO;
554 | 				SDKROOT = iphoneos;
555 | 				VALIDATE_PRODUCT = YES;
556 | 			};
557 | 			name = Release;
558 | 		};
559 | 		2B89FA3B1FEAB1EC001349A7 /* Debug */ = {
560 | 			isa = XCBuildConfiguration;
561 | 			baseConfigurationReference = A15400B2138DCEB842EA3E71 /* Pods-JsInterface.debug.xcconfig */;
562 | 			buildSettings = {
563 | 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
564 | 				CODE_SIGN_STYLE = Automatic;
565 | 				INFOPLIST_FILE = JsInterface/Info.plist;
566 | 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
567 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
568 | 				PRODUCT_BUNDLE_IDENTIFIER = com.ganwenpeng.js;
569 | 				PRODUCT_NAME = "$(TARGET_NAME)";
570 | 				TARGETED_DEVICE_FAMILY = "1,2";
571 | 			};
572 | 			name = Debug;
573 | 		};
574 | 		2B89FA3C1FEAB1EC001349A7 /* Release */ = {
575 | 			isa = XCBuildConfiguration;
576 | 			baseConfigurationReference = 3D4F34A4DDAE73D3185F77E2 /* Pods-JsInterface.release.xcconfig */;
577 | 			buildSettings = {
578 | 				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
579 | 				CODE_SIGN_STYLE = Automatic;
580 | 				INFOPLIST_FILE = JsInterface/Info.plist;
581 | 				IPHONEOS_DEPLOYMENT_TARGET = 8.0;
582 | 				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
583 | 				PRODUCT_BUNDLE_IDENTIFIER = com.ganwenpeng.js;
584 | 				PRODUCT_NAME = "$(TARGET_NAME)";
585 | 				TARGETED_DEVICE_FAMILY = "1,2";
586 | 			};
587 | 			name = Release;
588 | 		};
589 | /* End XCBuildConfiguration section */
590 | 
591 | /* Begin XCConfigurationList section */
592 | 		2B89FA1F1FEAB1EC001349A7 /* Build configuration list for PBXProject "JsInterface" */ = {
593 | 			isa = XCConfigurationList;
594 | 			buildConfigurations = (
595 | 				2B89FA381FEAB1EC001349A7 /* Debug */,
596 | 				2B89FA391FEAB1EC001349A7 /* Release */,
597 | 			);
598 | 			defaultConfigurationIsVisible = 0;
599 | 			defaultConfigurationName = Release;
600 | 		};
601 | 		2B89FA3A1FEAB1EC001349A7 /* Build configuration list for PBXNativeTarget "JsInterface" */ = {
602 | 			isa = XCConfigurationList;
603 | 			buildConfigurations = (
604 | 				2B89FA3B1FEAB1EC001349A7 /* Debug */,
605 | 				2B89FA3C1FEAB1EC001349A7 /* Release */,
606 | 			);
607 | 			defaultConfigurationIsVisible = 0;
608 | 			defaultConfigurationName = Release;
609 | 		};
610 | /* End XCConfigurationList section */
611 | 	};
612 | 	rootObject = 2B89FA1C1FEAB1EC001349A7 /* Project object */;
613 | }
614 | 


--------------------------------------------------------------------------------
/iOS/JsInterface.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 | 
2 | 
4 |    
6 |    
7 | 
8 | 


--------------------------------------------------------------------------------
/iOS/JsInterface.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 4 |    
 6 |    
 7 |    
 9 |    
10 | 
11 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "images" : [
 3 |     {
 4 |       "idiom" : "iphone",
 5 |       "size" : "20x20",
 6 |       "scale" : "2x"
 7 |     },
 8 |     {
 9 |       "idiom" : "iphone",
10 |       "size" : "20x20",
11 |       "scale" : "3x"
12 |     },
13 |     {
14 |       "idiom" : "iphone",
15 |       "size" : "29x29",
16 |       "scale" : "2x"
17 |     },
18 |     {
19 |       "idiom" : "iphone",
20 |       "size" : "29x29",
21 |       "scale" : "3x"
22 |     },
23 |     {
24 |       "idiom" : "iphone",
25 |       "size" : "40x40",
26 |       "scale" : "2x"
27 |     },
28 |     {
29 |       "idiom" : "iphone",
30 |       "size" : "40x40",
31 |       "scale" : "3x"
32 |     },
33 |     {
34 |       "idiom" : "iphone",
35 |       "size" : "60x60",
36 |       "scale" : "2x"
37 |     },
38 |     {
39 |       "idiom" : "iphone",
40 |       "size" : "60x60",
41 |       "scale" : "3x"
42 |     },
43 |     {
44 |       "idiom" : "ipad",
45 |       "size" : "20x20",
46 |       "scale" : "1x"
47 |     },
48 |     {
49 |       "idiom" : "ipad",
50 |       "size" : "20x20",
51 |       "scale" : "2x"
52 |     },
53 |     {
54 |       "idiom" : "ipad",
55 |       "size" : "29x29",
56 |       "scale" : "1x"
57 |     },
58 |     {
59 |       "idiom" : "ipad",
60 |       "size" : "29x29",
61 |       "scale" : "2x"
62 |     },
63 |     {
64 |       "idiom" : "ipad",
65 |       "size" : "40x40",
66 |       "scale" : "1x"
67 |     },
68 |     {
69 |       "idiom" : "ipad",
70 |       "size" : "40x40",
71 |       "scale" : "2x"
72 |     },
73 |     {
74 |       "idiom" : "ipad",
75 |       "size" : "76x76",
76 |       "scale" : "1x"
77 |     },
78 |     {
79 |       "idiom" : "ipad",
80 |       "size" : "76x76",
81 |       "scale" : "2x"
82 |     },
83 |     {
84 |       "idiom" : "ipad",
85 |       "size" : "83.5x83.5",
86 |       "scale" : "2x"
87 |     },
88 |     {
89 |       "idiom" : "ios-marketing",
90 |       "size" : "1024x1024",
91 |       "scale" : "1x"
92 |     }
93 |   ],
94 |   "info" : {
95 |     "version" : 1,
96 |     "author" : "xcode"
97 |   }
98 | }


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/AppDelegate/AppDelegate.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  AppDelegate.h
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/20.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import 
10 | 
11 | @interface AppDelegate : UIResponder 
12 | 
13 | @property (strong, nonatomic) UIWindow *window;
14 | 
15 | 
16 | @end
17 | 
18 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/AppDelegate/AppDelegate.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  AppDelegate.m
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/20.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "AppDelegate.h"
10 | #import "JSHomeViewController.h"
11 | 
12 | @interface AppDelegate ()
13 | 
14 | @end
15 | 
16 | @implementation AppDelegate
17 | 
18 | 
19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
20 |     self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds];
21 |     self.window.backgroundColor = [UIColor whiteColor];
22 |     JSHomeViewController *homeVc = [[JSHomeViewController alloc] init];
23 |     self.window.rootViewController = [[UINavigationController alloc] initWithRootViewController:homeVc];
24 |     [self.window makeKeyAndVisible];
25 | 
26 |     return YES;
27 | }
28 | 
29 | 
30 | - (void)applicationWillResignActive:(UIApplication *)application {
31 |     // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
32 |     // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
33 | }
34 | 
35 | 
36 | - (void)applicationDidEnterBackground:(UIApplication *)application {
37 |     // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
38 |     // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
39 | }
40 | 
41 | 
42 | - (void)applicationWillEnterForeground:(UIApplication *)application {
43 |     // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
44 | }
45 | 
46 | 
47 | - (void)applicationDidBecomeActive:(UIApplication *)application {
48 |     // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
49 | }
50 | 
51 | 
52 | - (void)applicationWillTerminate:(UIApplication *)application {
53 |     // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
54 | }
55 | 
56 | 
57 | @end
58 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Category/NSDictionary+Json.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  NSDictionary+Json.h
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/21.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import 
10 | 
11 | @interface NSDictionary (Json)
12 | - (NSString *)jsonString;
13 | @end
14 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Category/NSDictionary+Json.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  NSDictionary+Json.m
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/21.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "NSDictionary+Json.h"
10 | 
11 | @implementation NSDictionary (Json)
12 | - (NSString *)jsonString {
13 |     NSError *error;
14 |     NSData *responseData = [NSJSONSerialization dataWithJSONObject:self options:NSJSONWritingPrettyPrinted error:&error];
15 |     if (error) return nil;
16 |     
17 |     return [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
18 | }
19 | @end
20 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Category/NSURL+Params.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  NSURL+Params.h
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import 
10 | 
11 | @interface NSURL (Params)
12 | - (NSDictionary *)getURLParams;
13 | @end
14 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Category/NSURL+Params.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  NSURL+Params.m
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "NSURL+Params.h"
10 | 
11 | @implementation NSURL (Params)
12 | - (NSDictionary *)getURLParams {
13 |     NSString *queryString = self.query;
14 |     NSArray *paramsArr = [queryString componentsSeparatedByString:@"&"];
15 |     NSMutableDictionary *paramsDict = [NSMutableDictionary dictionary];
16 |     for (NSString *item in paramsArr) {
17 |         NSArray *kv = [item componentsSeparatedByString:@"="];
18 |         if (kv.count == 2) {
19 |             paramsDict[kv[0]] = kv[1];
20 |         }
21 |     }
22 |     return [NSDictionary dictionaryWithDictionary:paramsDict];
23 | }
24 | @end
25 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Model/JSContextModel.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSContextModel.h
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import 
10 | #import 
11 | 
12 | @protocol JsContextExport
13 | /**
14 |  * 登出方法,js调用的方法名也是logout
15 |  */
16 | - (void)logout;
17 | 
18 | /**
19 |  * 登录方法,JSExportAs的作用就是给OC方法导出一个js方法名,例如下面的方法js调用就是 login("your account", "your password")。在多参数的方法声明时必须使用这种方式
20 |  */
21 | JSExportAs(login, - (void)loginWithAccount:(NSString *)account password:(NSString *)password);
22 | 
23 | /**
24 |  * 获取登录信息
25 |  * @return 当前登录用户的身份信息。JSContext方式调用OC时,方法的返回值只能是NSString、NSArray、NSDictionary、NSNumber、BooL,其他类型不能解析
26 |  */
27 | - (NSDictionary *)getLoginUser;
28 | @end
29 | 
30 | @interface JSContextModel : NSObject
31 | 
32 | @end
33 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Model/JSContextModel.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSContextModel.m
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSContextModel.h"
10 | #import 
11 | 
12 | @implementation JSContextModel
13 | - (void)logout {
14 |     [[NSOperationQueue mainQueue] addOperationWithBlock:^{
15 |         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:@"执行【登出】操作" delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
16 |         [alert show];
17 |     }];
18 | }
19 | 
20 | - (void)loginWithAccount:(NSString *)account password:(NSString *)password {
21 |     NSString *msg = [NSString stringWithFormat:@"执行登录操作,账号为:%@,密码为:%@", account, password];
22 |     [[NSOperationQueue mainQueue] addOperationWithBlock:^{
23 |         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
24 |         [alert show];
25 |     }];
26 | }
27 | 
28 | - (NSDictionary *)getLoginUser {
29 |     return @{
30 |              @"user_id": @(666),
31 |              @"username": @"你就说6不6",
32 |              @"sex": @"未知",
33 |              @"isStudent": @(NO)
34 |              };
35 | }
36 | 
37 | @end
38 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Model/JSHomeModel.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSHomeModel.h
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/28.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //  首页控制器上的数据模型
 8 | 
 9 | #import 
10 | #import 
11 | 
12 | @interface JSHomeModel : NSObject
13 | /**
14 |  * WebView的类型描述
15 |  */
16 | @property(nonatomic, copy) NSString *webViewType;
17 | /**
18 |  * JS调用Native的方式
19 |  */
20 | @property(nonatomic, copy) NSString *jsCallNative;
21 | /**
22 |  * Native调用JS的方式
23 |  */
24 | @property(nonatomic, copy) NSString *nativeCallJs;
25 | 
26 | /**
27 |  * 点击后跳转的控制器的Class
28 |  */
29 | @property(nonatomic, copy) Class targetClass;
30 | 
31 | /**
32 |  * 构造方法
33 |  * @param type WebView的类型
34 |  * @param jsCallNative JS调用Native的方式
35 |  * @param nativeCallJS Native调用JS的方式
36 |  * @param targetClass 点击后跳转的控制器
37 |  * @return 构造后的对象
38 |  */
39 | - (instancetype)initWithType:(NSString *)type jsCallNative:(NSString *)jsCallNative nativeCallJS:(NSString *)nativeCallJS targetClass:(Class)targetClass;
40 | @end
41 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Model/JSHomeModel.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSHomeModel.m
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/28.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSHomeModel.h"
10 | 
11 | @implementation JSHomeModel
12 | - (instancetype)initWithType:(NSString *)type jsCallNative:(NSString *)jsCallNative nativeCallJS:(NSString *)nativeCallJS targetClass:(Class)targetClass {
13 |     JSHomeModel *homeModel = [[JSHomeModel alloc] init];
14 |     homeModel.webViewType = type;
15 |     homeModel.jsCallNative = jsCallNative;
16 |     homeModel.nativeCallJs = nativeCallJS;
17 |     homeModel.targetClass = targetClass;
18 |     return homeModel;
19 | }
20 | 
21 | @end
22 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/Home/Controller/JSHomeViewController.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSHomeViewController.h
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/20.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //  首页列表控制器
 8 | 
 9 | #import 
10 | 
11 | @interface JSHomeViewController : UITableViewController
12 | 
13 | @end
14 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/Home/Controller/JSHomeViewController.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSHomeViewController.m
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/20.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSHomeViewController.h"
10 | #import "JSHomeModel.h"
11 | 
12 | #import "JSUIInterceptViewController.h"
13 | #import "JSUIJsContextViewController.h"
14 | #import "JSUIBridgeViewController.h"
15 | 
16 | #import "JSWKInterceptViewController.h"
17 | #import "JSWKBridgeViewController.h"
18 | #import "JSWKWebKitViewController.h"
19 | 
20 | #import "JSHomeTableViewCell.h"
21 | 
22 | @interface JSHomeViewController ()
23 | /**
24 |  * 列表数据
25 |  */
26 | @property(nonatomic, strong) NSArray *> *modelArray;
27 | @end
28 | 
29 | @implementation JSHomeViewController
30 | 
31 | - (instancetype)initWithStyle:(UITableViewStyle)style {
32 |     return self = [super initWithStyle:UITableViewStyleGrouped];
33 | }
34 | 
35 | - (void)viewDidLoad {
36 |     [super viewDidLoad];
37 | 
38 |     self.title = @"iOS & H5 交互";
39 |     self.navigationItem.backBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"返回" style:UIBarButtonItemStyleDone target:nil action:nil];
40 |     [self.tableView registerNib:[UINib nibWithNibName:NSStringFromClass([JSHomeTableViewCell class]) bundle:nil] forCellReuseIdentifier:NSStringFromClass([JSHomeTableViewCell class])];
41 | }
42 | 
43 | - (NSArray *> *)modelArray {
44 |     if (!_modelArray) {
45 |         JSHomeModel *uiwebviewIntercept = [[JSHomeModel alloc] initWithType:@"UIWebView" jsCallNative:@"拦截跳转" nativeCallJS:@"stringByEvaluatingJavaScriptFromString:" targetClass:[JSUIInterceptViewController class]];
46 |         JSHomeModel *wkwebviewIntercept = [[JSHomeModel alloc] initWithType:@"WKWebView" jsCallNative:@"拦截跳转" nativeCallJS:@"evaluateJavaScript:completionHandler" targetClass:[JSWKInterceptViewController class]];
47 | 
48 |         JSHomeModel *uiwebviewJsContext = [[JSHomeModel alloc] initWithType:@"UIWebView" jsCallNative:@"JsContextExport" nativeCallJS:@"evaluateScript: 或 callWithArguments:" targetClass:[JSUIJsContextViewController class]];
49 |         
50 |         JSHomeModel *wkWebKit = [[JSHomeModel alloc] initWithType:@"WKWebView" jsCallNative:@"window.webkit.messageHandlers" nativeCallJS:@"evaluateJavaScript:completionHandler" targetClass:[JSWKWebKitViewController class]];
51 | 
52 |         JSHomeModel *uiJsbridge = [[JSHomeModel alloc] initWithType:@"UIWebView" jsCallNative:@"WebViewJavascriptBridge" nativeCallJS:@"callHandler" targetClass:[JSUIBridgeViewController class]];
53 |         JSHomeModel *wkJsbridge = [[JSHomeModel alloc] initWithType:@"WKWebView" jsCallNative:@"WebViewJavascriptBridge" nativeCallJS:@"callHandler" targetClass:[JSWKBridgeViewController class]];
54 | 
55 |         _modelArray = @[
56 |                 @[uiwebviewIntercept, wkwebviewIntercept],
57 |                 @[uiwebviewJsContext],
58 |                 @[wkWebKit],
59 |                 @[uiJsbridge, wkJsbridge],
60 |         ];
61 |     }
62 |     return _modelArray;
63 | }
64 | 
65 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
66 |     return self.modelArray.count;
67 | }
68 | 
69 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
70 |     return self.modelArray[section].count;
71 | }
72 | 
73 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
74 |     JSHomeTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:NSStringFromClass([JSHomeTableViewCell class])];
75 |     JSHomeModel *model = self.modelArray[indexPath.section][indexPath.row];
76 |     cell.typeLabel.text = model.webViewType;
77 |     cell.jsCallNativeLabel.text = model.jsCallNative;
78 |     cell.nativeCallJsLabel.text = model.nativeCallJs;
79 | 
80 |     return cell;
81 | }
82 | 
83 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath {
84 |     JSHomeModel *model = self.modelArray[indexPath.section][indexPath.row];
85 |     
86 |     UIViewController *vc = (UIViewController *)[[model.targetClass alloc] init];
87 | 
88 |     [self.navigationController pushViewController:vc animated:YES];
89 | }
90 | @end
91 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/Home/View/JSHomeTableViewCell.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSHomeTableViewCell.h
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 12/01/2018.
 6 | //  Copyright © 2018 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import 
10 | 
11 | @interface JSHomeTableViewCell : UITableViewCell
12 | @property (weak, nonatomic) IBOutlet UILabel *typeLabel;
13 | @property (weak, nonatomic) IBOutlet UILabel *jsCallNativeLabel;
14 | @property (weak, nonatomic) IBOutlet UILabel *nativeCallJsLabel;
15 | 
16 | @end
17 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/Home/View/JSHomeTableViewCell.m:
--------------------------------------------------------------------------------
 1 | 
 2 | //
 3 | //  JSHomeTableViewCell.m
 4 | //  JsInterface
 5 | //
 6 | //  Created by 甘文鹏 on 12/01/2018.
 7 | //  Copyright © 2018 ganwenpeng.com. All rights reserved.
 8 | //
 9 | 
10 | #import "JSHomeTableViewCell.h"
11 | 
12 | @implementation JSHomeTableViewCell
13 | 
14 | - (void)awakeFromNib {
15 |     [super awakeFromNib];
16 |     
17 |     self.typeLabel.text = nil;
18 |     self.jsCallNativeLabel.text = nil;
19 |     self.nativeCallJsLabel.text = nil;
20 | }
21 | 
22 | - (void)setSelected:(BOOL)selected animated:(BOOL)animated {
23 |     [super setSelected:selected animated:animated];
24 | 
25 |     // Configure the view for the selected state
26 | }
27 | 
28 | @end
29 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/Home/View/JSHomeTableViewCell.xib:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |     
 4 |         
 5 |     
 6 |     
 7 |         
 8 |         
 9 |         
10 |     
11 |     
12 |         
13 |         
14 |         
15 |             
16 |             
17 |             
18 |                 
19 |                 
20 |                 
21 |                     
31 |                     
40 |                     
49 |                     
55 |                     
61 |                     
67 |                 
68 |                 
69 |                     
70 |                     
71 |                     
72 |                     
73 |                     
74 |                     
75 |                     
76 |                     
77 |                     
78 |                     
79 |                     
80 |                     
81 |                     
82 |                     
83 |                     
84 |                     
85 |                     
86 |                     
87 |                 
88 |             
89 |             
90 |                 
91 |                 
92 |                 
93 |             
94 |             
95 |         
96 |     
97 | 
98 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/UIWebView/Controller/JSUIBridgeViewController.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSUIBridgeViewController.h
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSUIWebViewController.h"
10 | 
11 | @interface JSUIBridgeViewController : JSUIWebViewController
12 | 
13 | @end
14 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/UIWebView/Controller/JSUIBridgeViewController.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSUIBridgeViewController.m
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSUIBridgeViewController.h"
10 | #import "NSDictionary+Json.h"
11 | 
12 | 
13 | @interface JSUIBridgeViewController ()
14 | @property(nonatomic, strong) WebViewJavascriptBridge* bridge;
15 | @end
16 | 
17 | @implementation JSUIBridgeViewController
18 | 
19 | - (void)viewDidLoad {
20 |     [super viewDidLoad];
21 |     
22 |     self.title = @"UIWebView - JSBridge";
23 |     
24 |     // 初始化jsbridge对象
25 |     [self setupJsBridge];
26 |     
27 |     // 加载测试用的HTML页面
28 |     NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"jsbridge" ofType:@"html"]];
29 |     [self.webView loadRequest:[NSURLRequest requestWithURL:url]];
30 | }
31 | 
32 | 
33 | - (void)btn1Click {
34 |     [self.bridge callHandler:@"jsbridge_showMessage" data:@"点击了原生的按钮111111111111" responseCallback:nil];
35 | }
36 | 
37 | - (void)btn2Click {
38 |     [self.bridge callHandler:@"jsbridge_getJsMessage" data:@"点击了原生的按钮222222222" responseCallback:^(id responseData) {
39 |         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"显示jsbridge返回值" message:responseData delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
40 |         [alert show];
41 |     }];
42 | }
43 | 
44 | - (void)setupJsBridge {
45 |     if (self.bridge) return;
46 |     
47 |     self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
48 |     
49 |     [self.bridge registerHandler:@"getOS" handler:^(id data, WVJBResponseCallback responseCallback) {
50 |         // 这里Response的回调可以传id类型数据,但是为了保持Android、iOS的统一,全部使用json字符串作为返回数据
51 |         NSDictionary *response = @{@"error": @(0), @"message": @"", @"data": @{@"os": @"ios"}};
52 |         responseCallback([response jsonString]);
53 |     }];
54 |     
55 |     [self.bridge registerHandler:@"login" handler:^(id data, WVJBResponseCallback responseCallback) {
56 |         if (data == nil || ![data isKindOfClass:[NSDictionary class]]) {
57 |             NSDictionary *response = @{@"error": @(-1), @"message": @"调用参数有误"};
58 |             responseCallback([response jsonString]);
59 |             return;
60 |         }
61 |         
62 |         NSString *account = data[@"account"];
63 |         NSString *passwd = data[@"password"];
64 |         NSDictionary *response = @{@"error": @(0), @"message": @"登录成功", @"data" : [NSString stringWithFormat:@"执行登录操作,账号为:%@、密码为:@%@", account, passwd]};
65 |         responseCallback([response jsonString]);
66 |     }];
67 | }
68 | 
69 | @end
70 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/UIWebView/Controller/JSUIInterceptViewController.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSUIInterceptViewController.h
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //  JS调用APP方法 - 拦截跳转 - UIWebView
 8 | 
 9 | #import "JSUIWebViewController.h"
10 | 
11 | @interface JSUIInterceptViewController : JSUIWebViewController
12 | 
13 | @end
14 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/UIWebView/Controller/JSUIInterceptViewController.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSUIInterceptViewController.m
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSUIInterceptViewController.h"
10 | #import "NSURL+Params.h"
11 | 
12 | @interface JSUIInterceptViewController ()
13 | 
14 | @end
15 | 
16 | @implementation JSUIInterceptViewController
17 | 
18 | - (void)viewDidLoad {
19 |     [super viewDidLoad];
20 |     self.title = @"UIWebView - 拦截跳转";
21 | 
22 |     // 加载测试用的HTML页面
23 |     NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"intercept" ofType:@"html"]];
24 |     [self.webView loadRequest:[NSURLRequest requestWithURL:url]];
25 | }
26 | 
27 | - (void)btn1Click {
28 |     [self.webView stringByEvaluatingJavaScriptFromString:@"showResponse('点击了原生的按钮11111111111')"];
29 | }
30 | 
31 | - (void)btn2Click {
32 |     [self.webView stringByEvaluatingJavaScriptFromString:@"showResponse('点击了原生的按钮2222222222')"];
33 | }
34 | 
35 | #pragma mark - UIWebViewDelegate
36 | - (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
37 |     
38 |     NSString *scheme = request.URL.scheme;
39 |     NSString *host = request.URL.host;
40 |     
41 |     // 一般用作交互的链接都会有一个固定的协议头,这里我们一“app”作为协议头为了,实际项目中可以修改
42 |     if ([scheme isEqualToString:@"app"]) { // scheme为“app”说明是做交互的链接
43 |         if ([host isEqualToString:@"login"]) { // host为“login”对应的就是登录操作
44 |             NSDictionary *paramsDict = [request.URL getURLParams];
45 |             NSString *account = paramsDict[@"account"];
46 |             NSString *password = paramsDict[@"password"];
47 |             
48 |             NSString *msg = [NSString stringWithFormat:@"执行登录操作,账号为:%@,密码为:%@", account, password];
49 |             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
50 |             [alert show];
51 |         } else if ([host isEqualToString:@"share"]) { // host为“share”对应的就是分享操作
52 |             NSDictionary *paramsDict = [request.URL getURLParams];
53 |             NSString *title = [paramsDict[@"title"] stringByRemovingPercentEncoding];
54 |             NSString *desc = [paramsDict[@"desc"] stringByRemovingPercentEncoding];
55 |             
56 |             NSString *msg = [NSString stringWithFormat:@"执行分享操作,title为:【%@】,desc为:【%@】", title, desc];
57 |             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
58 |             [alert show];
59 |         }
60 |         
61 |         return NO;
62 |     }
63 |     return YES;
64 | }
65 | 
66 | @end
67 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/UIWebView/Controller/JSUIJsContextViewController.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSUIJsContextViewController.h
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //  JS调用APP方法 - JSContext - UIWebView
 8 | 
 9 | #import "JSUIWebViewController.h"
10 | 
11 | @interface JSUIJsContextViewController : JSUIWebViewController
12 | 
13 | @end
14 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/UIWebView/Controller/JSUIJsContextViewController.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSUIJsContextViewController.m
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSUIJsContextViewController.h"
10 | #import 
11 | #import "JSContextModel.h"
12 | 
13 | @interface JSUIJsContextViewController ()
14 | @property(nonatomic, strong) JSContext *jsContext;
15 | @end
16 | 
17 | @implementation JSUIJsContextViewController
18 | 
19 | - (void)viewDidLoad {
20 |     [super viewDidLoad];
21 |     
22 |     self.title = @"UIWebView - JSContext";
23 |     
24 |     // 加载测试用的HTML页面
25 |     NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"jscontext" ofType:@"html"]];
26 |     [self.webView loadRequest:[NSURLRequest requestWithURL:url]];
27 | }
28 | 
29 | - (void)btn1Click {
30 |     [self.jsContext evaluateScript:@"showResponse('点击了按钮1111111111111111')"];
31 | }
32 | 
33 | - (void)btn2Click {
34 |     JSValue *value = self.jsContext[@"showResponse"];
35 |     [value callWithArguments:@[@"点击了按钮222222222"]];
36 | }
37 | 
38 | #pragma mark - UIWebViewDelegate
39 | - (void)webViewDidFinishLoad:(UIWebView *)webView {
40 |     self.jsContext = [webView valueForKeyPath:@"documentView.webView.mainFrame.javaScriptContext"];
41 |     self.jsContext.exceptionHandler = ^(JSContext *context, JSValue *exception) {
42 |         context.exception = exception;
43 |         NSLog(@"异常信息:%@", exception);
44 |     };
45 |     self.jsContext[@"app"] = [[JSContextModel alloc] init];
46 | }
47 | @end
48 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/UIWebView/Controller/JSUIWebViewController.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSUIWebViewController.h
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/20.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import 
10 | #import 
11 | 
12 | @interface JSUIWebViewController : UIViewController
13 | @property(nonatomic, strong) UIWebView *webView;
14 | 
15 | - (void)btn1Click;
16 | - (void)btn2Click;
17 | @end
18 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/UIWebView/Controller/JSUIWebViewController.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSUIWebViewController.m
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/20.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSUIWebViewController.h"
10 | #import 
11 | 
12 | @interface JSUIWebViewController ()
13 | @property(nonatomic, weak) UIButton *btn1;
14 | @property(nonatomic, weak) UIButton *btn2;
15 | @end
16 | 
17 | @implementation JSUIWebViewController
18 | - (void)viewDidLoad {
19 |     [super viewDidLoad];
20 |     
21 |     [self setupUI];
22 | }
23 | 
24 | - (void)setupUI {
25 |     self.edgesForExtendedLayout = UIRectEdgeNone;
26 |     
27 |     UIView *origin = [[UIView alloc] init];
28 |     origin.backgroundColor = [UIColor whiteColor];
29 |     [self.view addSubview:origin];
30 |     [origin mas_makeConstraints:^(MASConstraintMaker *make) {
31 |         make.left.top.right.equalTo(self.view);
32 |         make.height.mas_equalTo(100);
33 |     }];
34 |     
35 |     UILabel *titleLabel = [[UILabel alloc] init];
36 |     titleLabel.textColor = [UIColor redColor];
37 |     [origin addSubview:titleLabel];
38 |     [titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
39 |         make.left.top.equalTo(self.view).with.offset(8);
40 |         make.right.equalTo(self.view).with.offset(-8);
41 |     }];
42 |     titleLabel.text = @"原生区域";
43 |     
44 |     UIButton *btn1 = [[UIButton alloc] init];
45 |     btn1.backgroundColor = [UIColor grayColor];
46 |     [btn1 setTitle:@"按钮1" forState:UIControlStateNormal];
47 |     [origin addSubview:btn1];
48 |     [btn1 mas_makeConstraints:^(MASConstraintMaker *make) {
49 |         make.centerY.equalTo(origin).with.offset(8);
50 |         make.left.equalTo(origin).with.offset(8);
51 |         make.size.mas_equalTo(CGSizeMake(100, 40));
52 |     }];
53 |     [btn1 addTarget:self action:@selector(btn1Click) forControlEvents:UIControlEventTouchUpInside];
54 |     self.btn1 = btn1;
55 |     
56 |     UIButton *btn2 = [[UIButton alloc] init];
57 |     btn2.backgroundColor = [UIColor grayColor];
58 |     [btn2 setTitle:@"按钮2" forState:UIControlStateNormal];
59 |     [origin addSubview:btn2];
60 |     [btn2 mas_makeConstraints:^(MASConstraintMaker *make) {
61 |         make.centerY.equalTo(btn1);
62 |         make.left.equalTo(btn1.mas_right).with.offset(8);
63 |         make.size.mas_equalTo(CGSizeMake(100, 40));
64 |     }];
65 |     [btn2 addTarget:self action:@selector(btn2Click) forControlEvents:UIControlEventTouchUpInside];
66 |     self.btn2 = btn2;
67 |     
68 |     self.view.backgroundColor = [UIColor blackColor];
69 |     self.webView = [[UIWebView alloc] init];
70 |     self.webView.delegate = self;
71 |     [self.view addSubview:self.webView];
72 |     [self.webView mas_makeConstraints:^(MASConstraintMaker *make) {
73 |         make.left.bottom.right.equalTo(self.view);
74 |         make.top.equalTo(origin.mas_bottom).with.offset(1);
75 |     }];
76 | }
77 | 
78 | - (void)btn1Click {
79 | }
80 | 
81 | - (void)btn2Click {
82 | }
83 | 
84 | @end
85 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/WKWebView/Controller/JSWKBridgeViewController.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSWKBridgeViewController.h
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSWKWebViewController.h"
10 | 
11 | @interface JSWKBridgeViewController : JSWKWebViewController
12 | 
13 | @end
14 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/WKWebView/Controller/JSWKBridgeViewController.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSWKBridgeViewController.m
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSWKBridgeViewController.h"
10 | #import 
11 | #import "NSDictionary+Json.h"
12 | 
13 | @interface JSWKBridgeViewController ()
14 | @property(nonatomic, strong) WebViewJavascriptBridge* bridge;
15 | @end
16 | 
17 | @implementation JSWKBridgeViewController
18 | 
19 | - (void)viewDidLoad {
20 |     [super viewDidLoad];
21 |     
22 |     self.title = @"WKWebView - JSBridge";
23 |     
24 |     // 初始化jsbridge对象
25 |     [self setupJsBridge];
26 |     
27 |     // 加载测试用的HTML页面
28 |     NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"jsbridge" ofType:@"html"]];
29 |     [self.webView loadRequest:[NSURLRequest requestWithURL:url]];
30 | }
31 | 
32 | - (void)didReceiveMemoryWarning {
33 |     [super didReceiveMemoryWarning];
34 |     // Dispose of any resources that can be recreated.
35 | }
36 | 
37 | - (void)btn1Click {
38 |     [self.bridge callHandler:@"jsbridge_showMessage" data:@"点击了原生的按钮111111111111" responseCallback:nil];
39 | }
40 | 
41 | - (void)btn2Click {
42 |     [self.bridge callHandler:@"jsbridge_getJsMessage" data:@"点击了原生的按钮222222222" responseCallback:^(id responseData) {
43 |         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"显示jsbridge返回值" message:responseData delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
44 |         [alert show];
45 |     }];
46 | }
47 | 
48 | - (void)setupJsBridge {
49 |     if (self.bridge) return;
50 |     
51 |     self.bridge = [WebViewJavascriptBridge bridgeForWebView:self.webView];
52 |     
53 |     [self.bridge registerHandler:@"getOS" handler:^(id data, WVJBResponseCallback responseCallback) {
54 |         // 这里Response的回调可以传id类型数据,但是为了保持Android、iOS的统一,全部使用json字符串作为返回数据
55 |         NSDictionary *response = @{@"error": @(0), @"message": @"", @"data": @{@"os": @"ios"}};
56 |         responseCallback([response jsonString]);
57 |     }];
58 |     
59 |     [self.bridge registerHandler:@"login" handler:^(id data, WVJBResponseCallback responseCallback) {
60 |         if (data == nil || ![data isKindOfClass:[NSDictionary class]]) {
61 |             NSDictionary *response = @{@"error": @(-1), @"message": @"调用参数有误"};
62 |             responseCallback([response jsonString]);
63 |             return;
64 |         }
65 |         
66 |         NSString *account = data[@"account"];
67 |         NSString *passwd = data[@"password"];
68 |         NSDictionary *response = @{@"error": @(0), @"message": @"登录成功", @"data" : [NSString stringWithFormat:@"执行登录操作,账号为:%@、密码为:@%@", account, passwd]};
69 |         responseCallback([response jsonString]);
70 |     }];
71 | }
72 | @end
73 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/WKWebView/Controller/JSWKInterceptViewController.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSWKInterceptViewController.h
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSWKWebViewController.h"
10 | 
11 | @interface JSWKInterceptViewController : JSWKWebViewController
12 | 
13 | @end
14 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/WKWebView/Controller/JSWKInterceptViewController.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSWKInterceptViewController.m
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/1/3.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSWKInterceptViewController.h"
10 | #import "NSURL+Params.h"
11 | 
12 | @interface JSWKInterceptViewController ()
13 | 
14 | @end
15 | 
16 | @implementation JSWKInterceptViewController
17 | 
18 | - (void)viewDidLoad {
19 |     [super viewDidLoad];
20 |     
21 |     self.title = @"WKWebView - 拦截跳转";
22 |     
23 |     // 加载测试用的HTML页面
24 |     NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"intercept" ofType:@"html"]];
25 |     [self.webView loadRequest:[NSURLRequest requestWithURL:url]];
26 | }
27 | 
28 | - (void)btn1Click {
29 |     [self.webView evaluateJavaScript:@"showResponse('点击了原生的按钮11111111111')" completionHandler:nil];
30 | }
31 | 
32 | - (void)btn2Click {
33 |     [self.webView evaluateJavaScript:@"showResponse('点击了原生的按钮22222222222')" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
34 |         if (error) {
35 |             NSLog(@"%@", error);
36 |         } else {
37 |             NSLog(@"%@", response);
38 |         }
39 |     }];
40 | }
41 | 
42 | #pragma mark - WKNavigationDelegate
43 | - (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
44 |     
45 |     NSURLRequest *request = navigationAction.request;
46 |     NSString *scheme = request.URL.scheme;
47 |     NSString *host = request.URL.host;
48 |     
49 |     // 一般用作交互的链接都会有一个固定的协议头,这里我们一“app”作为协议头为了,实际项目中可以修改
50 |     if ([scheme isEqualToString:@"app"]) { // scheme为“app”说明是做交互的链接
51 |         if ([host isEqualToString:@"login"]) { // host为“login”对应的就是登录操作
52 |             NSDictionary *paramsDict = [request.URL getURLParams];
53 |             NSString *account = paramsDict[@"account"];
54 |             NSString *password = paramsDict[@"password"];
55 |             
56 |             NSString *msg = [NSString stringWithFormat:@"执行登录操作,账号为:%@,密码为:%@", account, password];
57 |             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
58 |             [alert show];
59 |         } else if ([host isEqualToString:@"share"]) { // host为“share”对应的就是分享操作
60 |             NSDictionary *paramsDict = [request.URL getURLParams];
61 |             NSString *title = [paramsDict[@"title"] stringByRemovingPercentEncoding];
62 |             NSString *desc = [paramsDict[@"desc"] stringByRemovingPercentEncoding];
63 |             
64 |             NSString *msg = [NSString stringWithFormat:@"执行分享操作,title为:【%@】,desc为:【%@】", title, desc];
65 |             UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
66 |             [alert show];
67 |         }
68 |         
69 |         // ... 这里可以继续加
70 |         
71 |         decisionHandler(WKNavigationActionPolicyCancel);
72 |         return;
73 |     }
74 |     decisionHandler(WKNavigationActionPolicyAllow);
75 | }
76 | @end
77 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/WKWebView/Controller/JSWKWebKitViewController.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSWKWebKitViewController.h
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/2/7.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSWKWebViewController.h"
10 | 
11 | @interface JSWKWebKitViewController : JSWKWebViewController
12 | 
13 | @end
14 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/WKWebView/Controller/JSWKWebKitViewController.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSWKWebKitViewController.m
 3 | //  JsInterface
 4 | //
 5 | //  Created by 甘文鹏 on 2018/2/7.
 6 | //  Copyright © 2018年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSWKWebKitViewController.h"
10 | 
11 | #define FUNC_LOGIN @"login"
12 | #define FUNC_LOGOUT @"logout"
13 | 
14 | @interface JSWKWebKitViewController ()
15 | 
16 | @end
17 | 
18 | @implementation JSWKWebKitViewController
19 | 
20 | - (void)viewDidLoad {
21 |     [super viewDidLoad];
22 |     
23 |     self.title = @"WKWebView - WebKti";
24 |     WKUserContentController *confVc = self.webView.configuration.userContentController;
25 |     [confVc addScriptMessageHandler:self name:FUNC_LOGIN];
26 |     [confVc addScriptMessageHandler:self name:FUNC_LOGOUT];
27 |     
28 |     // 加载测试用的HTML页面
29 |     NSURL *url = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"webkit" ofType:@"html"]];
30 |     [self.webView loadRequest:[NSURLRequest requestWithURL:url]];
31 | }
32 | 
33 | - (void)btn1Click {
34 |     [self.webView evaluateJavaScript:@"showResponse('点击了原生的按钮11111111111')" completionHandler:nil];
35 | }
36 | 
37 | - (void)btn2Click {
38 |     [self.webView evaluateJavaScript:@"showResponse('点击了原生的按钮22222222222')" completionHandler:^(id _Nullable response, NSError * _Nullable error) {
39 |         if (error) {
40 |             NSLog(@"%@", error);
41 |         } else {
42 |             NSLog(@"%@", response);
43 |         }
44 |     }];
45 | }
46 | 
47 | #pragma mark - WKScriptMessageHandler
48 | - (void)userContentController:(WKUserContentController *)userContentController didReceiveScriptMessage:(WKScriptMessage *)message {
49 |     if ([message.name isEqualToString:FUNC_LOGIN]) {
50 |         if (![message.body isKindOfClass:[NSDictionary class]]) {
51 |             return;
52 |         }
53 |         NSDictionary *data = message.body;
54 |         NSString *account = data[@"account"];
55 |         NSString *password = data[@"password"];
56 |         
57 |         NSString *msg = [NSString stringWithFormat:@"执行登录操作,账号为:%@,密码为:%@", account, password];
58 |         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:msg delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
59 |         [alert show];
60 |         return;
61 |     }
62 |     
63 |     if ([message.name isEqualToString:FUNC_LOGOUT]) {
64 |         UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"原生弹窗" message:@"执行登出操作" delegate:nil cancelButtonTitle:@"好" otherButtonTitles:nil, nil];
65 |         [alert show];
66 |         return;
67 |     }
68 | }
69 | @end
70 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/WKWebView/Controller/JSWKWebViewController.h:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSWKWebViewController.h
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/20.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import 
10 | #import 
11 | 
12 | @interface JSWKWebViewController : UIViewController
13 | @property(nonatomic, strong) WKWebView *webView;
14 | 
15 | - (void)btn1Click;
16 | - (void)btn2Click;
17 | @end
18 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Class/Pages/WKWebView/Controller/JSWKWebViewController.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  JSWKWebViewController.m
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/20.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import "JSWKWebViewController.h"
10 | #import 
11 | 
12 | @interface JSWKWebViewController ()
13 | @property(nonatomic, weak) UIButton *btn1;
14 | @property(nonatomic, weak) UIButton *btn2;
15 | @end
16 | 
17 | @implementation JSWKWebViewController
18 | - (void)viewDidLoad {
19 |     [super viewDidLoad];
20 |     
21 |     [self setupUI];
22 | }
23 | 
24 | - (void)setupUI {
25 |     self.edgesForExtendedLayout = UIRectEdgeNone;
26 |     
27 |     UIView *origin = [[UIView alloc] init];
28 |     origin.backgroundColor = [UIColor whiteColor];
29 |     [self.view addSubview:origin];
30 |     [origin mas_makeConstraints:^(MASConstraintMaker *make) {
31 |         make.left.top.right.equalTo(self.view);
32 |         make.height.mas_equalTo(100);
33 |     }];
34 |     
35 |     UILabel *titleLabel = [[UILabel alloc] init];
36 |     titleLabel.textColor = [UIColor redColor];
37 |     [origin addSubview:titleLabel];
38 |     [titleLabel mas_makeConstraints:^(MASConstraintMaker *make) {
39 |         make.left.top.equalTo(self.view).with.offset(8);
40 |         make.right.equalTo(self.view).with.offset(-8);
41 |     }];
42 |     titleLabel.text = @"原生区域";
43 |     
44 |     UIButton *btn1 = [[UIButton alloc] init];
45 |     btn1.backgroundColor = [UIColor grayColor];
46 |     [btn1 setTitle:@"按钮1" forState:UIControlStateNormal];
47 |     [origin addSubview:btn1];
48 |     [btn1 mas_makeConstraints:^(MASConstraintMaker *make) {
49 |         make.centerY.equalTo(origin).with.offset(8);
50 |         make.left.equalTo(origin).with.offset(8);
51 |         make.size.mas_equalTo(CGSizeMake(100, 40));
52 |     }];
53 |     [btn1 addTarget:self action:@selector(btn1Click) forControlEvents:UIControlEventTouchUpInside];
54 |     self.btn1 = btn1;
55 |     
56 |     UIButton *btn2 = [[UIButton alloc] init];
57 |     btn2.backgroundColor = [UIColor grayColor];
58 |     [btn2 setTitle:@"按钮2" forState:UIControlStateNormal];
59 |     [origin addSubview:btn2];
60 |     [btn2 mas_makeConstraints:^(MASConstraintMaker *make) {
61 |         make.centerY.equalTo(btn1);
62 |         make.left.equalTo(btn1.mas_right).with.offset(8);
63 |         make.size.mas_equalTo(CGSizeMake(100, 40));
64 |     }];
65 |     [btn2 addTarget:self action:@selector(btn2Click) forControlEvents:UIControlEventTouchUpInside];
66 |     self.btn2 = btn2;
67 |     
68 |     self.view.backgroundColor = [UIColor blackColor];
69 |     self.webView = [[WKWebView alloc] initWithFrame:CGRectZero configuration:[[WKWebViewConfiguration alloc] init]];
70 |     self.webView.navigationDelegate = self;
71 |     self.webView.UIDelegate = self;
72 |     [self.view addSubview:self.webView];
73 |     [self.webView mas_makeConstraints:^(MASConstraintMaker *make) {
74 |         make.left.bottom.right.equalTo(self.view);
75 |         make.top.equalTo(origin.mas_bottom).with.offset(1);
76 |     }];
77 | }
78 | 
79 | - (void)btn1Click {
80 | }
81 | 
82 | - (void)btn2Click {
83 | }
84 | 
85 | @end
86 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Info.plist:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 | 
 5 | 	CFBundleDevelopmentRegion
 6 | 	$(DEVELOPMENT_LANGUAGE)
 7 | 	CFBundleExecutable
 8 | 	$(EXECUTABLE_NAME)
 9 | 	CFBundleIdentifier
10 | 	$(PRODUCT_BUNDLE_IDENTIFIER)
11 | 	CFBundleInfoDictionaryVersion
12 | 	6.0
13 | 	CFBundleName
14 | 	$(PRODUCT_NAME)
15 | 	CFBundlePackageType
16 | 	APPL
17 | 	CFBundleShortVersionString
18 | 	1.0
19 | 	CFBundleVersion
20 | 	1
21 | 	LSRequiresIPhoneOS
22 | 	
23 | 	UILaunchStoryboardName
24 | 	Launch Screen
25 | 	UIRequiredDeviceCapabilities
26 | 	
27 | 		armv7
28 | 	
29 | 	UISupportedInterfaceOrientations
30 | 	
31 | 		UIInterfaceOrientationPortrait
32 | 	
33 | 	UISupportedInterfaceOrientations~ipad
34 | 	
35 | 		UIInterfaceOrientationPortrait
36 | 		UIInterfaceOrientationPortraitUpsideDown
37 | 		UIInterfaceOrientationLandscapeLeft
38 | 		UIInterfaceOrientationLandscapeRight
39 | 	
40 | 
41 | 
42 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Launch Screen.storyboard:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |     
 4 |         
 5 |     
 6 |     
 7 |         
 8 |         
 9 |         
10 |         
11 |     
12 |     
13 |         
14 |         
15 |             
16 |                 
17 |                     
18 |                         
19 |                         
20 |                     
21 |                     
22 |                         
23 |                         
24 |                         
25 |                             
31 |                             
37 |                         
38 |                         
39 |                         
40 |                             
41 |                             
42 |                             
43 |                             
44 |                             
45 |                             
46 |                         
47 |                     
48 |                 
49 |                 
50 |             
51 |             
52 |         
53 |     
54 | 
55 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Resource/html/intercept.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 |     拦截跳转实现通信
 5 |     
 6 |     
 8 |     
 9 | 
10 |     
31 | 
32 | 
33 | 

嵌入页区域

34 |

以拦截跳转链接的方式实现

35 | 36 | 37 | 38 | 39 |

40 | 
61 | 
62 | 
63 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Resource/html/jsbridge.html:
--------------------------------------------------------------------------------
  1 | 
  2 | 
  3 | 
  4 |     JSBridge Test
  5 |     
  6 |     
  8 |     
  9 | 
 10 |     
 31 | 
 32 | 
 33 | 

嵌入页区域

34 |

以JSBridge的方式实现

35 | 36 | 37 | 38 |

 39 | 
 40 | 
140 | 
141 | 
142 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Resource/html/jscontext.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 |     拦截跳转实现通信
 5 |     
 6 |     
 8 |     
 9 | 
10 |     
31 | 
32 | 
33 | 

嵌入页区域

34 |

以JSContext的方式实现

35 | 36 | 37 | 38 | 39 | 40 | 41 |

42 | 
66 | 
67 | 
68 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/Resource/html/webkit.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 |     JSBridge Test
 5 |     
 6 |     
 8 |     
 9 | 
10 |     
31 | 
32 | 
33 | 

嵌入页区域

34 |

以webkit的方式实现

35 | 36 | 37 | 38 |

39 | 
40 | 
65 | 
66 | 
67 | 


--------------------------------------------------------------------------------
/iOS/JsInterface/main.m:
--------------------------------------------------------------------------------
 1 | //
 2 | //  main.m
 3 | //  JsBridge
 4 | //
 5 | //  Created by 甘文鹏 on 2017/12/20.
 6 | //  Copyright © 2017年 ganwenpeng.com. All rights reserved.
 7 | //
 8 | 
 9 | #import 
10 | #import "AppDelegate.h"
11 | 
12 | int main(int argc, char * argv[]) {
13 |     @autoreleasepool {
14 |         return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
15 |     }
16 | }
17 | 


--------------------------------------------------------------------------------
/iOS/Podfile:
--------------------------------------------------------------------------------
1 | platform :ios, '8.0'
2 | inhibit_all_warnings!
3 | 
4 | target 'JsInterface' do
5 |     pod 'WebViewJavascriptBridge', '~> 6.0'
6 |     pod 'Masonry', '~> 1.1.0'
7 | end
8 | 


--------------------------------------------------------------------------------
/ios-home.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gwpp/jsinterface/29dbd20d8b00ceae956e1604fa5644403dc0fe90/ios-home.png


--------------------------------------------------------------------------------