dev android,and complete load context

This commit is contained in:
pengfei.zhou
2019-07-18 16:29:24 +08:00
parent f2fc57bbc7
commit 2a34d7b638
21 changed files with 425 additions and 43 deletions

View File

@@ -21,7 +21,11 @@ android {
consumerProguardFiles 'proguard-rules.pro'
}
}
sourceSets {
main {
assets.srcDirs = [project.rootDir.getParent() + "/js-framework/bundle"]
}
}
}
dependencies {

View File

@@ -1,9 +1,20 @@
package com.github.pengfeizhou.hego;
import android.app.Application;
/**
* @Description: Android
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class Hego {
private static Application sApplication;
public static void init(Application application) {
sApplication = application;
}
public static Application application() {
return sApplication;
}
}

View File

@@ -6,4 +6,21 @@ package com.github.pengfeizhou.hego;
* @CreateDate: 2019-07-18
*/
public class HegoConstant {
public static final String INJECT_LOG = "nativeLog";
public static final String INJECT_REQUIRE = "nativeRequire";
public static final String INJECT_BRIDGE = "nativeBridge";
public static final String TEMPLATE_CONTEXT_CREATE = "Reflect.apply(" +
"function(hego,context,require,exports){" + "\n" +
"%s" + "\n" +
"},hego.jsObtainContext(%s),[" +
"undefined," +
"hego.jsObtainContext(%s)," +
"hego.__require__" +
",{}" +
"])";
public static final String TEMPLATE_CONTEXT_DESTORY = "hego.jsRelease(%s)";
public static final String GLOBAL_HEGO = "hego";
public static final String HEGO_CONTEXT_RELEASE = "jsReleaseContext";
public static final String HEGO_CONTEXT_INVOKE = "jsCallEntityMethod";
}

View File

@@ -1,9 +1,31 @@
package com.github.pengfeizhou.hego;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Description: Android
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class HegoHolder {
public class HegoContext {
private static AtomicInteger sCounter = new AtomicInteger();
private final String mContextId;
private HegoContext(String contextId) {
this.mContextId = contextId;
}
public static HegoContext createContext(String script, String alias) {
String contextId = String.valueOf(sCounter.incrementAndGet());
HegoDriver.getInstance().createPage(contextId, script, alias);
return new HegoContext(contextId);
}
public void callJS(String methodName, Object... args) {
HegoDriver.getInstance().invokeContextMethod(mContextId, methodName, args);
}
public void teardown() {
HegoDriver.getInstance().destoryContext(mContextId);
}
}

View File

@@ -1,9 +1,38 @@
package com.github.pengfeizhou.hego;
import com.github.pengfeizhou.jscore.JSDecoder;
/**
* @Description: Android
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public interface HegoDriver {
public class HegoDriver {
private final HegoJSEngine hegoJSEngine;
public HegoSettableFuture<JSDecoder> invokeContextMethod(final String contextId, final String method, final Object... args) {
return hegoJSEngine.invokeContextEntityMethod(contextId, method, args);
}
private static class Inner {
private static final HegoDriver sInstance = new HegoDriver();
}
private HegoDriver() {
hegoJSEngine = new HegoJSEngine();
}
public static HegoDriver getInstance() {
return Inner.sInstance;
}
public void createPage(final String contextId, final String script, final String source) {
hegoJSEngine.prepareContext(contextId, script, source);
}
public void destoryContext(String contextId) {
hegoJSEngine.destroyContext(contextId);
}
}

View File

@@ -1,9 +1,167 @@
package com.github.pengfeizhou.hego;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import com.github.pengfeizhou.hego.jse.HegoJSExecutor;
import com.github.pengfeizhou.hego.jse.IHegoJSE;
import com.github.pengfeizhou.jscore.JSDecoder;
import com.github.pengfeizhou.jscore.JavaFunction;
import com.github.pengfeizhou.jscore.JavaValue;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.concurrent.FutureTask;
/**
* @Description: Android
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class HegoJSEngine {
public class HegoJSEngine implements Handler.Callback {
private final Handler mJSHandler;
private IHegoJSE mHegoJSE;
public HegoJSEngine() {
HandlerThread handlerThread = new HandlerThread(this.getClass().getSimpleName());
handlerThread.start();
Looper looper = handlerThread.getLooper();
mJSHandler = new Handler(looper, this);
mJSHandler.post(new Runnable() {
@Override
public void run() {
initJSExecutor();
initHugoRuntime();
}
});
}
private void initJSExecutor() {
mHegoJSE = new HegoJSExecutor();
mHegoJSE.injectGlobalJSFunction(HegoConstant.INJECT_LOG, new JavaFunction() {
@Override
public JavaValue exec(JSDecoder[] args) {
try {
String type = args[0].string();
String message = args[1].string();
switch (type) {
case "w":
HegoLog.w("js", message);
break;
case "e":
HegoLog.e("js", message);
break;
default:
HegoLog.d("js", message);
break;
}
} catch (Exception e) {
e.printStackTrace();
}
return new JavaValue();
}
});
}
private void initHugoRuntime() {
loadBuiltinJS("sandbox.js");
}
@Override
public boolean handleMessage(Message msg) {
return false;
}
public void teardown() {
mHegoJSE.teardown();
}
private void loadBuiltinJS(String assetName) {
String script = HegoUtils.readAssetFile(assetName);
mHegoJSE.loadJS(script, "Assets://" + assetName);
}
public void prepareContext(final String contextId, final String script, final String source) {
Runnable runnable = new Runnable() {
@Override
public void run() {
mHegoJSE.loadJS(packageContextScript(script, contextId), "Context://" + source);
}
};
doOnJSThread(runnable);
}
public void doOnJSThread(Runnable runnable) {
if (isJSThread()) {
runnable.run();
} else {
mJSHandler.post(runnable);
}
}
public void destroyContext(final String contextId) {
Runnable runnable = new Runnable() {
@Override
public void run() {
mHegoJSE.loadJS(String.format(HegoConstant.TEMPLATE_CONTEXT_DESTORY, contextId), "_Context://" + contextId);
}
};
doOnJSThread(runnable);
}
private String packageContextScript(String content, String contextId) {
return String.format(HegoConstant.TEMPLATE_CONTEXT_CREATE, content, contextId, contextId);
}
public boolean isJSThread() {
return Looper.myLooper() == mJSHandler.getLooper();
}
public HegoSettableFuture<JSDecoder> invokeContextEntityMethod(final String contextId, final String method, final Object... args) {
final Object[] nArgs = new Object[args.length + 2];
nArgs[0] = contextId;
nArgs[1] = method;
if (args.length > 0) {
System.arraycopy(args, 0, nArgs, 2, args.length);
}
return invokeHegoMethod(HegoConstant.HEGO_CONTEXT_INVOKE, args);
}
public HegoSettableFuture<JSDecoder> invokeHegoMethod(final String method, final Object... args) {
final HegoSettableFuture<JSDecoder> settableFuture = new HegoSettableFuture<>();
Runnable runnable = new Runnable() {
@Override
public void run() {
ArrayList<JavaValue> values = new ArrayList<>();
for (Object arg : args) {
if (arg == null) {
values.add(new JavaValue());
} else if (arg instanceof JSONObject) {
values.add(new JavaValue((JSONObject) arg));
} else if (arg instanceof String) {
values.add(new JavaValue((String) arg));
} else if (arg instanceof Integer) {
values.add(new JavaValue((Integer) arg));
} else if (arg instanceof Double) {
values.add(new JavaValue((Double) arg));
} else if (arg instanceof Boolean) {
values.add(new JavaValue((Boolean) arg));
} else if (arg instanceof JavaValue) {
values.add((JavaValue) arg);
} else {
values.add(new JavaValue(String.valueOf(arg)));
}
}
settableFuture.set(mHegoJSE.invokeMethod(HegoConstant.GLOBAL_HEGO, method,
values.toArray(new JavaValue[values.size()]), true));
}
};
doOnJSThread(runnable);
return settableFuture;
}
}

View File

@@ -1,9 +1,24 @@
package com.github.pengfeizhou.hego;
import android.util.Log;
/**
* @Description: Android
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class HegoLog {
private static String TAG = Hego.class.getSimpleName();
public static void d(String suffix, String message) {
Log.d(TAG + suffix, message);
}
public static void w(String suffix, String message) {
Log.w(TAG + suffix, message);
}
public static void e(String suffix, String message) {
Log.e(TAG + suffix, message);
}
}

View File

@@ -5,5 +5,54 @@ package com.github.pengfeizhou.hego;
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class HegoSettableFuture {
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
/**
* A super simple Future-like class that can safely notify another Thread when a value is ready.
* Does not support setting errors or canceling.
*/
public class HegoSettableFuture<T> {
private final CountDownLatch mReadyLatch = new CountDownLatch(1);
private volatile
T mResult;
/**
* Sets the result. If another thread has called {@link #get}, they will immediately receive the
* value. Must only be called once.
*/
public void set(T result) {
if (mReadyLatch.getCount() == 0) {
throw new RuntimeException("Result has already been set!");
}
mResult = result;
mReadyLatch.countDown();
}
/**
* Wait up to the timeout time for another Thread to set a value on this future. If a value has
* already been set, this method will return immediately.
* <p>
* NB: For simplicity, we catch and wrap InterruptedException. Do NOT use this class if you
* are in the 1% of cases where you actually want to handle that.
*/
public T get(long timeoutMS) {
try {
if (!mReadyLatch.await(timeoutMS, TimeUnit.MILLISECONDS)) {
throw new TimeoutException();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
return mResult;
}
public static class TimeoutException extends RuntimeException {
public TimeoutException() {
super("Timed out waiting for future");
}
}
}

View File

@@ -1,9 +1,36 @@
package com.github.pengfeizhou.hego;
import android.content.res.AssetManager;
import java.io.IOException;
import java.io.InputStream;
/**
* @Description: Android
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class HegoUtils {
public static String readAssetFile(String assetFile) {
InputStream inputStream = null;
try {
AssetManager assetManager = Hego.application().getAssets();
inputStream = assetManager.open(assetFile);
int length = inputStream.available();
byte[] buffer = new byte[length];
inputStream.read(buffer);
return new String(buffer);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "";
}
}

View File

@@ -11,11 +11,11 @@ import com.github.pengfeizhou.jscore.JavaValue;
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public class HugoJSExecutor implements IHugoJSE {
public class HegoJSExecutor implements IHegoJSE {
private final JSExecutor mJSExecutor;
public HugoJSExecutor() {
public HegoJSExecutor() {
this.mJSExecutor = JSExecutor.create();
}

View File

@@ -10,7 +10,7 @@ import com.github.pengfeizhou.jscore.JavaValue;
* @Author: pengfei.zhou
* @CreateDate: 2019-07-18
*/
public interface IHugoJSE {
public interface IHegoJSE {
/**
* 执行JS语句
*