├── .idea └── workspace.xml ├── JavascriptInterface ├── .gitignore ├── .idea │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── misc.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── javascriptinterface │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ ├── JSBridge.js │ │ │ └── index.html │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── javascriptinterface │ │ │ │ ├── CallBack.java │ │ │ │ ├── JSBridge.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── NativeMethods.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.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 │ │ └── example │ │ └── javascriptinterface │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── LICENSE ├── Prompt ├── .gitignore ├── .idea │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── misc.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── prompt │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ ├── JSBridge.js │ │ │ └── index.html │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── prompt │ │ │ │ ├── CallBack.java │ │ │ │ ├── JSBridge.java │ │ │ │ ├── JSBridgeChromeClient.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── NativeMethods.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.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 │ │ └── example │ │ └── prompt │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── README.md ├── UrlSchema ├── .gitignore ├── .idea │ ├── codeStyles │ │ └── Project.xml │ ├── gradle.xml │ ├── misc.xml │ ├── runConfigurations.xml │ └── vcs.xml ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── example │ │ │ └── urlschema │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ │ ├── JSBridge.js │ │ │ └── index.html │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── urlschema │ │ │ │ ├── CallBack.java │ │ │ │ ├── JSBridge.java │ │ │ │ ├── JSBridgeViewClient.java │ │ │ │ ├── MainActivity.java │ │ │ │ └── NativeMethods.java │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── layout │ │ │ └── activity_main.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 │ │ └── example │ │ └── urlschema │ │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle └── assets ├── addJavascriptInterface.png ├── evaluateJavascript.png ├── intercept-prompt.png ├── intercept-url.png └── loadUrl.png /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /JavascriptInterface/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /JavascriptInterface/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /JavascriptInterface/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /JavascriptInterface/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /JavascriptInterface/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /JavascriptInterface/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /JavascriptInterface/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /JavascriptInterface/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.example.javascriptinterface" 7 | minSdkVersion 19 8 | targetSdkVersion 28 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-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility = '1.8' 21 | targetCompatibility = '1.8' 22 | sourceCompatibility JavaVersion.VERSION_1_8 23 | targetCompatibility JavaVersion.VERSION_1_8 24 | } 25 | } 26 | 27 | dependencies { 28 | implementation fileTree(include: ['*.jar'], dir: 'libs') 29 | implementation 'com.android.support:appcompat-v7:28.0.0' 30 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 31 | testImplementation 'junit:junit:4.12' 32 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 33 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 34 | } 35 | -------------------------------------------------------------------------------- /JavascriptInterface/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 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/androidTest/java/com/example/javascriptinterface/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.javascriptinterface; 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() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.javascriptinterface", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/assets/JSBridge.js: -------------------------------------------------------------------------------- 1 | window.callbackId = 0; 2 | 3 | var bridge = { 4 | call: function(method, arg, cb) { 5 | var args = { 6 | data: arg === undefined ? null : JSON.stringify(arg), 7 | }; 8 | 9 | if (typeof cb === 'function') { 10 | var cbName = 'CALLBACK' + window.callbackId++; 11 | window[cbName] = cb; 12 | args['cbName'] = cbName; 13 | } 14 | 15 | if (window._jsbridge) { 16 | window._jsbridge.call(method, JSON.stringify(args)); 17 | } 18 | } 19 | }; 20 | 21 | module.exports = bridge; 22 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/java/com/example/javascriptinterface/CallBack.java: -------------------------------------------------------------------------------- 1 | package com.example.javascriptinterface; 2 | 3 | import android.webkit.ValueCallback; 4 | import android.webkit.WebView; 5 | 6 | import org.json.JSONObject; 7 | 8 | public class CallBack { 9 | private String cbName; 10 | private WebView mWebView; 11 | 12 | public CallBack(WebView webView, String cbName) { 13 | this.cbName = cbName; 14 | this.mWebView = webView; 15 | } 16 | 17 | public void apply(JSONObject jsonObject) { 18 | if (mWebView!=null) { 19 | mWebView.post(() -> { 20 | mWebView.evaluateJavascript("javascript:" + cbName + "(" + jsonObject.toString() + ")", new ValueCallback() { 21 | @Override 22 | public void onReceiveValue(String value) { 23 | return; 24 | } 25 | }); 26 | }); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/java/com/example/javascriptinterface/JSBridge.java: -------------------------------------------------------------------------------- 1 | package com.example.javascriptinterface; 2 | 3 | import android.net.Uri; 4 | import android.util.Log; 5 | import android.webkit.JavascriptInterface; 6 | import android.webkit.WebView; 7 | 8 | import org.json.JSONObject; 9 | 10 | import java.lang.reflect.Method; 11 | import java.lang.reflect.Modifier; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | public class JSBridge { 16 | private WebView mWebView; 17 | 18 | public JSBridge(WebView webView) { 19 | this.mWebView = webView; 20 | } 21 | 22 | 23 | private static Map> exposeMethods = new HashMap<>(); 24 | 25 | public static void register(String exposeName, Class classz) { 26 | if (!exposeMethods.containsKey(exposeName)) { 27 | exposeMethods.put(exposeName, getAllMethod(classz)); 28 | } 29 | } 30 | 31 | private static HashMap getAllMethod(Class injectedCls) { 32 | HashMap methodHashMap = new HashMap<>(); 33 | 34 | Method[] methods = injectedCls.getDeclaredMethods(); 35 | 36 | for (Method method: methods) { 37 | if(method.getModifiers()!=(Modifier.PUBLIC | Modifier.STATIC) || method.getName()==null) { 38 | continue; 39 | } 40 | Class[] parameters = method.getParameterTypes(); 41 | if (parameters!=null && parameters.length==3) { 42 | if (parameters[0] == WebView.class && parameters[1] == JSONObject.class && parameters[2] == CallBack.class) { 43 | methodHashMap.put(method.getName(), method); 44 | } 45 | } 46 | } 47 | 48 | return methodHashMap; 49 | } 50 | 51 | @JavascriptInterface 52 | public String call(String methodName, String args) { 53 | try { 54 | JSONObject _args = new JSONObject(args); 55 | JSONObject arg = new JSONObject(_args.getString("data")); 56 | String cbName = _args.getString("cbName"); 57 | 58 | 59 | if (exposeMethods.containsKey("JSBridge")) { 60 | HashMap methodHashMap = exposeMethods.get("JSBridge"); 61 | 62 | if (methodHashMap!=null && methodHashMap.size()!=0 && methodHashMap.containsKey(methodName)) { 63 | Method method = methodHashMap.get(methodName); 64 | 65 | if (method!=null) { 66 | method.invoke(null, mWebView, arg, new CallBack(mWebView, cbName)); 67 | } 68 | } 69 | } 70 | } catch (Exception e) { 71 | e.printStackTrace(); 72 | } 73 | 74 | 75 | return null; 76 | } 77 | } -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/java/com/example/javascriptinterface/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.javascriptinterface; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.KeyEvent; 6 | import android.webkit.WebChromeClient; 7 | import android.webkit.WebView; 8 | import android.webkit.WebViewClient; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | 12 | private WebView mWebView; 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_main); 18 | 19 | mWebView = (WebView) findViewById(R.id.mWebView); 20 | 21 | // 设置 webViewClient 类 22 | mWebView.setWebViewClient(new WebViewClient()); 23 | 24 | // 设置 webChromeClient 类 25 | mWebView.setWebChromeClient(new WebChromeClient()); 26 | 27 | // 设置支持调用 JS 28 | mWebView.getSettings().setJavaScriptEnabled(true); 29 | 30 | mWebView.loadUrl("file:///android_asset/index.html"); 31 | 32 | JSBridge.register("JSBridge", NativeMethods.class); 33 | mWebView.addJavascriptInterface(new JSBridge(mWebView), "_jsbridge"); 34 | } 35 | 36 | 37 | // 通过拦截 onKeyDown 事件实现网页回退 38 | @Override 39 | public boolean onKeyDown(int keyCode, KeyEvent event) { 40 | if(keyCode == event.KEYCODE_BACK) { 41 | if (mWebView.canGoBack()) { 42 | mWebView.goBack(); 43 | return true; 44 | } 45 | } 46 | 47 | return super.onKeyDown(keyCode, event); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/java/com/example/javascriptinterface/NativeMethods.java: -------------------------------------------------------------------------------- 1 | package com.example.javascriptinterface; 2 | 3 | import android.webkit.JavascriptInterface; 4 | import android.webkit.WebView; 5 | import android.widget.Toast; 6 | 7 | import org.json.JSONObject; 8 | 9 | public class NativeMethods { 10 | public static void showToast(WebView view, JSONObject arg, CallBack callBack) { 11 | String message = arg.optString("msg"); 12 | 13 | Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show(); 14 | 15 | if (callBack !=null) { 16 | try { 17 | JSONObject result = new JSONObject(); 18 | result.put("msg", "js 调用 native 成功!"); 19 | callBack.apply(result); 20 | } catch (Exception e) { 21 | e.printStackTrace(); 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /JavascriptInterface/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 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | JavascriptInterface 3 | 4 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /JavascriptInterface/app/src/test/java/com/example/javascriptinterface/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.javascriptinterface; 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() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /JavascriptInterface/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.3.2' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /JavascriptInterface/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /JavascriptInterface/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/JavascriptInterface/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /JavascriptInterface/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 17 20:15:06 CST 2019 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.10.1-all.zip 7 | -------------------------------------------------------------------------------- /JavascriptInterface/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /JavascriptInterface/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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /JavascriptInterface/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 mcuking 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 | -------------------------------------------------------------------------------- /Prompt/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /Prompt/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Prompt/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /Prompt/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /Prompt/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /Prompt/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Prompt/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /Prompt/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.example.prompt" 7 | minSdkVersion 19 8 | targetSdkVersion 28 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-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(include: ['*.jar'], dir: 'libs') 27 | implementation 'com.android.support:appcompat-v7:28.0.0' 28 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 29 | testImplementation 'junit:junit:4.12' 30 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 31 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 32 | } 33 | -------------------------------------------------------------------------------- /Prompt/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 | -------------------------------------------------------------------------------- /Prompt/app/src/androidTest/java/com/example/prompt/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.prompt; 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() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.prompt", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Prompt/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Prompt/app/src/main/assets/JSBridge.js: -------------------------------------------------------------------------------- 1 | window.callbackId = 0; 2 | 3 | var bridge = { 4 | call: function(method, arg, cb) { 5 | var args = { 6 | data: arg === undefined ? null : JSON.stringify(arg), 7 | }; 8 | 9 | if (typeof cb === 'function') { 10 | var cbName = 'CALLBACK' + window.callbackId++; 11 | window[cbName] = cb; 12 | args['cbName'] = cbName; 13 | } 14 | 15 | var url = 'jsbridge://' + method + '?' + JSON.stringify(args); 16 | 17 | prompt(url); 18 | } 19 | }; 20 | 21 | module.exports = bridge; 22 | -------------------------------------------------------------------------------- /Prompt/app/src/main/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /Prompt/app/src/main/java/com/example/prompt/CallBack.java: -------------------------------------------------------------------------------- 1 | package com.example.prompt; 2 | 3 | import android.util.Log; 4 | import android.webkit.ValueCallback; 5 | import android.webkit.WebView; 6 | 7 | import org.json.JSONObject; 8 | 9 | public class CallBack { 10 | private String cbName; 11 | private WebView mWebView; 12 | 13 | public CallBack(WebView webView, String cbName) { 14 | this.cbName = cbName; 15 | this.mWebView = webView; 16 | } 17 | 18 | public void apply(JSONObject jsonObject) { 19 | if (mWebView!=null) { 20 | mWebView.post(() -> { 21 | mWebView.evaluateJavascript("javascript:" + cbName + "(" + jsonObject.toString() + ")", new ValueCallback() { 22 | @Override 23 | public void onReceiveValue(String value) { 24 | return; 25 | } 26 | }); 27 | }); 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Prompt/app/src/main/java/com/example/prompt/JSBridge.java: -------------------------------------------------------------------------------- 1 | package com.example.prompt; 2 | 3 | import android.net.Uri; 4 | import android.webkit.WebView; 5 | 6 | import org.json.JSONObject; 7 | 8 | import java.lang.reflect.Method; 9 | import java.lang.reflect.Modifier; 10 | import java.util.HashMap; 11 | import java.util.Map; 12 | 13 | public class JSBridge { 14 | 15 | private static Map> exposeMethods = new HashMap<>(); 16 | 17 | public static void register(String exposeName, Class classz) { 18 | if (!exposeMethods.containsKey(exposeName)) { 19 | exposeMethods.put(exposeName, getAllMethod(classz)); 20 | } 21 | } 22 | 23 | private static HashMap getAllMethod(Class injectedCls) { 24 | HashMap methodHashMap = new HashMap<>(); 25 | 26 | Method[] methods = injectedCls.getDeclaredMethods(); 27 | 28 | for (Method method: methods) { 29 | if(method.getModifiers()!=(Modifier.PUBLIC | Modifier.STATIC) || method.getName()==null) { 30 | continue; 31 | } 32 | Class[] parameters = method.getParameterTypes(); 33 | if (parameters!=null && parameters.length==3) { 34 | if (parameters[0] == WebView.class && parameters[1] == JSONObject.class && parameters[2] == CallBack.class) { 35 | methodHashMap.put(method.getName(), method); 36 | } 37 | } 38 | } 39 | 40 | return methodHashMap; 41 | } 42 | 43 | 44 | public static String call(WebView webView, String urlString) { 45 | 46 | if (!urlString.equals("") && urlString!=null && urlString.startsWith("jsbridge")) { 47 | Uri uri = Uri.parse(urlString); 48 | 49 | String methodName = uri.getHost(); 50 | 51 | try { 52 | JSONObject args = new JSONObject(uri.getQuery()); 53 | JSONObject arg = new JSONObject(args.getString("data")); 54 | String cbName = args.getString("cbName"); 55 | 56 | 57 | if (exposeMethods.containsKey("JSBridge")) { 58 | HashMap methodHashMap = exposeMethods.get("JSBridge"); 59 | 60 | if (methodHashMap!=null && methodHashMap.size()!=0 && methodHashMap.containsKey(methodName)) { 61 | Method method = methodHashMap.get(methodName); 62 | 63 | if (method!=null) { 64 | method.invoke(null, webView, arg, new CallBack(webView, cbName)); 65 | } 66 | } 67 | } 68 | } catch (Exception e) { 69 | e.printStackTrace(); 70 | } 71 | 72 | } 73 | return null; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Prompt/app/src/main/java/com/example/prompt/JSBridgeChromeClient.java: -------------------------------------------------------------------------------- 1 | package com.example.prompt; 2 | 3 | import android.webkit.JsPromptResult; 4 | import android.webkit.WebChromeClient; 5 | import android.webkit.WebView; 6 | 7 | public class JSBridgeChromeClient extends WebChromeClient { 8 | @Override 9 | public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { 10 | result.confirm(JSBridge.call(view, message)); 11 | return true; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Prompt/app/src/main/java/com/example/prompt/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.prompt; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.KeyEvent; 6 | import android.webkit.WebView; 7 | import android.webkit.WebViewClient; 8 | 9 | public class MainActivity extends AppCompatActivity { 10 | 11 | private WebView mWebView; 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | 18 | mWebView = (WebView) findViewById(R.id.mWebView); 19 | 20 | // 设置 webViewClient 类 21 | mWebView.setWebViewClient(new WebViewClient()); 22 | 23 | // 设置 webChromeClient 类 24 | mWebView.setWebChromeClient(new JSBridgeChromeClient()); 25 | 26 | // 设置支持调用 JS 27 | mWebView.getSettings().setJavaScriptEnabled(true); 28 | 29 | mWebView.loadUrl("file:///android_asset/index.html"); 30 | 31 | JSBridge.register("JSBridge", NativeMethods.class); 32 | } 33 | 34 | // 通过拦截 onKeyDown 事件实现网页回退 35 | @Override 36 | public boolean onKeyDown(int keyCode, KeyEvent event) { 37 | if(keyCode == event.KEYCODE_BACK) { 38 | if (mWebView.canGoBack()) { 39 | mWebView.goBack(); 40 | return true; 41 | } 42 | } 43 | 44 | return super.onKeyDown(keyCode, event); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Prompt/app/src/main/java/com/example/prompt/NativeMethods.java: -------------------------------------------------------------------------------- 1 | package com.example.prompt; 2 | 3 | import android.webkit.WebView; 4 | import android.widget.Toast; 5 | 6 | import org.json.JSONObject; 7 | 8 | public class NativeMethods { 9 | public static void showToast(WebView view, JSONObject arg, CallBack callBack) { 10 | String message = arg.optString("msg"); 11 | 12 | Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show(); 13 | 14 | if (callBack !=null) { 15 | try { 16 | JSONObject result = new JSONObject(); 17 | result.put("msg", "js 调用 native 成功!"); 18 | callBack.apply(result); 19 | } catch (Exception e) { 20 | e.printStackTrace(); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Prompt/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /Prompt/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 | -------------------------------------------------------------------------------- /Prompt/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 21 | 22 | -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Prompt/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Prompt/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /Prompt/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Prompt 3 | 4 | -------------------------------------------------------------------------------- /Prompt/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Prompt/app/src/test/java/com/example/prompt/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.prompt; 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() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /Prompt/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.3.2' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /Prompt/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /Prompt/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/Prompt/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /Prompt/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun Mar 17 20:27:31 CST 2019 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.10.1-all.zip 7 | -------------------------------------------------------------------------------- /Prompt/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /Prompt/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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /Prompt/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JSBridge 2 | 3 | A demo JSBridge based on android 4 | 5 | 本项目以 js 与 android 通信为例,讲解 JSBridge 实现原理,下面提到的方法在 iOS(UIWebview 或 WKWebview)均有对应方法。 6 | 7 | ## 1. native to js 8 | 9 | 两种 native 调用 js 方法,注意被调用的方法需要在 JS 全局上下文上 10 | 11 | #### loadUrl 12 | 13 | #### evaluateJavascript 14 | 15 | ### 1.1 loadUrl 16 | 17 | 18 | 19 | ```java 20 | mWebview.loadUrl("javascript: func()"); 21 | ``` 22 | 23 | ### 1.2 evaluateJavascript 24 | 25 | 26 | 27 | ```java 28 | mWebview.evaluateJavascript("javascript: func()", new ValueCallback() { 29 | @Override 30 | public void onReceiveValue(String value) { 31 | return; 32 | } 33 | }); 34 | ``` 35 | 36 | #### 上述两种 native 调用 js 的方式对比如下表: 37 | 38 | | 方式 | 优点 | 缺点 | 39 | | ------------------ | ------------------------------------- | ----------------------------------------- | 40 | | loadUrl | 兼容性好 | 1. 会刷新页面 2. 无法获取 js 方法执行结果 | 41 | | evaluateJavascript | 1. 性能好 2. 可获取 js 执行后的返回值 | 仅在安卓 4.4 以上可用 | 42 | 43 | ## 2. js to native 44 | 45 | 三种 js 调用 native 方法 46 | 47 | #### 拦截 Url Schema(假请求) 48 | 49 | #### 拦截 prompt alert confirm 50 | 51 | #### 注入 JS 上下文 52 | 53 | ### 2.1 拦截 Url Schema 54 | 55 | 56 | 57 | 即由 h5 发出一条新的跳转请求,native 通过拦截 URL 获取 h5 传过来的数据。 58 | 59 | 跳转的目的地是一个非法不存在的 URL 地址,例如: 60 | 61 | ```javascript 62 | "jsbridge://methodName?{"data": arg, "cbName": cbName}" 63 | ``` 64 | 65 | 具体示例如下: 66 | 67 | ```javascript 68 | "jsbridge://openScan?{"data": {"scanType": "qrCode"}, "cbName": "handleScanResult"}" 69 | ``` 70 | 71 | h5 和 native 约定一个通信协议,例如 jsbridge, 同时约定调用 native 的方法名 methodName 作为域名,以及后面带上调用该方法的参数 arg,和接收该方法执行结果的 js 方法名 cbName。 72 | 73 | 具体可以在 js 端封装相关方法,供业务端统一调用,代码如下: 74 | 75 | ```javascript 76 | window.callbackId = 0; 77 | 78 | function callNative(methodName, arg, cb) { 79 | const args = { 80 | data: arg === undefined ? null : JSON.stringify(arg), 81 | }; 82 | 83 | if (typeof cb === 'function') { 84 | const cbName = 'CALLBACK' + window.callbackId++; 85 | window[cbName] = cb; 86 | args['cbName'] = cbName; 87 | } 88 | 89 | const url = 'jsbridge://' + methodName + '?' + JSON.stringify(args); 90 | 91 | ... 92 | } 93 | ``` 94 | 95 | 以上封装中较为巧妙的是将用于接收 native 执行结果的 js 回调方法 cb 挂载到 window 上,并为防止命名冲突,通过全局的 callbackId 来区分,然后将该回调函数在 window 上的名字放在参数中传给 native 端。native 拿到 cbName 后,执行完方法后,将执行结果通过 native 调用 js 的方式(上面提到的两种方法),调用 cb 传给 h5 端(例如将扫描结果传给 h5)。 96 | 97 | 至于如何在 h5 中发起请求,可以设置 window.location.href 或者创建一个新的 iframe 进行跳转。 98 | 99 | ```javascript 100 | function callNative(methodName, arg, cb) { 101 | ... 102 | 103 | const url = 'jsbridge://' + method + '?' + JSON.stringify(args); 104 | 105 | // 通过 location.href 跳转 106 | window.location.href = url; 107 | 108 | // 通过创建新的 iframe 跳转 109 | const iframe = document.createElement('iframe'); 110 | iframe.src = url; 111 | iframe.style.width = 0; 112 | iframe.style.height = 0; 113 | document.body.appendChild(iframe); 114 | 115 | window.setTimeout(function() { 116 | document.body.removeChild(iframe); 117 | }, 800); 118 | } 119 | ``` 120 | 121 | native 会拦截 h5 发出的请求,当检测到协议为 jsbridge 而非普通的 http/https/file 等协议时,会拦截该请求,解析出 URL 中的 methodName、arg、 cbName,执行该方法并调用 js 回调函数。 122 | 123 | 下面以安卓为例,通过覆盖 WebViewClient 类的 shouldOverrideUrlLoading 方法进行拦截,android 端具体封装会在下面单独的板块进行说明。 124 | 125 | ```java 126 | import android.util.Log; 127 | import android.webkit.WebView; 128 | import android.webkit.WebViewClient; 129 | 130 | public class JSBridgeViewClient extends WebViewClient { 131 | @Override 132 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 133 | JSBridge.call(view, url); 134 | return true; 135 | } 136 | } 137 | ``` 138 | 139 | #### 拦截 URL Schema 的问题 140 | 141 | - 连续发送时消息丢失 142 | 143 | 如下代码: 144 | 145 | ```javascript 146 | window.location.href = "jsbridge://callNativeNslog?{"data": "111", "cbName": ""}"; 147 | window.location.href = "jsbridge://callNativeNslog?{"data": "222", "cbName": ""}"; 148 | ``` 149 | 150 | js 此时的诉求是在同一个运行逻辑内,快速的连续发送出 2 个通信请求,用客户端本身 IDE 的 log,按顺序打印 111,222,那么实际结果是 222 的通信消息根本收不到,直接会被系统抛弃丢掉。 151 | 152 | 原因:因为 h5 的请求归根结底是一种模拟跳转,跳转这件事情上 webview 会有限制,当 h5 连续发送多条跳转的时候,webview 会直接过滤掉后发的跳转请求,因此第二个消息根本收不到,想要收到怎么办?js 里将第二条消息延时一下。 153 | 154 | ```javascript 155 | //发第一条消息 156 | location.href = "jsbridge://callNativeNslog?{"data": "111", "cbName": ""}"; 157 | 158 | //延时发送第二条消息 159 | setTimeout(500,function(){ 160 | location.href = "jsbridge://callNativeNslog?{"data": "222", "cbName": ""}"; 161 | }); 162 | ``` 163 | 164 | 但这并不能保证此时是否有其他地方通过这种方式进行请求,为系统解决此问题,js 端可以封装一层队列,所有 js 代码调用消息都先进入队列并不立刻发送,然后 h5 会周期性比如 500 毫秒,清空一次队列,保证在很快的时间内绝对不会连续发 2 次请求通信。 165 | 166 | - URL 长度限制 167 | 168 | 如果需要传输的数据较长,例如方法参数很多时,由于 URL 长度限制,仍以丢失部分数据。 169 | 170 | ### 2.2 拦截 prompt alert confirm 171 | 172 | 173 | 174 | 即由 h5 发起 alert confirm prompt,native 通过拦截 prompt 等获取 h5 传过来的数据。 175 | 176 | 因为 alert confirm 比较常用,所以一般通过 prompt 进行通信。 177 | 178 | 约定的传输数据的组合方式以及 js 端封装方法的可以类似上面的 拦截 URL Schema 提到的方式。 179 | 180 | ```javascript 181 | function callNative(methodName, arg, cb) { 182 | ... 183 | 184 | const url = 'jsbridge://' + method + '?' + JSON.stringify(args); 185 | 186 | prompt(url); 187 | } 188 | ``` 189 | 190 | native 会拦截 h5 发出的 prompt,当检测到协议为 jsbridge 而非普通的 http/https/file 等协议时,会拦截该请求,解析出 URL 中的 methodName、arg、 cbName,执行该方法并调用 js 回调函数。 191 | 192 | 下面以安卓为例,通过覆盖 WebChromeClient 类的 onJsPrompt 方法进行拦截,android 端具体封装会在下面单独的板块进行说明。 193 | 194 | ```java 195 | import android.webkit.JsPromptResult; 196 | import android.webkit.WebChromeClient; 197 | import android.webkit.WebView; 198 | 199 | public class JSBridgeChromeClient extends WebChromeClient { 200 | @Override 201 | public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) { 202 | result.confirm(JSBridge.call(view, message)); 203 | return true; 204 | } 205 | } 206 | ``` 207 | 208 | 这种方式没有太大缺点,也不存在连续发送时信息丢失。不过 iOS 的 UIWebView 不支持该方式(WKWebView 支持)。 209 | 210 | ### 2.3 注入 JS 上下文 211 | 212 | 213 | 214 | 即由 native 将实例对象通过 webview 提供的方法注入到 js 全局上下文,js 可以通过调用 native 的实例方法来进行通信。 215 | 216 | 具体有安卓 webview 的 addJavascriptInterface,iOS UIWebview 的 JSContext,iOS WKWebview 的 scriptMessageHandler。 217 | 218 | 下面以安卓 webview 的 addJavascriptInterface 为例进行讲解。 219 | 220 | 首先 native 端注入实例对象到 js 全局上下文,代码大致如下,具体封装会在下面的单独板块进行讲解: 221 | 222 | ```java 223 | public class MainActivity extends AppCompatActivity { 224 | 225 | private WebView mWebView; 226 | 227 | @Override 228 | protected void onCreate(Bundle savedInstanceState) { 229 | super.onCreate(savedInstanceState); 230 | setContentView(R.layout.activity_main); 231 | 232 | mWebView = (WebView) findViewById(R.id.mWebView); 233 | 234 | ... 235 | 236 | // 将 NativeMethods 类下面的提供给 js 的方法转换成 hashMap 237 | JSBridge.register("JSBridge", NativeMethods.class); 238 | 239 | // 将 JSBridge 的实例对象注入到 js 全局上下文中,名字为 _jsbridge,该实例对象下有 call 方法 240 | mWebView.addJavascriptInterface(new JSBridge(mWebView), "_jsbridge"); 241 | } 242 | } 243 | 244 | public class NativeMethods { 245 | // 用来供 js 调用的方法 246 | public static void methodName(WebView view, JSONObject arg, CallBack callBack) { 247 | } 248 | } 249 | 250 | public class JSBridge { 251 | private WebView mWebView; 252 | 253 | public JSBridge(WebView webView) { 254 | this.mWebView = webView; 255 | } 256 | 257 | 258 | private static Map> exposeMethods = new HashMap<>(); 259 | 260 | // 静态方法,用于将传入的第二个参数的类下面用于提供给 javacript 的接口转成 Map,名字为第一个参数 261 | public static void register(String exposeName, Class classz) { 262 | ... 263 | } 264 | 265 | // 实例方法,用于提供给 js 统一调用的方法 266 | @JavascriptInterface 267 | public String call(String methodName, String args) { 268 | ... 269 | } 270 | } 271 | ``` 272 | 273 | 然后 h5 端可以在 js 调用 window.\_jsbridge 实例下面的 call 方法,传入的数据组合方式可以类似上面两种方式。具体代码如下: 274 | 275 | ```javascript 276 | window.callbackId = 0; 277 | 278 | function callNative(method, arg, cb) { 279 | let args = { 280 | data: arg === undefined ? null : JSON.stringify(arg) 281 | }; 282 | 283 | if (typeof cb === 'function') { 284 | const cbName = 'CALLBACK' + window.callbackId++; 285 | window[cbName] = cb; 286 | args['cbName'] = cbName; 287 | } 288 | 289 | if (window._jsbridge) { 290 | window._jsbridge.call(method, JSON.stringify(args)); 291 | } 292 | } 293 | ``` 294 | 295 | #### 注入 JS 上下文的问题 296 | 297 | 以安卓 webview 的 addJavascriptInterface 为例,在安卓 4.2 版本之前,js 可以利用 java 的反射 Reflection API,取得构造该实例对象的类的內部信息,并能直接操作该对象的内部属性及方法,这种方式会造成安全隐患,例如如果加载了外部网页,该网页的恶意 js 脚本可以获取手机的存储卡上的信息。 298 | 299 | 在安卓 4.2 版本后,可以通过在提供给 js 调用的 java 方法前加装饰器 @JavascriptInterface,来表明仅该方法可以被 js 调用。 300 | 301 | #### 上述三种 js 调用 native 的方式对比如下表: 302 | 303 | | 方式 | 优点 | 缺点 | 304 | | ------------------------- | ------------------ | ------------------------------------------------------- | 305 | | 拦截 Url Schema(假请求) | 无安全漏洞 | 1. 连续发送时消息丢失 2. Url 长度限制,传输数据大小受限 | 306 | | 拦截 prompt alert confirm | 无安全漏洞 | iOS 的 UIWebView 不支持该方式 | 307 | | 注入 JS 上下文 | 官方提供,方便简捷 | 在安卓 4.2 以下有安全漏洞 | 308 | 309 | ## 3. 安卓端 java 的封装 310 | 311 | native 与 h5 交互部分的代码在上面已经提到了,这里主要是讲述 native 端如何封装暴露给 h5 的方法。 312 | 313 | 首先单独封装一个类 NativeMethods,将供 h5 调用的方法以公有且静态方法的形式写入。如下: 314 | 315 | ```java 316 | public class NativeMethods { 317 | public static void showToast(WebView view, JSONObject arg, CallBack callBack) { 318 | ... 319 | } 320 | } 321 | ``` 322 | 323 | 接下来考虑如何在 NativeMethods 和 h5 之前建立一个桥梁,JSBridge 类因运而生。 324 | JSBridge 类下主要有两个静态方法 register 和 call。其中 register 方法是用来将供 h5 调用的方法转化成 Map 形式,以便查询。而 call 方法主要是用接收 h5 端的调用,分解 h5 端传来的参数,查找并调用 Map 中的对应的 Native 方法。 325 | 326 | #### JSBridge 类的静态方法 register 327 | 328 | 首先在 JSBridge 类下声明一个静态属性 exposeMethods,数据类型为 HashMap 。然后声明静态方法 register,参数有字符串 exposeName 和类 classz,将 exposeName 和 classz 的所有静态方法 组合成一个 map,例如: 329 | 330 | ```java 331 | { 332 | jsbridge: { 333 | showToast: ... 334 | openScan: ... 335 | } 336 | } 337 | ``` 338 | 339 | 代码如下: 340 | 341 | ```java 342 | private static Map> exposeMethods = new HashMap<>(); 343 | 344 | public static void register(String exposeName, Class classz) { 345 | if (!exposeMethods.containsKey(exposeName)) { 346 | exposeMethods.put(exposeName, getAllMethod(classz)); 347 | } 348 | } 349 | ``` 350 | 351 | 由上可知我们需要定义一个 getAllMethod 方法用来将类里的方法转化为 HashMap 数据格式。在该方法里同样声明一个 HashMap,并将满足条件的方法转化成 Map,key 为方法名,value 为方法。 352 | 353 | 其中条件为 公有 public 静态 static 方法且第一个参数为 Webview 类的实例,第二个参数为 JSONObject 类的实例,第三个参数为 CallBack 类的实例。 (CallBack 是自定义的类,后面会讲到) 354 | 代码如下: 355 | 356 | ```java 357 | private static HashMap getAllMethod(Class injectedCls) { 358 | HashMap methodHashMap = new HashMap<>(); 359 | 360 | Method[] methods = injectedCls.getDeclaredMethods(); 361 | 362 | for (Method method: methods) { 363 | if(method.getModifiers()!=(Modifier.PUBLIC | Modifier.STATIC) || method.getName()==null) { 364 | continue; 365 | } 366 | Class[] parameters = method.getParameterTypes(); 367 | if (parameters!=null && parameters.length==3) { 368 | if (parameters[0] == WebView.class && parameters[1] == JSONObject.class && parameters[2] == CallBack.class) { 369 | methodHashMap.put(method.getName(), method); 370 | } 371 | } 372 | } 373 | 374 | return methodHashMap; 375 | } 376 | ``` 377 | 378 | #### JSBridge 类的静态方法 call 379 | 380 | 由于注入 JS 上下文和两外两种,h5 端传过来的参数形式不同,所以处理参数的方式略有不同。 381 | 下面以拦截 Prompt 的方式为例进行讲解,在该方式中 call 接收的第一个参数为 webView,第二个参数是 arg,即 h5 端传过来的参数。还记得拦截 Prompt 方式时 native 端和 h5 端约定的传输数据的方式么? 382 | 383 | ```javascript 384 | "jsbridge://openScan?{"data": {"scanType": "qrCode"}, "cbName":"handleScanResult"}" 385 | ``` 386 | 387 | call 方法首先会判断字符串是否以 jsbridge 开头(native 端和 h5 端之间约定的传输数据的协议名),然后该字符串转成 Uri 格式,然后获取其中的 host 名,即方法名,获取 query,即方法参数和 js 回调函数名组合的对象。最后查找 exposeMethods 的映射,找到对应的方法并执行该方法。 388 | 389 | ```java 390 | public static String call(WebView webView, String urlString) { 391 | 392 | if (!urlString.equals("") && urlString!=null && urlString.startsWith("jsbridge")) { 393 | Uri uri = Uri.parse(urlString); 394 | 395 | String methodName = uri.getHost(); 396 | 397 | try { 398 | JSONObject args = new JSONObject(uri.getQuery()); 399 | JSONObject arg = new JSONObject(args.getString("data")); 400 | String cbName = args.getString("cbName"); 401 | 402 | 403 | if (exposeMethods.containsKey("JSBridge")) { 404 | HashMap methodHashMap = exposeMethods.get("JSBridge"); 405 | 406 | if (methodHashMap!=null && methodHashMap.size()!=0 && methodHashMap.containsKey(methodName)) { 407 | Method method = methodHashMap.get(methodName); 408 | 409 | if (method!=null) { 410 | method.invoke(null, webView, arg, new CallBack(webView, cbName)); 411 | } 412 | } 413 | } 414 | } catch (Exception e) { 415 | e.printStackTrace(); 416 | } 417 | 418 | } 419 | return null; 420 | } 421 | ``` 422 | 423 | #### CallBack 类 424 | 425 | js 调用 native 方法成功后,native 有必要返回给 js 一些反馈,例如接口是否调用成功,或者 native 执行后的得到的数据(例如扫码)。所以 native 需要执行 js 回调函数。 426 | 427 | 执行 js 回调函数方式本质是 native 调用 h5 的 js 方法,方式仍旧是上面提到的两种方式 evaluateJavascript 和 loadUrl。简单来说可以直接将 js 的回调函数名传给对应的 native 方法,native 执行通过 evaluateJavascript 调用。 428 | 429 | 但为了统一封装调用回调的方式,我们可以定义一个 CallBack 类,在其中定义一个名为 apply 的静态方法,该方法直接调用 js 回调。 430 | 431 | 注意:native 执行 js 方法需要在主线程上。 432 | 433 | ```java 434 | public class CallBack { 435 | private String cbName; 436 | private WebView mWebView; 437 | 438 | public CallBack(WebView webView, String cbName) { 439 | this.cbName = cbName; 440 | this.mWebView = webView; 441 | } 442 | 443 | public void apply(JSONObject jsonObject) { 444 | if (mWebView!=null) { 445 | mWebView.post(() -> { 446 | mWebView.evaluateJavascript("javascript:" + cbName + "(" + jsonObject.toString() + ")", new ValueCallback() { 447 | @Override 448 | public void onReceiveValue(String value) { 449 | return; 450 | } 451 | }); 452 | }); 453 | } 454 | } 455 | } 456 | ``` 457 | 458 | 到此为止,JSBridge 的大致原理都讲完了。但功能仍可再加完善,例如: 459 | 460 | native 执行 js 方法时,可接受 js 方法中异步返回的数据,比如在 js 方法中请求某个接口在返回数据。直接调用 webview 提供的 evaluateJavascript,在第二个参数的类 ValueCallback 的实例方法 onReceiveValue 并不能接收到 js 异步返回的数据。 461 | 462 | 后面有空 native 调用 js 方式会继续完善的,最后以一句古语互勉: 463 | 464 | 路漫漫其修远兮 吾将上下而求索 465 | -------------------------------------------------------------------------------- /UrlSchema/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | -------------------------------------------------------------------------------- /UrlSchema/.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /UrlSchema/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /UrlSchema/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /UrlSchema/.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /UrlSchema/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /UrlSchema/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /UrlSchema/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.example.urlschema" 7 | minSdkVersion 19 8 | targetSdkVersion 28 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-optimize.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility JavaVersion.VERSION_1_8 21 | targetCompatibility JavaVersion.VERSION_1_8 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(include: ['*.jar'], dir: 'libs') 27 | implementation 'com.android.support:appcompat-v7:28.0.0' 28 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 29 | testImplementation 'junit:junit:4.12' 30 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 31 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 32 | } 33 | -------------------------------------------------------------------------------- /UrlSchema/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 | -------------------------------------------------------------------------------- /UrlSchema/app/src/androidTest/java/com/example/urlschema/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.example.urlschema; 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() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.example.urlschema", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/assets/JSBridge.js: -------------------------------------------------------------------------------- 1 | window.callbackId = 0; 2 | 3 | var bridge = { 4 | call: function(method, arg, cb) { 5 | var args = { 6 | data: arg === undefined ? null : JSON.stringify(arg), 7 | }; 8 | 9 | if (typeof cb === 'function') { 10 | var cbName = 'CALLBACK' + window.callbackId++; 11 | window[cbName] = cb; 12 | args['cbName'] = cbName; 13 | } 14 | 15 | var url = 'jsbridge://' + method + '?' + JSON.stringify(args); 16 | 17 | var iframe = document.createElement('iframe'); 18 | iframe.src = url; 19 | iframe.style.width = 0; 20 | iframe.style.height = 0; 21 | document.body.appendChild(iframe); 22 | 23 | window.setTimeout(function() { 24 | document.body.removeChild(iframe); 25 | }, 800); 26 | } 27 | }; 28 | 29 | module.exports = bridge; 30 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/assets/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/java/com/example/urlschema/CallBack.java: -------------------------------------------------------------------------------- 1 | package com.example.urlschema; 2 | 3 | import android.webkit.ValueCallback; 4 | import android.webkit.WebView; 5 | 6 | import org.json.JSONObject; 7 | 8 | public class CallBack { 9 | private String cbName; 10 | private WebView mWebView; 11 | 12 | public CallBack(WebView webView, String cbName) { 13 | this.cbName = cbName; 14 | this.mWebView = webView; 15 | } 16 | 17 | public void apply(JSONObject jsonObject) { 18 | if (mWebView != null) { 19 | mWebView.post(() -> { 20 | mWebView.evaluateJavascript("javascript:" + cbName + "(" + jsonObject.toString() + ")", new ValueCallback() { 21 | @Override 22 | public void onReceiveValue(String value) { 23 | return; 24 | } 25 | }); 26 | }); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/java/com/example/urlschema/JSBridge.java: -------------------------------------------------------------------------------- 1 | package com.example.urlschema; 2 | 3 | import android.net.Uri; 4 | import android.util.Log; 5 | import android.webkit.WebView; 6 | 7 | import org.json.JSONObject; 8 | 9 | import java.lang.reflect.Method; 10 | import java.lang.reflect.Modifier; 11 | import java.util.HashMap; 12 | import java.util.Map; 13 | 14 | public class JSBridge { 15 | 16 | private static Map> exposeMethods = new HashMap<>(); 17 | 18 | public static void register(String exposeName, Class classz) { 19 | if (!exposeMethods.containsKey(exposeName)) { 20 | exposeMethods.put(exposeName, getAllMethod(classz)); 21 | } 22 | } 23 | 24 | private static HashMap getAllMethod(Class injectedCls) { 25 | HashMap methodHashMap = new HashMap<>(); 26 | 27 | Method[] methods = injectedCls.getDeclaredMethods(); 28 | 29 | for (Method method: methods) { 30 | if(method.getModifiers()!=(Modifier.PUBLIC | Modifier.STATIC) || method.getName()==null) { 31 | continue; 32 | } 33 | Class[] parameters = method.getParameterTypes(); 34 | if (parameters!=null && parameters.length==3) { 35 | if (parameters[0] == WebView.class && parameters[1] == JSONObject.class && parameters[2] == CallBack.class) { 36 | methodHashMap.put(method.getName(), method); 37 | } 38 | } 39 | } 40 | 41 | return methodHashMap; 42 | } 43 | 44 | 45 | public static String call(WebView webView, String urlString) { 46 | if (!urlString.equals("") && urlString!=null && urlString.startsWith("jsbridge")) { 47 | 48 | Uri uri = Uri.parse(urlString); 49 | 50 | String methodName = uri.getHost(); 51 | 52 | try { 53 | JSONObject args = new JSONObject(uri.getQuery()); 54 | JSONObject arg = new JSONObject(args.getString("data")); 55 | String cbName = args.getString("cbName"); 56 | 57 | 58 | if (exposeMethods.containsKey("JSBridge")) { 59 | HashMap methodHashMap = exposeMethods.get("JSBridge"); 60 | 61 | if (methodHashMap!=null && methodHashMap.size()!=0 && methodHashMap.containsKey(methodName)) { 62 | Method method = methodHashMap.get(methodName); 63 | 64 | if (method!=null) { 65 | method.invoke(null, webView, arg, new CallBack(webView, cbName)); 66 | } 67 | } 68 | } 69 | } catch (Exception e) { 70 | e.printStackTrace(); 71 | } 72 | 73 | } 74 | return null; 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/java/com/example/urlschema/JSBridgeViewClient.java: -------------------------------------------------------------------------------- 1 | package com.example.urlschema; 2 | 3 | import android.util.Log; 4 | import android.webkit.WebView; 5 | import android.webkit.WebViewClient; 6 | 7 | public class JSBridgeViewClient extends WebViewClient { 8 | @Override 9 | public boolean shouldOverrideUrlLoading(WebView view, String url) { 10 | JSBridge.call(view, url); 11 | return true; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/java/com/example/urlschema/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.urlschema; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.KeyEvent; 6 | import android.webkit.WebChromeClient; 7 | import android.webkit.WebView; 8 | 9 | public class MainActivity extends AppCompatActivity { 10 | 11 | private WebView mWebView; 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | 18 | mWebView = (WebView) findViewById(R.id.mWebView); 19 | 20 | // 设置 webViewClient 类 21 | mWebView.setWebViewClient(new JSBridgeViewClient()); 22 | 23 | // 设置 webChromeClient 类 24 | mWebView.setWebChromeClient(new WebChromeClient()); 25 | 26 | // 设置支持调用 JS 27 | mWebView.getSettings().setJavaScriptEnabled(true); 28 | 29 | mWebView.loadUrl("file:///android_asset/index.html"); 30 | 31 | JSBridge.register("JSBridge", NativeMethods.class); 32 | } 33 | 34 | // 通过拦截 onKeyDown 事件实现网页回退 35 | @Override 36 | public boolean onKeyDown(int keyCode, KeyEvent event) { 37 | if(keyCode == event.KEYCODE_BACK) { 38 | if (mWebView.canGoBack()) { 39 | mWebView.goBack(); 40 | return true; 41 | } 42 | } 43 | 44 | return super.onKeyDown(keyCode, event); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/java/com/example/urlschema/NativeMethods.java: -------------------------------------------------------------------------------- 1 | package com.example.urlschema; 2 | 3 | import android.webkit.WebView; 4 | import android.widget.Toast; 5 | 6 | import org.json.JSONObject; 7 | 8 | public class NativeMethods { 9 | public static void showToast(WebView view, JSONObject arg, CallBack callBack) { 10 | String message = arg.optString("msg"); 11 | 12 | Toast.makeText(view.getContext(), message, Toast.LENGTH_SHORT).show(); 13 | 14 | if (callBack !=null) { 15 | try { 16 | JSONObject result = new JSONObject(); 17 | result.put("msg", "js 调用 native 成功!"); 18 | callBack.apply(result); 19 | } catch (Exception e) { 20 | e.printStackTrace(); 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /UrlSchema/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 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 14 | 15 | 16 | 21 | 22 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #008577 4 | #00574B 5 | #D81B60 6 | 7 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | UrlSchema 3 | 4 | -------------------------------------------------------------------------------- /UrlSchema/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /UrlSchema/app/src/test/java/com/example/urlschema/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.example.urlschema; 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() { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /UrlSchema/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | google() 6 | jcenter() 7 | 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.3.2' 11 | 12 | // NOTE: Do not place your application dependencies here; they belong 13 | // in the individual module build.gradle files 14 | } 15 | } 16 | 17 | allprojects { 18 | repositories { 19 | google() 20 | jcenter() 21 | 22 | } 23 | } 24 | 25 | task clean(type: Delete) { 26 | delete rootProject.buildDir 27 | } 28 | -------------------------------------------------------------------------------- /UrlSchema/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | 15 | 16 | -------------------------------------------------------------------------------- /UrlSchema/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/UrlSchema/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /UrlSchema/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Mar 19 14:00:24 CST 2019 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.10.1-all.zip 7 | -------------------------------------------------------------------------------- /UrlSchema/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Attempt to set APP_HOME 10 | # Resolve links: $0 may be a link 11 | PRG="$0" 12 | # Need this for relative symlinks. 13 | while [ -h "$PRG" ] ; do 14 | ls=`ls -ld "$PRG"` 15 | link=`expr "$ls" : '.*-> \(.*\)$'` 16 | if expr "$link" : '/.*' > /dev/null; then 17 | PRG="$link" 18 | else 19 | PRG=`dirname "$PRG"`"/$link" 20 | fi 21 | done 22 | SAVED="`pwd`" 23 | cd "`dirname \"$PRG\"`/" >/dev/null 24 | APP_HOME="`pwd -P`" 25 | cd "$SAVED" >/dev/null 26 | 27 | APP_NAME="Gradle" 28 | APP_BASE_NAME=`basename "$0"` 29 | 30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 31 | DEFAULT_JVM_OPTS="" 32 | 33 | # Use the maximum available, or set MAX_FD != -1 to use that value. 34 | MAX_FD="maximum" 35 | 36 | warn () { 37 | echo "$*" 38 | } 39 | 40 | die () { 41 | echo 42 | echo "$*" 43 | echo 44 | exit 1 45 | } 46 | 47 | # OS specific support (must be 'true' or 'false'). 48 | cygwin=false 49 | msys=false 50 | darwin=false 51 | nonstop=false 52 | case "`uname`" in 53 | CYGWIN* ) 54 | cygwin=true 55 | ;; 56 | Darwin* ) 57 | darwin=true 58 | ;; 59 | MINGW* ) 60 | msys=true 61 | ;; 62 | NONSTOP* ) 63 | nonstop=true 64 | ;; 65 | esac 66 | 67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 68 | 69 | # Determine the Java command to use to start the JVM. 70 | if [ -n "$JAVA_HOME" ] ; then 71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 72 | # IBM's JDK on AIX uses strange locations for the executables 73 | JAVACMD="$JAVA_HOME/jre/sh/java" 74 | else 75 | JAVACMD="$JAVA_HOME/bin/java" 76 | fi 77 | if [ ! -x "$JAVACMD" ] ; then 78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 79 | 80 | Please set the JAVA_HOME variable in your environment to match the 81 | location of your Java installation." 82 | fi 83 | else 84 | JAVACMD="java" 85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 86 | 87 | Please set the JAVA_HOME variable in your environment to match the 88 | location of your Java installation." 89 | fi 90 | 91 | # Increase the maximum file descriptors if we can. 92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 93 | MAX_FD_LIMIT=`ulimit -H -n` 94 | if [ $? -eq 0 ] ; then 95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 96 | MAX_FD="$MAX_FD_LIMIT" 97 | fi 98 | ulimit -n $MAX_FD 99 | if [ $? -ne 0 ] ; then 100 | warn "Could not set maximum file descriptor limit: $MAX_FD" 101 | fi 102 | else 103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 104 | fi 105 | fi 106 | 107 | # For Darwin, add options to specify how the application appears in the dock 108 | if $darwin; then 109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 110 | fi 111 | 112 | # For Cygwin, switch paths to Windows format before running java 113 | if $cygwin ; then 114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 116 | JAVACMD=`cygpath --unix "$JAVACMD"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Escape application args 158 | save () { 159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 160 | echo " " 161 | } 162 | APP_ARGS=$(save "$@") 163 | 164 | # Collect all arguments for the java command, following the shell quoting and substitution rules 165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 166 | 167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong 168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then 169 | cd "$(dirname "$0")" 170 | fi 171 | 172 | exec "$JAVACMD" "$@" 173 | -------------------------------------------------------------------------------- /UrlSchema/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 | set DIRNAME=%~dp0 12 | if "%DIRNAME%" == "" set DIRNAME=. 13 | set APP_BASE_NAME=%~n0 14 | set APP_HOME=%DIRNAME% 15 | 16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 17 | set DEFAULT_JVM_OPTS= 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 Windows variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | 53 | :win9xME_args 54 | @rem Slurp the command line arguments. 55 | set CMD_LINE_ARGS= 56 | set _SKIP=2 57 | 58 | :win9xME_args_slurp 59 | if "x%~1" == "x" goto execute 60 | 61 | set CMD_LINE_ARGS=%* 62 | 63 | :execute 64 | @rem Setup the command line 65 | 66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 67 | 68 | @rem Execute Gradle 69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 70 | 71 | :end 72 | @rem End local scope for the variables with windows NT shell 73 | if "%ERRORLEVEL%"=="0" goto mainEnd 74 | 75 | :fail 76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 77 | rem the _cmd.exe /c_ return code! 78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 79 | exit /b 1 80 | 81 | :mainEnd 82 | if "%OS%"=="Windows_NT" endlocal 83 | 84 | :omega 85 | -------------------------------------------------------------------------------- /UrlSchema/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /assets/addJavascriptInterface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/assets/addJavascriptInterface.png -------------------------------------------------------------------------------- /assets/evaluateJavascript.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/assets/evaluateJavascript.png -------------------------------------------------------------------------------- /assets/intercept-prompt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/assets/intercept-prompt.png -------------------------------------------------------------------------------- /assets/intercept-url.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/assets/intercept-url.png -------------------------------------------------------------------------------- /assets/loadUrl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mcuking/JSBridge/172e01add8b053e320c1f3f884391221726bbbc9/assets/loadUrl.png --------------------------------------------------------------------------------