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

@ -14,7 +14,7 @@
</profile-state> </profile-state>
</entry> </entry>
</component> </component>
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -16,6 +16,11 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
sourceSets {
main {
assets.srcDirs = [project.rootDir.getParent() + "/js-framework/bundle"]
}
}
} }
dependencies { dependencies {

View File

@ -3,6 +3,7 @@
package="com.github.pengfeizhou.hegodemo"> package="com.github.pengfeizhou.hegodemo">
<application <application
android:name=".MyApplication"
android:allowBackup="true" android:allowBackup="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"

View File

@ -3,7 +3,8 @@ package com.github.pengfeizhou.hegodemo;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import com.github.pengfeizhou.hegodemo.R; import com.github.pengfeizhou.hego.HegoContext;
import com.github.pengfeizhou.hego.HegoUtils;
public class MainActivity extends AppCompatActivity { public class MainActivity extends AppCompatActivity {
@ -11,5 +12,7 @@ public class MainActivity extends AppCompatActivity {
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); setContentView(R.layout.activity_main);
HegoContext hegoContext = HegoContext.createContext(HegoUtils.readAssetFile("test.js"), "demo");
hegoContext.callJS("");
} }
} }

View File

@ -2,10 +2,17 @@ package com.github.pengfeizhou.hegodemo;
import android.app.Application; import android.app.Application;
import com.github.pengfeizhou.hego.Hego;
/** /**
* @Description: Android * @Description: Android
* @Author: pengfei.zhou * @Author: pengfei.zhou
* @CreateDate: 2019-07-18 * @CreateDate: 2019-07-18
*/ */
public class MyApplication extends Application { public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Hego.init(this);
}
} }

View File

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

View File

@ -1,9 +1,20 @@
package com.github.pengfeizhou.hego; package com.github.pengfeizhou.hego;
import android.app.Application;
/** /**
* @Description: Android * @Description: Android
* @Author: pengfei.zhou * @Author: pengfei.zhou
* @CreateDate: 2019-07-18 * @CreateDate: 2019-07-18
*/ */
public class Hego { 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 * @CreateDate: 2019-07-18
*/ */
public class HegoConstant { 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; package com.github.pengfeizhou.hego;
import java.util.concurrent.atomic.AtomicInteger;
/** /**
* @Description: Android * @Description: Android
* @Author: pengfei.zhou * @Author: pengfei.zhou
* @CreateDate: 2019-07-18 * @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; package com.github.pengfeizhou.hego;
import com.github.pengfeizhou.jscore.JSDecoder;
/** /**
* @Description: Android * @Description: Android
* @Author: pengfei.zhou * @Author: pengfei.zhou
* @CreateDate: 2019-07-18 * @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; 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 * @Description: Android
* @Author: pengfei.zhou * @Author: pengfei.zhou
* @CreateDate: 2019-07-18 * @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; package com.github.pengfeizhou.hego;
import android.util.Log;
/** /**
* @Description: Android * @Description: Android
* @Author: pengfei.zhou * @Author: pengfei.zhou
* @CreateDate: 2019-07-18 * @CreateDate: 2019-07-18
*/ */
public class HegoLog { 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 * @Author: pengfei.zhou
* @CreateDate: 2019-07-18 * @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; package com.github.pengfeizhou.hego;
import android.content.res.AssetManager;
import java.io.IOException;
import java.io.InputStream;
/** /**
* @Description: Android * @Description: Android
* @Author: pengfei.zhou * @Author: pengfei.zhou
* @CreateDate: 2019-07-18 * @CreateDate: 2019-07-18
*/ */
public class HegoUtils { 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 * @Author: pengfei.zhou
* @CreateDate: 2019-07-18 * @CreateDate: 2019-07-18
*/ */
public class HugoJSExecutor implements IHugoJSE { public class HegoJSExecutor implements IHegoJSE {
private final JSExecutor mJSExecutor; private final JSExecutor mJSExecutor;
public HugoJSExecutor() { public HegoJSExecutor() {
this.mJSExecutor = JSExecutor.create(); this.mJSExecutor = JSExecutor.create();
} }

View File

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

View File

@ -0,0 +1,29 @@
import { Text, Alignment, Color, VLayout, Registor, Panel, log, logw, loge } from "./index"
const v = new Text
v.width = 20
v.height = 30
v.left = 5
v.top = 5
v.bgColor = Color.parse('#00ff00')
v.config = {
alignment: Alignment.start
}
console.log(v.toModel())
const layout = new VLayout
layout.space = 10
console.log(layout.viewId)
console.log(layout.toModel())
@Registor
export class MyPage extends Panel {
build() {
return layout
}
log() {
log("Hello.HEGO")
logw("Hello.HEGO")
loge("Hello.HEGO")
}
}

View File

@ -1,31 +1,4 @@
import { Text, Alignment, VLayout, Gravity } from "./src/ui/view";
import { Color } from "./src/util/color";
import { Panel, Registor } from "./src/ui/panel";
export * from "./src/ui/view" export * from "./src/ui/view"
export * from "./src/ui/panel" export * from "./src/ui/panel"
export * from "./src/util/color" export * from "./src/util/color"
export * from './src/util/log'
const v = new Text
v.width = 20
v.height = 30
v.left = 5
v.top = 5
v.bgColor = Color.parse('#00ff00')
v.config = {
alignment: Alignment.start
}
console.log(v.toModel())
const layout = new VLayout
layout.space = 10
console.log(layout.viewId)
console.log(layout.toModel())
// @Registor
class MyPage extends Panel {
build(): import("./src/ui/view").View {
throw new Error("Method not implemented.");
}
}
console.log('end')

View File

@ -29,4 +29,17 @@ export default [
commonjs() commonjs()
] ]
}, },
{
input: "build/index.test.js",
output: {
format: "cjs",
file: "bundle/test.js",
},
sourceMap: true,
plugins: [
resolve({ jsnext: true, main: true }),
commonjs()
],
external: ['./index'],
},
] ]

View File

@ -8,9 +8,9 @@ import { loge } from "../util/log";
* function(hego,context,require){ * function(hego,context,require){
* //Script content * //Script content
* REG() * REG()
* },hego.obtainContext(id),[ * },hego.jsObtainContext(id),[
* undefined, * undefined,
* hego.obtainContext(id), * hego.jsObtainContext(id),
* hego.__require__, * hego.__require__,
* ]) * ])
* // load module in global scope * // load module in global scope
@ -24,6 +24,7 @@ import { loge } from "../util/log";
* *
* ``` * ```
*/ */
declare function nativeRequire(moduleName: string): boolean declare function nativeRequire(moduleName: string): boolean
declare function nativeBridge(contextId: string, namespace: string, method: string, args?: any, callbackId?: string): boolean declare function nativeBridge(contextId: string, namespace: string, method: string, args?: any, callbackId?: string): boolean

View File

@ -1,11 +1,29 @@
declare function nativeLog(type: 'd' | 'w' | 'e', message: string): void
function toString(message: any) {
if (message instanceof Function) {
return message.toString()
} else if (message instanceof Object) {
try {
return JSON.stringify(message)
} catch (e) {
return message.toString()
}
} else if (message === undefined) {
return "undefined"
} else {
return message.toString()
}
}
export function log(message: any) { export function log(message: any) {
console.log(message) nativeLog('d', toString(message))
} }
export function loge(message: any) { export function loge(message: any) {
console.error(message) nativeLog('e', toString(message))
} }
export function logw(message: any) { export function logw(message: any) {
console.warn(message) nativeLog('w', toString(message))
} }