【Android】Hippy自定义终端模块

自定义终端模块

很多时候 JS 需要访问对应终端的一些能力模块,比如数据库、下载、网络请求等,这时候就需要使用 Module 来暴露接口给JS使用。Hippy SDK 中默认实现了部分 Module,但这极有可能无法满足你的需求,这就需要你对 Module 进行扩展封装。


Module扩展

我们将以 TestModule 为例,从头扩展一个 Module,这个 Module 将展示前端如何调用终端能力,并且把结果返回给前端。

终端扩展Module包括四步:

  1. 创建 HippyNativeModuleBase 的子类。
  2. 添加 HippyNativeModule 注解。
  3. 实现导出给 JS 的方法。
  4. 注册 Module。

1. 创建HippyNativeModuleBase的子类

首先我们需要创建TestModule类,并且继承HippyNativeModuleBase

package com.tencent.mtt.hippy.example.modules;

import com.tencent.mtt.hippy.HippyEngineContext;
import com.tencent.mtt.hippy.modules.nativemodules.HippyNativeModuleBase;

public class TestModule extends HippyNativeModuleBase
{

    public LogModule(HippyEngineContext context)
    {
        super(context);
    }
}

2. 添加HippyNativeModule注解

HippyNativeModuleBase 要求增加注解 @HippyNativeModule

HippyNativeModule有两个注解参数:

  • name:能力名称,js调用时需要通过此访问该能力。
  • thread:能力运行的线程。包括 HippyNativeModule.Thread.DOM(Dom线程)、HippyNativeModule.Thread.MAIN(主线程)、HippyNativeModule.Thread.BRIDGE(Bridge线程、默认值)。
@HippyNativeModule(name = "TestModule", thread = HippyNativeModule.Thread.BRIDGE)
public class TestModule extends HippyNativeModuleBase
{
    ...
}

3. 实现导出给JS的方法

导出给 JS 使用的方法,必须使用注解 @HippyMethod ,方法必须为 public 类型,返回类型必须为 void

支持的方法参数类型包括:

  • Java基本数据类型。
  • HippyArray:类似于ArrayList,线程非安全。
  • HippyMap:类似于HashMap,线程非安全。
  • Promise:回调JS的触发器,通过 resolve 方法返回成功信息给JS。通过 reject 方法返回失败实现给JS。
@HippyMethod(name="log")
public void log(String msg)
{
    Log.d("TestModule", msg);
}

@HippyMethod(name="helloNative")
public void helloNative(HippyMap hippyMap)
{
    String hello = hippyMap.getString("hello");
    Log.d("TestModule", hello);
}

@HippyMethod(name = "helloNativeWithPromise")
public void helloNativeWithPromise(HippyMap hippyMap, Promise promise)
{
    // 这里回来的参数可以为java的基础类型,和hippymap与hippyarry, 但是前端调用的时候必须对应上
    String hello = hippyMap.getString("hello");
    Log.d("TestModule", hello);
    if (true)
    {
        // TODO 如果模块这里处理成功回调resolve
        HippyMap hippyMap1 = new HippyMap();
        hippyMap1.pushInt("code", 1);
        hippyMap1.pushString("result", "hello i am from native");
        promise.resolve(hippyMap1);
    }
    else
    {
        // 失败就回调reject
        HippyMap hippyMap1 = new HippyMap();
        hippyMap1.pushInt("code", -1);
        promise.reject(hippyMap1);
    }
}

4. 注册Module

然后需要注册这个Module。需要在 HippyPackagegetNativeModules 方法中添加这个 Module,这样它才能在JS中被访问到。

import com.tencent.mtt.hippy.HippyEngineContext;
import com.tencent.mtt.hippy.HippyPackage;
import com.tencent.mtt.hippy.common.Provider;
import com.tencent.mtt.hippy.example.module.TestModule;

import com.tencent.mtt.hippy.modules.javascriptmodules.HippyJavaScriptModule;
import com.tencent.mtt.hippy.modules.nativemodules.HippyNativeModuleBase;
import com.tencent.mtt.hippy.uimanager.HippyViewController;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ExamplePackages implements HippyPackage
{
    @Override
    public Map<Class<? extends HippyNativeModuleBase>, Provider<? extends     HippyNativeModuleBase>> getNativeModules(final HippyEngineContext context)
    {
        Map<Class<? extends HippyNativeModuleBase>, Provider<? extends HippyNativeModuleBase>> modules = new HashMap<>();

        // regist the LogModule
        modules.put(ToastModule.class, new Provider<HippyNativeModuleBase>()
        {
            @Override
            public HippyNativeModuleBase get()
            {
                return new TestModule(context);
            }
        });

        return modules;
    }

5. 执行流程

1. 注册流程

Register NativeModule

2. 使用流程

Call NativeModule Method

6. 源码

1. 注册流程

  1. MyActivityonCreate时初始化Hippy引擎
mHippyEngine.initEngine(new HippyEngine.EngineListener() {...});

// HippyEngineManagerImpl#initEngine
public void initEngine(EngineListener listener) {
  ...
  restartEngineInBackground();
  ...
}

// HippyEngineManagerImpl#restartEngineInBackground
private synchronized void restartEngineInBackground() {
  ...
  mEngineContext = new HippyEngineContextImpl(mDebugMode, mServerHost);
  ...
}

// HippyEngineContextImpl#constructor
public HippyEngineContextImpl(DebugMode debugMode, String debugServerHost) {
  mModuleManager = new HippyModuleManagerImpl(this, mAPIProviders);
  ...
}

// HippyModuleManagerImpl#constructor
public HippyModuleManagerImpl(HippyEngineContext context, List<HippyAPIProvider> packages) {
    this.mContext = context;
    mNativeModuleInfo = new ConcurrentHashMap<>();
    mJsModules = new HashMap<>();
  	/* 分析1 */
    addModules(packages);
}
  1. / 分析1 / HippyModuleManagerImpl 将指定本地模块添加到内存中的入口
public synchronized void addModules(List<HippyAPIProvider> apiProviders) {
    ...
    for (HippyAPIProvider provider : apiProviders) {
      	// 此处取出的即是在 【4. 注册Module 】步骤中添加的 module
        Map<Class<? extends HippyNativeModuleBase>, Provider<? extends 
          HippyNativeModuleBase>> nativeModules = provider.getNativeModules(mContext);
        if (nativeModules != null && nativeModules.size() > 0) {
            Set<Class<? extends HippyNativeModuleBase>> keys = nativeModules.keySet();
            for (Class cls : keys) {
              	/* 分析2 */
                addNativeModule(cls, nativeModules.get(cls));
            }
        }
      ...
    }
}
  1. / 分析2 / HippyModuleManagerImpl 将本地模块转换成模块名与模块信息的映射
public synchronized <T extends HippyNativeModuleBase> void addNativeModule(Class<T> cls,
        Provider<T> provider) {
		...
  	/* 分析3 */
    HippyNativeModuleInfo moduleInfo = new HippyNativeModuleInfo(cls, provider);
    String[] names = moduleInfo.getNames();
    if (names != null && names.length > 0) {
        for (String name : names) {
            if (!mNativeModuleInfo.containsKey(name)) {
                mNativeModuleInfo.put(name, moduleInfo);
            }
        }
    }
		...
}
  1. / 分析3 / HippyNativeModuleInfo 保存模块信息,如有必要则注册模块的方法并初始化模块
public HippyNativeModuleInfo(@NonNull Class<?> cls,
        Provider<? extends HippyNativeModuleBase> provider) {
    HippyNativeModule annotation = cls.getAnnotation(HippyNativeModule.class);
    mClass = cls;
    mProvider = provider;
    if (annotation != null) {
        mName = annotation.name();
        mNames = annotation.names();
        mThread = annotation.thread();
      	/* 分析a */
        initImmediately(annotation);
    }
}

/* 分析a */
private void initImmediately(@NonNull HippyNativeModule annotation) {
  // 如果注解要求立即初始化,则注册模块的方法并初始化模块  
  if (annotation.init()) {
        try {
          	/* 分析b */
            initialize();
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }
}

/* 分析b */
public void initialize() {
    if (mInit) {
        return;
    }
  	/* 分析c */
    // 注册模块的方法,不重入
    checkModuleMethods();
   // 新建模块实例
    mInstance = mProvider.get();
  	// 初始化模块
    mInstance.initialize();
    mInit = true;
}

/* 分析c */
// 检查一个模块的方法是否已经注册,没有则立即注册
private void checkModuleMethods() {
    if (mMethods != null) {
        return;
    }
    synchronized (this) {
        if (mMethods != null) {
            return;
        }
        mMethods = new ConcurrentHashMap<>();
        Method[] targetMethods = mClass.getMethods();
        for (Method targetMethod : targetMethods) {
            HippyMethod hippyMethod = targetMethod.getAnnotation(HippyMethod.class);
            if (hippyMethod == null) {
                continue;
            }
            String methodName = hippyMethod.name();
            ...
            mMethods.put(methodName, new HippyNativeMethod(targetMethod, 
                                                           hippyMethod.isSync(), 
                                                           hippyMethod.useJSValueType()));
        }
    }
}

2. 使用流程

  1. HippyBridgeImpl 前端调用模块方法后,android端的执行入口
public void callNatives(String moduleName, String moduleFunc, String callId, byte[] buffer) {
  /* a */  
  callNatives(moduleName, moduleFunc, callId, ByteBuffer.wrap(buffer));
}

/* a */ 
public void callNatives(String moduleName, String moduleFunc, String callId,
        ByteBuffer buffer) {
    ...
    /* b */ 
    mBridgeCallback.callNatives(moduleName, moduleFunc, callId, params);
}

/* b */
/* HippyBridgeManagerImpl */
public void callNatives(String moduleName, String moduleFunc, String callId,
        Object params) {
  	...
    HippyCallNativeParams callNativeParams = HippyCallNativeParams
                .obtain(moduleName, moduleFunc, callId, params);
  	/* 分析1 */
    moduleManager.callNatives(callNativeParams);
}
  1. / 分析1 / HippyModuleManagerImpl 根据注解中配置的线程发送消息
public void callNatives(HippyCallNativeParams params) {
    ...
    if (moduleInfo.getThread() == HippyNativeModule.Thread.DOM) {
        Handler handler = getDomThreadHandler();
        Message msg = handler.obtainMessage(MSG_CODE_CALL_NATIVES, params);
        handler.sendMessage(msg);
    } else if (moduleInfo.getThread() == HippyNativeModule.Thread.MAIN) {
        Handler handler = getUIThreadHandler();
        Message msg = handler.obtainMessage(MSG_CODE_CALL_NATIVES, params);
        handler.sendMessage(msg);
    } else {
        Handler handler = getBridgeThreadHandler();
        Message msg = handler.obtainMessage(MSG_CODE_CALL_NATIVES, params);
        handler.sendMessage(msg);
    }
}

public boolean handleMessage(Message msg) {
  ...
  case MSG_CODE_CALL_NATIVES: {
      ...
      /* 分析2 */
      doCallNatives(params);
    	...
  }
  ...
}
  1. / 分析2 / HippyModuleManagerImpl 利用注册的模块信息调用相关方法
void doCallNatives(@NonNull HippyCallNativeParams params) {
    PromiseImpl promise = new PromiseImpl(mContext, params.moduleName, params.moduleFunc,
            params.callId);
    try {
      	// 获取注册的模块信息
        HippyNativeModuleInfo moduleInfo = mNativeModuleInfo.get(params.moduleName);
        if (moduleInfo == null) {
            promise.doCallback(PromiseImpl.PROMISE_CODE_NORMAN_ERROR,
                    "module can not be found");
            return;
        }
      	// 初始化模块 (可重入,不会重复初始化)
        moduleInfo.initialize();
      	// 获取注册的模块方法
        HippyNativeModuleInfo.HippyNativeMethod method = moduleInfo
                .findMethod(params.moduleFunc);
        if (method == null || method.isSync()) {
            promise.doCallback(PromiseImpl.PROMISE_CODE_NORMAN_ERROR,
                               "module function can not be found");
            return;
        }
      	/* a */
        method.invoke(moduleInfo.getInstance(), params.params, promise);
    } catch (Throwable e) {
        promise.doCallback(PromiseImpl.PROMISE_CODE_NORMAN_ERROR, e.getMessage());
        mContext.getGlobalConfigs().getExceptionHandler()
                .handleNativeException(new RuntimeException(e), true);
    }
}

/* a */
// HippyNativeModuleInfo
public void invoke(Object receiver, @Nullable Object args,
        PromiseImpl promise) throws Exception {
    Object[] params = null;
    if (args != null) {
        params = prepareArguments(args, promise);
    }
  	// 反射调用
    mMethod.invoke(receiver, params);
    if (promise.needResolveBySelf()) {
        promise.resolve("");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小黄才不管那么多

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值