├── .gitignore ├── .idea ├── gradle.xml ├── misc.xml └── vcs.xml ├── .travis.yml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── wendu │ │ └── jsbdemo │ │ ├── CallJavascriptActivityTest.java │ │ └── ExampleInstrumentedTest.java │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── dsbridge.js │ ├── fly.html │ ├── js-call-native.html │ └── native-call-js.html │ ├── java │ └── wendu │ │ └── jsbdemo │ │ ├── AjaxHandler.java │ │ ├── CallJavascriptActivity.java │ │ ├── JavascriptCallNativeActivity.java │ │ ├── JsApi.java │ │ ├── JsEchoApi.java │ │ ├── MainActivity.java │ │ ├── NetUtils.java │ │ └── WrokWithFlyioTestActivity.java │ └── res │ ├── layout │ ├── activity_call_javascript.xml │ ├── activity_js_call_native.xml │ ├── activity_main.xml │ └── activity_wrok_with_flyio_test.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── changelist.md ├── dsbridge ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── libs │ └── tbs_sdk_thirdapp_v4.3.0.316_44216_sharewithdownloadwithfile_withoutGame_obfs_20220728_101601.jar │ └── main │ ├── AndroidManifest.xml │ └── java │ └── wendu │ └── dsbridge │ ├── CompletionHandler.java │ ├── DWebView.java │ └── OnReturnValue.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── readme-chs.md ├── readme.md └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 23 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 45 | 50 | 51 | 52 | 53 | 54 | 55 | 1.8 56 | 57 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | jdk: oraclejdk8 3 | android: 4 | components: 5 | - platform-tools 6 | - tools 7 | 8 | # The BuildTools version used by your project 9 | - build-tools-25.0.0 10 | 11 | 12 | # The SDK version used to compile your project 13 | - android-23 14 | 15 | # Support library 16 | - extra-android-support 17 | - extra-android-m2repository 18 | - sys-img-x86_64-android-22 19 | licenses: 20 | - 'android-sdk-preview-license-52d11cd2' 21 | - 'android-sdk-license-.+' 22 | - 'google-gdk-license-.+' 23 | 24 | script: 25 | - ./gradlew build 26 | 27 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 31 5 | buildToolsVersion '28.0.3' 6 | 7 | defaultConfig { 8 | applicationId "wendu.jsbdemo" 9 | minSdkVersion 23 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | debug { 21 | testCoverageEnabled true 22 | } 23 | } 24 | 25 | lintOptions { 26 | abortOnError false 27 | } 28 | 29 | testOptions { 30 | unitTests.returnDefaultValues = true 31 | } 32 | } 33 | 34 | dependencies { 35 | compile fileTree(dir: 'libs', include: ['*.jar']) 36 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 37 | exclude group: 'com.android.support', module: 'support-annotations' 38 | }) 39 | compile project(path: ':dsbridge') 40 | compile 'com.android.support:appcompat-v7:23.4.0' 41 | compile 'com.squareup.okhttp3:okhttp:3.3.0' 42 | compile 'com.android.support.constraint:constraint-layout:1.0.2' 43 | testCompile 'junit:junit:4.12' 44 | } 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/du/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/wendu/jsbdemo/CallJavascriptActivityTest.java: -------------------------------------------------------------------------------- 1 | package wendu.jsbdemo; 2 | 3 | import android.os.SystemClock; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | import android.test.ActivityInstrumentationTestCase2; 7 | import android.view.View; 8 | 9 | import org.junit.After; 10 | import org.junit.Before; 11 | import org.junit.Test; 12 | import org.junit.runner.RunWith; 13 | 14 | import wendu.dsbridge.DWebView; 15 | import wendu.dsbridge.OnReturnValue; 16 | 17 | /** 18 | * Created by du on 2018/2/5. 19 | */ 20 | @RunWith (AndroidJUnit4.class) 21 | public class CallJavascriptActivityTest extends ActivityInstrumentationTestCase2 { 22 | 23 | private DWebView dWebView; 24 | 25 | public CallJavascriptActivityTest() { 26 | super(CallJavascriptActivity.class); 27 | } 28 | 29 | @Override 30 | @Before 31 | public void setUp() throws Exception { 32 | super.setUp(); 33 | // Injecting the Instrumentation instance is required 34 | // for your test to run with AndroidJUnitRunner. 35 | injectInstrumentation(InstrumentationRegistry.getInstrumentation()); 36 | CallJavascriptActivity activity=getActivity(); 37 | dWebView= (DWebView) getActivity().findViewById(R.id.webview); 38 | //dWebView.loadUrl("file:///android_asset/js-call-native.html"); 39 | DWebView.setWebContentsDebuggingEnabled(true); 40 | } 41 | 42 | @After 43 | public void tearDown() throws Exception { 44 | 45 | } 46 | 47 | @Test 48 | public void onClick(View v) throws Exception { 49 | dWebView.callHandler("addValue", new Object[]{3, 4}, new OnReturnValue() { 50 | @Override 51 | public void onValue(Integer retValue) { 52 | assertEquals(7,retValue.intValue()); 53 | } 54 | }); 55 | SystemClock.sleep(5000); 56 | } 57 | 58 | } -------------------------------------------------------------------------------- /app/src/androidTest/java/wendu/jsbdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package wendu.jsbdemo; 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 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("wendu.jsbdemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/assets/dsbridge.js: -------------------------------------------------------------------------------- 1 | var bridge = { 2 | default:this,// for typescript 3 | call: function (method, args, cb) { 4 | var ret = ''; 5 | if (typeof args == 'function') { 6 | cb = args; 7 | args = {}; 8 | } 9 | var arg={data:args===undefined?null:args} 10 | if (typeof cb == 'function') { 11 | var cbName = 'dscb' + window.dscb++; 12 | window[cbName] = cb; 13 | arg['_dscbstub'] = cbName; 14 | } 15 | arg = JSON.stringify(arg) 16 | 17 | //if in webview that dsBridge provided, call! 18 | if(window._dsbridge){ 19 | ret= _dsbridge.call(method, arg) 20 | }else if(window._dswk||navigator.userAgent.indexOf("_dsbridge")!=-1){ 21 | ret = prompt("_dsbridge=" + method, arg); 22 | } 23 | 24 | return JSON.parse(ret||'{}').data 25 | }, 26 | register: function (name, fun, asyn) { 27 | var q = asyn ? window._dsaf : window._dsf 28 | if (!window._dsInit) { 29 | window._dsInit = true; 30 | //notify native that js apis register successfully on next event loop 31 | setTimeout(function () { 32 | bridge.call("_dsb.dsinit"); 33 | }, 0) 34 | } 35 | if (typeof fun == "object") { 36 | q._obs[name] = fun; 37 | } else { 38 | q[name] = fun 39 | } 40 | }, 41 | registerAsyn: function (name, fun) { 42 | this.register(name, fun, true); 43 | }, 44 | hasNativeMethod: function (name, type) { 45 | return this.call("_dsb.hasNativeMethod", {name: name, type:type||"all"}); 46 | }, 47 | disableJavascriptDialogBlock: function (disable) { 48 | this.call("_dsb.disableJavascriptDialogBlock", { 49 | disable: disable !== false 50 | }) 51 | } 52 | }; 53 | 54 | !function () { 55 | if (window._dsf) return; 56 | var ob = { 57 | _dsf: { 58 | _obs: {} 59 | }, 60 | _dsaf: { 61 | _obs: {} 62 | }, 63 | dscb: 0, 64 | dsBridge: bridge, 65 | close: function () { 66 | bridge.call("_dsb.closePage") 67 | }, 68 | _handleMessageFromNative: function (info) { 69 | var arg = JSON.parse(info.data); 70 | var ret = { 71 | id: info.callbackId, 72 | complete: true 73 | } 74 | var f = this._dsf[info.method]; 75 | var af = this._dsaf[info.method] 76 | var callSyn = function (f, ob) { 77 | ret.data = f.apply(ob, arg) 78 | bridge.call("_dsb.returnValue", ret) 79 | } 80 | var callAsyn = function (f, ob) { 81 | arg.push(function (data, complete) { 82 | ret.data = data; 83 | ret.complete = complete!==false; 84 | bridge.call("_dsb.returnValue", ret) 85 | }) 86 | f.apply(ob, arg) 87 | } 88 | if (f) { 89 | callSyn(f, this._dsf); 90 | } else if (af) { 91 | callAsyn(af, this._dsaf); 92 | } else { 93 | //with namespace 94 | var name = info.method.split('.'); 95 | if (name.length<2) return; 96 | var method=name.pop(); 97 | var namespace=name.join('.') 98 | var obs = this._dsf._obs; 99 | var ob = obs[namespace] || {}; 100 | var m = ob[method]; 101 | if (m && typeof m == "function") { 102 | callSyn(m, ob); 103 | return; 104 | } 105 | obs = this._dsaf._obs; 106 | ob = obs[namespace] || {}; 107 | m = ob[method]; 108 | if (m && typeof m == "function") { 109 | callAsyn(m, ob); 110 | return; 111 | } 112 | } 113 | } 114 | } 115 | for (var attr in ob) { 116 | window[attr] = ob[attr] 117 | } 118 | bridge.register("_hasJavascriptMethod", function (method, tag) { 119 | var name = method.split('.') 120 | if(name.length<2) { 121 | return !!(_dsf[name]||_dsaf[name]) 122 | }else{ 123 | // with namespace 124 | var method=name.pop() 125 | var namespace=name.join('.') 126 | var ob=_dsf._obs[namespace]||_dsaf._obs[namespace] 127 | return ob&&!!ob[method] 128 | } 129 | }) 130 | }(); 131 | 132 | module.exports = bridge; -------------------------------------------------------------------------------- /app/src/main/assets/fly.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 38 | 39 |
40 | Fly.js supports forwarding the http request (ajax) to Native through any Javascript bridge, And fly.js has already provide the dsBridge adapter. Because the Native side has no the same-origin policy restriction, fly.js can request any resource from any domain. 41 |
42 |
Get home page source code of baidu.com
43 |
Show the logo of github
44 |
45 |
Close window
46 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /app/src/main/assets/js-call-native.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DSBridge Test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 30 | 31 |
Synchronous call
32 |
Asynchronous call
33 |
Sync call without argument
34 |
Async call without argument
35 |
echo.syn
36 |
echo.asyn
37 |
Stress test,2K times consecutive asynchronous API calls
38 |
Never call because without @JavascriptInterface annotation
( This test is 39 | just for Android ,should be ignored in IOS ) 40 |
41 |
call progress
42 |
hasNativeMethod("xx")
43 |
hasNativeMethod("testSyn")
44 | 45 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /app/src/main/assets/native-call-js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | DSBridge Test 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /app/src/main/java/wendu/jsbdemo/AjaxHandler.java: -------------------------------------------------------------------------------- 1 | package wendu.jsbdemo; 2 | 3 | import android.util.Base64; 4 | 5 | import org.json.JSONObject; 6 | 7 | import java.io.IOException; 8 | import java.util.HashMap; 9 | import java.util.Iterator; 10 | import java.util.List; 11 | import java.util.Map; 12 | import java.util.concurrent.TimeUnit; 13 | 14 | import okhttp3.Call; 15 | import okhttp3.Callback; 16 | import okhttp3.MediaType; 17 | import okhttp3.OkHttpClient; 18 | import okhttp3.Request; 19 | import okhttp3.RequestBody; 20 | import okhttp3.Response; 21 | import wendu.dsbridge.CompletionHandler; 22 | 23 | /** 24 | * Created by du on 2017/10/31. 25 | * 26 | * This class handles the Ajax requests forwarded by fly.js in DWebView 27 | * More about fly.js see https://github.com/wendux/fly 28 | */ 29 | 30 | public class AjaxHandler { 31 | public static void onAjaxRequest(final JSONObject requestData, final CompletionHandler handler){ 32 | 33 | // Define response structure 34 | final Map responseData=new HashMap<>(); 35 | responseData.put("statusCode",0); 36 | 37 | try { 38 | int timeout =requestData.getInt("timeout"); 39 | // Create a okhttp instance and set timeout 40 | final OkHttpClient okHttpClient = new OkHttpClient 41 | .Builder() 42 | .connectTimeout(timeout, TimeUnit.MILLISECONDS) 43 | .build(); 44 | 45 | // Determine whether you need to encode the response result. 46 | // And encode when responseType is stream. 47 | String contentType=""; 48 | boolean encode=false; 49 | String responseType=requestData.optString("responseType",null); 50 | if(responseType!=null&&responseType.equals("stream")){ 51 | encode=true; 52 | } 53 | 54 | Request.Builder rb= new Request.Builder(); 55 | rb.url(requestData.getString("url")); 56 | JSONObject headers=requestData.getJSONObject("headers"); 57 | 58 | // Set request headers 59 | Iterator iterator = headers.keys(); 60 | while(iterator.hasNext()){ 61 | String key = (String) iterator.next(); 62 | String value = headers.getString(key); 63 | String lKey=key.toLowerCase(); 64 | if(lKey.equals("cookie")){ 65 | // Here you can use CookieJar to manage cookie in a unified way with you native code. 66 | continue; 67 | } 68 | if(lKey.toLowerCase().equals("content-type")){ 69 | contentType=value; 70 | } 71 | rb.header(key,value); 72 | } 73 | 74 | // Create request body 75 | if(requestData.getString("method").equals("POST")){ 76 | RequestBody requestBody=RequestBody 77 | .create(MediaType.parse(contentType),requestData.getString("data")); 78 | rb.post(requestBody) ; 79 | } 80 | // Create and send HTTP requests 81 | Call call=okHttpClient.newCall(rb.build()); 82 | final boolean finalEncode = encode; 83 | call.enqueue(new Callback() { 84 | @Override 85 | public void onFailure(Call call, IOException e) { 86 | responseData.put("responseText",e.getMessage()); 87 | handler.complete(new JSONObject(responseData).toString()); 88 | } 89 | 90 | @Override 91 | public void onResponse(Call call, Response response) throws IOException { 92 | String data; 93 | // If encoding is needed, the result is encoded by Base64 and returned 94 | if(finalEncode){ 95 | data= Base64.encodeToString(response.body().bytes(),Base64.DEFAULT); 96 | }else{ 97 | data=response.body().string(); 98 | } 99 | responseData.put("responseText",data); 100 | responseData.put("statusCode",response.code()); 101 | responseData.put("statusMessage",response.message()); 102 | Map> responseHeaders= response.headers().toMultimap(); 103 | responseHeaders.remove(null); 104 | responseData.put("headers",responseHeaders); 105 | handler.complete(new JSONObject(responseData).toString()); 106 | } 107 | }); 108 | 109 | }catch (Exception e){ 110 | responseData.put("responseText",e.getMessage()); 111 | handler.complete(new JSONObject(responseData).toString()); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /app/src/main/java/wendu/jsbdemo/CallJavascriptActivity.java: -------------------------------------------------------------------------------- 1 | package wendu.jsbdemo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.Toast; 7 | 8 | import org.json.JSONObject; 9 | 10 | import wendu.dsbridge.DWebView; 11 | import wendu.dsbridge.OnReturnValue; 12 | 13 | public class CallJavascriptActivity extends AppCompatActivity implements View.OnClickListener { 14 | 15 | DWebView dWebView; 16 | 17 | public T getView(int viewId) { 18 | View view = findViewById(viewId); 19 | return (T) view; 20 | } 21 | 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setContentView(R.layout.activity_call_javascript); 26 | getView(R.id.addValue).setOnClickListener(this); 27 | getView(R.id.append).setOnClickListener(this); 28 | getView(R.id.startTimer).setOnClickListener(this); 29 | getView(R.id.synAddValue).setOnClickListener(this); 30 | getView(R.id.synGetInfo).setOnClickListener(this); 31 | getView(R.id.asynAddValue).setOnClickListener(this); 32 | getView(R.id.asynGetInfo).setOnClickListener(this); 33 | getView(R.id.hasMethodAddValue).setOnClickListener(this); 34 | getView(R.id.hasMethodXX).setOnClickListener(this); 35 | getView(R.id.hasMethodAsynAddValue).setOnClickListener(this); 36 | getView(R.id.hasMethodAsynXX).setOnClickListener(this); 37 | DWebView.setWebContentsDebuggingEnabled(true); 38 | dWebView= getView(R.id.webview); 39 | dWebView.loadUrl("file:///android_asset/native-call-js.html"); 40 | 41 | 42 | 43 | } 44 | 45 | 46 | void showToast(Object o) { 47 | Toast.makeText(this, o.toString(), Toast.LENGTH_SHORT).show(); 48 | } 49 | 50 | @Override 51 | public void onClick(View v) { 52 | switch (v.getId()) { 53 | case R.id.addValue: 54 | dWebView.callHandler("addValue", new Object[]{3, 4}, new OnReturnValue() { 55 | @Override 56 | public void onValue(Integer retValue) { 57 | showToast(retValue); 58 | } 59 | }); 60 | break; 61 | case R.id.append: 62 | dWebView.callHandler("append", new Object[]{"I", "love", "you"}, new OnReturnValue() { 63 | @Override 64 | public void onValue(String retValue) { 65 | showToast(retValue); 66 | } 67 | }); 68 | break; 69 | case R.id.startTimer: 70 | dWebView.callHandler("startTimer", new OnReturnValue() { 71 | @Override 72 | public void onValue(Integer retValue) { 73 | showToast(retValue); 74 | } 75 | }); 76 | break; 77 | case R.id.synAddValue: 78 | dWebView.callHandler("syn.addValue", new Object[]{5, 6}, new OnReturnValue() { 79 | @Override 80 | public void onValue(Integer retValue) { 81 | showToast(retValue); 82 | } 83 | }); 84 | break; 85 | case R.id.synGetInfo: 86 | dWebView.callHandler("syn.getInfo", new OnReturnValue() { 87 | @Override 88 | public void onValue(JSONObject retValue) { 89 | showToast(retValue); 90 | } 91 | }); 92 | break; 93 | case R.id.asynAddValue: 94 | dWebView.callHandler("asyn.addValue", new Object[]{5, 6}, new OnReturnValue() { 95 | @Override 96 | public void onValue(Integer retValue) { 97 | showToast(retValue); 98 | } 99 | }); 100 | break; 101 | case R.id.asynGetInfo: 102 | dWebView.callHandler("asyn.getInfo", new OnReturnValue() { 103 | @Override 104 | public void onValue(JSONObject retValue) { 105 | showToast(retValue); 106 | } 107 | }); 108 | break; 109 | case R.id.hasMethodAddValue: 110 | dWebView.hasJavascriptMethod("addValue", new OnReturnValue() { 111 | @Override 112 | public void onValue(Boolean retValue) { 113 | showToast(retValue); 114 | } 115 | }); 116 | break; 117 | case R.id.hasMethodXX: 118 | dWebView.hasJavascriptMethod("XX", new OnReturnValue() { 119 | @Override 120 | public void onValue(Boolean retValue) { 121 | showToast(retValue); 122 | } 123 | }); 124 | break; 125 | case R.id.hasMethodAsynAddValue: 126 | dWebView.hasJavascriptMethod("asyn.addValue", new OnReturnValue() { 127 | @Override 128 | public void onValue(Boolean retValue) { 129 | showToast(retValue); 130 | } 131 | }); 132 | break; 133 | case R.id.hasMethodAsynXX: 134 | dWebView.hasJavascriptMethod("asyn.XX", new OnReturnValue() { 135 | @Override 136 | public void onValue(Boolean retValue) { 137 | showToast(retValue); 138 | } 139 | }); 140 | break; 141 | } 142 | 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /app/src/main/java/wendu/jsbdemo/JavascriptCallNativeActivity.java: -------------------------------------------------------------------------------- 1 | package wendu.jsbdemo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | 6 | import wendu.dsbridge.DWebView; 7 | 8 | public class JavascriptCallNativeActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_js_call_native); 14 | final DWebView dwebView= (DWebView) findViewById(R.id.webview); 15 | // set debug mode 16 | DWebView.setWebContentsDebuggingEnabled(true); 17 | dwebView.addJavascriptObject(new JsApi(), null); 18 | dwebView.addJavascriptObject(new JsEchoApi(),"echo"); 19 | dwebView.loadUrl("file:///android_asset/js-call-native.html"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/wendu/jsbdemo/JsApi.java: -------------------------------------------------------------------------------- 1 | package wendu.jsbdemo; 2 | 3 | import android.os.CountDownTimer; 4 | import android.webkit.JavascriptInterface; 5 | 6 | import org.json.JSONException; 7 | import org.json.JSONObject; 8 | 9 | import wendu.dsbridge.CompletionHandler; 10 | 11 | /** 12 | * Created by du on 16/12/31. 13 | */ 14 | 15 | public class JsApi{ 16 | @JavascriptInterface 17 | public String testSyn(Object msg) { 18 | return msg + "[syn call]"; 19 | } 20 | 21 | @JavascriptInterface 22 | public void testAsyn(Object msg, CompletionHandler handler){ 23 | handler.complete(msg+" [ asyn call]"); 24 | } 25 | 26 | @JavascriptInterface 27 | public String testNoArgSyn(Object arg) throws JSONException { 28 | return "testNoArgSyn called [ syn call]"; 29 | } 30 | 31 | @JavascriptInterface 32 | public void testNoArgAsyn(Object arg,CompletionHandler handler) { 33 | handler.complete( "testNoArgAsyn called [ asyn call]"); 34 | } 35 | 36 | 37 | //@JavascriptInterface 38 | //without @JavascriptInterface annotation can't be called 39 | public String testNever(Object arg) throws JSONException { 40 | JSONObject jsonObject= (JSONObject) arg; 41 | return jsonObject.getString("msg") + "[ never call]"; 42 | } 43 | 44 | @JavascriptInterface 45 | public void callProgress(Object args, final CompletionHandler handler) { 46 | 47 | new CountDownTimer(11000, 1000) { 48 | int i=10; 49 | @Override 50 | public void onTick(long millisUntilFinished) { 51 | //setProgressData can be called many times util complete be called. 52 | handler.setProgressData((i--)); 53 | 54 | } 55 | @Override 56 | public void onFinish() { 57 | //complete the js invocation with data; handler will be invalid when complete is called 58 | handler.complete(0); 59 | 60 | } 61 | }.start(); 62 | } 63 | } -------------------------------------------------------------------------------- /app/src/main/java/wendu/jsbdemo/JsEchoApi.java: -------------------------------------------------------------------------------- 1 | package wendu.jsbdemo; 2 | 3 | import android.webkit.JavascriptInterface; 4 | 5 | import org.json.JSONException; 6 | 7 | import wendu.dsbridge.CompletionHandler; 8 | 9 | /** 10 | * Created by du on 16/12/31. 11 | */ 12 | 13 | public class JsEchoApi { 14 | 15 | @JavascriptInterface 16 | public Object syn(Object args) throws JSONException { 17 | return args; 18 | } 19 | 20 | @JavascriptInterface 21 | public void asyn(Object args,CompletionHandler handler){ 22 | handler.complete(args); 23 | } 24 | } -------------------------------------------------------------------------------- /app/src/main/java/wendu/jsbdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package wendu.jsbdemo; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.util.Log; 7 | import android.view.View; 8 | 9 | import com.tencent.smtt.sdk.QbSdk; 10 | 11 | public class MainActivity extends AppCompatActivity { 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | 16 | super.onCreate(savedInstanceState); 17 | QbSdk.initX5Environment(this, new QbSdk.PreInitCallback() { 18 | @Override 19 | public void onCoreInitFinished() { 20 | Log.d("jsbridge","onCoreInitFinished"); 21 | } 22 | 23 | @Override 24 | public void onViewInitFinished(boolean b) { 25 | Log.d("jsbridge","onCoreInitFinished"); 26 | } 27 | }); 28 | setContentView(R.layout.activity_main); 29 | findViewById(R.id.callJs).setOnClickListener(new View.OnClickListener() { 30 | @Override 31 | public void onClick(View v) { 32 | startActivity(new Intent(MainActivity.this,CallJavascriptActivity.class)); 33 | } 34 | }); 35 | findViewById(R.id.callNative).setOnClickListener(new View.OnClickListener() { 36 | @Override 37 | public void onClick(View v) { 38 | startActivity(new Intent(MainActivity.this,JavascriptCallNativeActivity.class)); 39 | } 40 | }); 41 | findViewById(R.id.fly).setOnClickListener(new View.OnClickListener() { 42 | @Override 43 | public void onClick(View v) { 44 | startActivity(new Intent(MainActivity.this,WrokWithFlyioTestActivity.class)); 45 | } 46 | }); 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /app/src/main/java/wendu/jsbdemo/NetUtils.java: -------------------------------------------------------------------------------- 1 | package wendu.jsbdemo; 2 | 3 | import java.net.CookieManager; 4 | import org.json.JSONObject; 5 | import java.io.BufferedReader; 6 | import java.io.InputStream; 7 | import java.io.InputStreamReader; 8 | import java.io.PrintWriter; 9 | import java.net.HttpCookie; 10 | import java.net.HttpURLConnection; 11 | import java.net.URL; 12 | import java.util.HashMap; 13 | import java.util.Iterator; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | 18 | import javax.net.ssl.HostnameVerifier; 19 | import javax.net.ssl.HttpsURLConnection; 20 | import javax.net.ssl.SSLSession; 21 | 22 | /** 23 | * Created by du on 2017/9/16. 24 | */ 25 | 26 | public class NetUtils { 27 | public static CookieManager cookieManager=new CookieManager(); 28 | public static Map request(String method, String url, String param, JSONObject headers) throws Exception { 29 | URL uri = new URL(url); 30 | method = method.toUpperCase(); 31 | HttpURLConnection urlCon = (HttpURLConnection) uri.openConnection(); 32 | urlCon.setRequestMethod(method); 33 | urlCon.setConnectTimeout(10000); 34 | handleRequestHeaders(urlCon,headers); 35 | if (urlCon instanceof HttpsURLConnection) { 36 | addCertVerifier((HttpsURLConnection) urlCon); 37 | } 38 | if (method.equals("POST")) { 39 | urlCon.setDoOutput(true); 40 | urlCon.setDoInput(true); 41 | if (!param.trim().isEmpty()) { 42 | PrintWriter pw = new PrintWriter(urlCon.getOutputStream()); 43 | pw.print(param); 44 | pw.flush(); 45 | pw.close(); 46 | } 47 | } 48 | Map response=new HashMap<>(); 49 | response.put("responseText",inputStream2String(urlCon.getInputStream())); 50 | response.put("statusCode",urlCon.getResponseCode()); 51 | Map> responseHeaders= new HashMap<>(urlCon.getHeaderFields()); 52 | responseHeaders.remove(null); 53 | responseHeaders=handleResponseHeaders(urlCon,responseHeaders); 54 | response.put("headers",responseHeaders); 55 | return response; 56 | 57 | } 58 | 59 | //对于https请求,进行证书校验 60 | private static void addCertVerifier(HttpsURLConnection urlCon) throws Exception { 61 | // 在此做证书校验 62 | // urlCon.setSSLSocketFactory(getSSLSocketFactory()); 63 | urlCon.setHostnameVerifier(new HostnameVerifier() { 64 | @Override 65 | public boolean verify(String hostname, SSLSession session) { 66 | //return "api.dtworkroom.com".equals(hostname); 67 | HostnameVerifier hv=HttpsURLConnection.getDefaultHostnameVerifier(); 68 | return hv.verify("*.dtworkroom.com",session); 69 | } 70 | }); 71 | } 72 | 73 | //预处理请求头 74 | private static void handleRequestHeaders(HttpURLConnection connection,JSONObject headers) throws Exception { 75 | 76 | Iterator iterator = headers.keys(); 77 | while(iterator.hasNext()){ 78 | String key = (String) iterator.next(); 79 | String value = headers.getString(key); 80 | if(!key.toLowerCase().equals("cookie")){ 81 | //请求cookie 82 | connection.setRequestProperty(key, value); 83 | } 84 | } 85 | List cookies= cookieManager.getCookieStore().get(connection.getURL().toURI()); 86 | cookies.toString(); 87 | } 88 | 89 | private static Map> handleResponseHeaders(HttpURLConnection connection, Map> responseHeaders) throws Exception { 90 | //获取响应头中的cookies,端上统一管理cookie 91 | cookieManager.put(connection.getURL().toURI(),responseHeaders); 92 | responseHeaders.remove("set-cookie"); 93 | return responseHeaders; 94 | } 95 | 96 | 97 | private static String inputStream2String(InputStream is) { 98 | String result = ""; 99 | String line; 100 | InputStreamReader inputReader = new InputStreamReader(is); 101 | BufferedReader bufReader = new BufferedReader(inputReader); 102 | try { 103 | while ((line = bufReader.readLine()) != null) 104 | result += line + "\r\n"; 105 | bufReader.close(); 106 | } catch (Exception e) { 107 | e.printStackTrace(); 108 | } 109 | return result; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/wendu/jsbdemo/WrokWithFlyioTestActivity.java: -------------------------------------------------------------------------------- 1 | package wendu.jsbdemo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.webkit.JavascriptInterface; 6 | 7 | import org.json.JSONObject; 8 | 9 | import wendu.dsbridge.CompletionHandler; 10 | import wendu.dsbridge.DWebView; 11 | 12 | public class WrokWithFlyioTestActivity extends AppCompatActivity { 13 | 14 | @Override 15 | protected void onCreate(Bundle savedInstanceState) { 16 | super.onCreate(savedInstanceState); 17 | setContentView(R.layout.activity_wrok_with_flyio_test); 18 | DWebView dWebView= (DWebView) findViewById(R.id.webview); 19 | dWebView.addJavascriptObject(new Object(){ 20 | 21 | /** 22 | * Note: This method is for Fly.js 23 | * In browser, Ajax requests are sent by browser, but Fly can 24 | * redirect requests to native, more about Fly see https://github.com/wendux/fly 25 | * @param requestData passed by fly.js, more detail reference https://wendux.github.io/dist/#/doc/flyio-en/native 26 | * @param handler 27 | */ 28 | @JavascriptInterface 29 | public void onAjaxRequest(Object requestData, CompletionHandler handler){ 30 | // Handle ajax request redirected by Fly 31 | AjaxHandler.onAjaxRequest((JSONObject)requestData,handler); 32 | } 33 | 34 | },null); 35 | 36 | dWebView.loadUrl("file:///android_asset/fly.html"); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_call_javascript.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 14 | 15 | 20 | 21 |