android返回键返回指定目录,使用React-navigation时候 Android物理返回键&BackHandler exitApp 源码分析...

React Native 监听android 物理返回键

根据文档,安卓back键的处理主要就是一个事件监听:

componentDidMount() {

BackHandler.addEventListener('hardwareBackPress', this.onBackButtonPressAndroid);

}

componentWillUnmount() {

BackHandler.removeEventListener('hardwareBackPress', this.onBackButtonPressAndroid);

}

onBackButtonPressAndroid = () => {

if (this.state.currentStateRoute.length ==1) {

let array = this.state.currentStateRoute[0].routes;

if (array.length ==1) {

if (lastBackPressed && lastBackPressed + 2000 >= Date.now()) {

BackHandler.exitApp();

return false;

}

lastBackPressed = Date.now();

ToastAndroid.show('再按一次退出应用', ToastAndroid.SHORT);

return true;

}else {

return false;

}

}else {

return false;

}

};

导航使用的是react-navigation, 在注册 StackNavigator 路由的时候,同时注册BackHandler监听,也即很多时候是在程序入口文件App.js 中。

原生代码需要注意的是,监听back键有两个方法,但是不能同时使用,否则会出现监听不到的情况。

/**

* 监听Back键按下事件,方法1:

* 注意:

* super.onBackPressed()会自动调用finish()方法,关闭

* 当前Activity.

* 若要屏蔽Back键盘,注释该行代码即可

*/

@Override

public void onBackPressed() {

super.onBackPressed();

System.out.println("按下了back键 onBackPressed()");

}

/**

* 监听Back键按下事件,方法2:

* 注意:

* 返回值表示:是否能完全处理该事件

* 在此处返回false,所以会继续传播该事件.

* 在具体项目中此处的返回值视情况而定.

*/

@Override

public boolean onKeyDown(int keyCode, KeyEvent event) {

if ((keyCode == KeyEvent.KEYCODE_BACK)) {

System.out.println("按下了back键 onKeyDown()");

return false;

}else {

return super.onKeyDown(keyCode, event);

}

}

至此,back键就可以正常返回了,

遇到的另一个问题是,BackHandler.exitApp()无法正常退出App

原因是

@Override

public void invokeDefaultOnBackPressed() {

super.onBackPressed();

}

这一步是如何实现的呢?先说结论:

exitApp() 方法就是调用了 Native 层 ReactActivity 的 onBackPress 方法。

进入BackHandler 源码,路径:node_modules/react-native/Libraries/Utilities/BackHandler.android.js:

BackHandler

var DeviceEventManager = require('NativeModules').DeviceEventManager;

var BackHandler = {

exitApp: function() {

DeviceEventManager.invokeDefaultBackPressHandler();

},

/**

* Adds an event handler. Supported events:

*

* - `hardwareBackPress`: Fires when the Android hardware back button is pressed or when the

* tvOS menu button is pressed.

*/

addEventListener: function (

eventName: BackPressEventName,

handler: Function

): {remove: () => void} {

_backPressSubscriptions.add(handler);

return {

remove: () => BackHandler.removeEventListener(eventName, handler),

};

},

/**

* Removes the event handler.

*/

removeEventListener: function(

eventName: BackPressEventName,

handler: Function

): void {

_backPressSubscriptions.delete(handler);

},

};

BackHandler 作为一个常量对象,其中包含了 exitApp、addEventListener、removeEventListener 函数。在 exitApp 函数中,调用了 DeviceEventManager 的 invokeDefaultBackPressHandler 函数。

DeviceEventManager 是系统定义的处理硬件反压等设备硬件事件的本机模块的实现类,对应于 node_modules/react-native/ReactAndroid/src/main/java/com.facebook.react.modules.core 目录下的 DeviceEventManagerModule.java。

DeviceEventManagerModule

@ReactModule(name = "DeviceEventManager")

public class DeviceEventManagerModule extends ReactContextBaseJavaModule {

public interface RCTDeviceEventEmitter extends JavaScriptModule {

void emit(String eventName, @Nullable Object data);

}

private final Runnable mInvokeDefaultBackPressRunnable;

public DeviceEventManagerModule(

ReactApplicationContext reactContext,

final DefaultHardwareBackBtnHandler backBtnHandler) {

super(reactContext);

mInvokeDefaultBackPressRunnable = new Runnable() {

@Override

public void run() {

UiThreadUtil.assertOnUiThread();

backBtnHandler.invokeDefaultOnBackPressed();

}

};

}

/**

* Sends an event to the JS instance that the hardware back has been pressed.

*/

public void emitHardwareBackPressed() {

getReactApplicationContext()

.getJSModule(RCTDeviceEventEmitter.class)

.emit("hardwareBackPress", null);

}

/**

* Sends an event to the JS instance that a new intent was received.

*/

public void emitNewIntentReceived(Uri uri) {

WritableMap map = Arguments.createMap();

map.putString("url", uri.toString());

getReactApplicationContext()

.getJSModule(RCTDeviceEventEmitter.class)

.emit("url", map);

}

/**

* Invokes the default back handler for the host of this catalyst instance. This should be invoked

* if JS does not want to handle the back press itself.

*/

@ReactMethod

public void invokeDefaultBackPressHandler() {

getReactApplicationContext().runOnUiQueueThread(mInvokeDefaultBackPressRunnable);

}

@Override

public String getName() {

return "DeviceEventManager";

}

}

了解 Android 与 React Native 通信交互的朋友 看到 DeviceEventManagerModule 不会感到陌生。getName 方法返回原生 Module 模块的名称。在该模块下定义了供 JS 端调用的方法(ReactMethod注释)。

mInvokeDefaultBackPressRunnable 为 Runnable 对象,在构造函数中,初始化了 mInvokeDefaultBackPressRunnable ,并在 run 方法中执行 backBtnHandler.invokeDefaultOnBackPressed();继续跟踪到 invokeDefaultBackPressHandler 函数,可以看到,在函数中通过获取 Application 实例,将 mInvokeDefaultBackPressRunnable 放在 UI 主线程队列中执行。

从构造函数中,可以看出 backBtnHandler 是 DefaultHardwareBackBtnHandler 的实例。接下来重点来看 backBtnHandler 中做了什么。

DefaultHardwareBackBtnHandler 的实现代码同样在node_modules/react-native/ReactAndroid/src/main/java/com.facebook.react.modules.core目录下:

DefaultHardwareBackBtnHandler

package com.facebook.react.modules.core;

/**

* Interface used by {@link DeviceEventManagerModule} to delegate hardware back button events. It's

* suppose to provide a default behavior since it would be triggered in the case when JS side

* doesn't want to handle back press events.

*/

public interface DefaultHardwareBackBtnHandler {

/**

* By default, all onBackPress() calls should not execute the default backpress handler and should

* instead propagate it to the JS instance. If JS doesn't want to handle the back press itself,

* it shall call back into native to invoke this function which should execute the default handler

* 默认情况下,所有onBackPress()调用都不应该执行默认的反向处理程序,而应该将其传播到JS实例。

* 如果JS不想处理反压本身,它应该回调为native来调用这个应该执行默认处理程序的函数

*/

void invokeDefaultOnBackPressed();

}

DefaultHardwareBackBtnHandler 被定义为 一个接口,其中声明了 invokeDefaultOnBackPressed 函数方法。具体的实现行为交给子类来实现。现在我们就需要跟踪代码,找到 DeviceEventManagerModule 是在哪里被初始化的。还记得我们在Android中实现完JS的桥接Module模块后,需要将其添加到Package,并在Application中注册。所以,我们需要找到Package,就能找到 DeviceEventManagerModule 初始化。

React Native 系统的基本 Module 的 Package 为:CoreModulesPackage,路径为:node_modules/react-native/ReactAndroid/src/main/java/com/facebook/react/CoreModulesPackage.java:

CoreModulesPackage

package com.facebook.react;

/**

* 这是支持React Native的基本模块。 调试模块现在位于DebugCorePackage中。

*/

@ReactModuleList(

nativeModules = {

AndroidInfoModule.class,

DeviceEventManagerModule.class,

DeviceInfoModule.class,

ExceptionsManagerModule.class,

HeadlessJsTaskSupportModule.class,

SourceCodeModule.class,

Timing.class,

UIManagerModule.class,

}

)

/* package */ class CoreModulesPackage extends LazyReactPackage implements ReactPackageLogger {

private final ReactInstanceManager mReactInstanceManager;

private final DefaultHardwareBackBtnHandler mHardwareBackBtnHandler;

CoreModulesPackage(

ReactInstanceManager reactInstanceManager,

DefaultHardwareBackBtnHandler hardwareBackBtnHandler,

boolean lazyViewManagersEnabled,

int minTimeLeftInFrameForNonBatchedOperationMs) {

mReactInstanceManager = reactInstanceManager;

mHardwareBackBtnHandler = hardwareBackBtnHandler;

mLazyViewManagersEnabled = lazyViewManagersEnabled;

mMinTimeLeftInFrameForNonBatchedOperationMs = minTimeLeftInFrameForNonBatchedOperationMs;

}

// .... 代码省略

@Override

public List getNativeModules(final ReactApplicationContext reactContext) {

return Arrays.asList(

ModuleSpec.nativeModuleSpec(

AndroidInfoModule.class,

new Provider() {

@Override

public NativeModule get() {

return new AndroidInfoModule(reactContext);

}

}),

ModuleSpec.nativeModuleSpec(

DeviceEventManagerModule.class,

new Provider() {

@Override

public NativeModule get() {

return new DeviceEventManagerModule(reactContext, mHardwareBackBtnHandler);

}

}),

ModuleSpec.nativeModuleSpec(

ExceptionsManagerModule.class,

new Provider() {

@Override

public NativeModule get() {

return new ExceptionsManagerModule(mReactInstanceManager.getDevSupportManager());

}

}),

ModuleSpec.nativeModuleSpec(

HeadlessJsTaskSupportModule.class,

new Provider() {

@Override

public NativeModule get() {

return new HeadlessJsTaskSupportModule(reactContext);

}

}),

ModuleSpec.nativeModuleSpec(

SourceCodeModule.class,

new Provider() {

@Override

public NativeModule get() {

return new SourceCodeModule(reactContext);

}

}),

ModuleSpec.nativeModuleSpec(

Timing.class,

new Provider() {

@Override

public NativeModule get() {

return new Timing(reactContext, mReactInstanceManager.getDevSupportManager());

}

}),

ModuleSpec.nativeModuleSpec(

UIManagerModule.class,

new Provider() {

@Override

public NativeModule get() {

return createUIManager(reactContext);

}

}),

ModuleSpec.nativeModuleSpec(

DeviceInfoModule.class,

new Provider() {

@Override

public NativeModule get() {

return new DeviceInfoModule(reactContext);

}

}));

}

// .... 代码省略

}

ReactInstanceManager

ReactInstanceManager 类代码较长,我们只贴核心部分:

/**

* 注册 Module

*/

synchronized (mPackages) {

PrinterHolder.getPrinter()

.logMessage(ReactDebugOverlayTags.RN_CORE, "RNCore: Use Split Packages");

mPackages.add(

new CoreModulesPackage(

this,

new DefaultHardwareBackBtnHandler() {

@Override

public void invokeDefaultOnBackPressed() {

ReactInstanceManager.this.invokeDefaultOnBackPressed();

}

},

lazyViewManagersEnabled,

minTimeLeftInFrameForNonBatchedOperationMs));

if (mUseDeveloperSupport) {

mPackages.add(new DebugCorePackage());

}

mPackages.addAll(packages);

}

/**

* 处理键盘返回事件

*/

private void invokeDefaultOnBackPressed() {

UiThreadUtil.assertOnUiThread();

if (mDefaultBackButtonImpl != null) {

mDefaultBackButtonImpl.invokeDefaultOnBackPressed();

}

}

/**

* Activity 获取焦点

*/

@ThreadConfined(UI)

public void onHostResume(Activity activity, DefaultHardwareBackBtnHandler defaultBackButtonImpl) {

UiThreadUtil.assertOnUiThread();

mDefaultBackButtonImpl = defaultBackButtonImpl;

onHostResume(activity);

}

首先在 mPackages 中添加基本的Module,在初始化 CoreModulesPackage 的代码中,我们发现,在第二个参数中直接创建了DefaultHardwareBackBtnHandler 的实例,并在 invokeDefaultOnBackPressed() 方法中调用了 ReactInstanceManager 的 invokeDefaultOnBackPressed() 方法, 在 invokeDefaultOnBackPressed() 方法中 调用了 mDefaultBackButtonImpl 的 invokeDefaultOnBackPressed()。而 mDefaultBackButtonImpl 的具体实现实例是在 onHostResume 方法中传入。onHostResume 是在 Activity 获取焦点时执行的代码,而 ReactActivity 的实现依赖了 ReactActivityDelegate,所以我们来看 ReactActivityDelegate 中的 onResume 代码

ReactActivityDelegate

protected void onResume() {

if (getReactNativeHost().hasInstance()) {

getReactNativeHost().getReactInstanceManager().onHostResume(

getPlainActivity(),

(DefaultHardwareBackBtnHandler) getPlainActivity());

}

if (mPermissionsCallback != null) {

mPermissionsCallback.invoke();

mPermissionsCallback = null;

}

}

private Activity getPlainActivity() {

return ((Activity) getContext());

}

在 onResume 方法中,可以看到 onHostResume 的第二个参数传入了 (DefaultHardwareBackBtnHandler) getPlainActivity()。getPlainActivity() 方法其实就是返回的 ReactActivity 实例。从这里可以推断,具体的实现应该是交给了加载 React Native 视图的容器类:ReactActivity。

ReactActivity

ReactActivity 类实现了两个接口:DefaultHardwareBackBtnHandler、PermissionAwareActivity,并实现了对应的方法。PermissionAwareActivity是处理权限相关的接口,此处我们不再深入赘述。来看 ReactActivity 是如何实现 DefaultHardwareBackBtnHandler 接口中 invokeDefaultOnBackPressed 方法的。

package com.facebook.react;

/**

* Base Activity for React Native applications.

*/

public abstract class ReactActivity extends Activity

implements DefaultHardwareBackBtnHandler, PermissionAwareActivity {

// .... 代码省略

@Override

public void onBackPressed() {

if (!mDelegate.onBackPressed()) {

super.onBackPressed();

}

}

@Override

public void invokeDefaultOnBackPressed() {

super.onBackPressed();

}

}

invokeDefaultOnBackPressed 方法中调用了super.onBackPressed(),即调用了父类 Activity 中的 onBackPressed 函数。onBackPressed 函数的作用是在 Android 中返回上一界面的,与 react-navigation 路由导航中的 goBack功能类似。到这里,我们最终可以得出结论:exitApp() 方法就是调用了 Native 层 ReactActivity 的 onBackPress 方法。

此时也就能解答文章开始时的问题了,通过 BackHandler.exitApp() 就可以完成在RN端跳转回原生层上一个Activity界面。同样,在纯React Native应用中,因为只有一个MainActivity(继承自ReactActivity),所以在 JS 端 代码调用 BackHandler.exitApp() 会直接执行 onBackPressed() ,完成退出当前App的操作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值