compose ->backhandler

2,148 阅读2分钟

拦截后退键

先分析非compose组件,再分析compose组件,其实原理是一样的

以前非compose的情况

首先Activity基类里对back键的拦截,代码如下

public boolean onKeyUp(int keyCode, KeyEvent event) {
    if (getApplicationInfo().targetSdkVersion
            >= Build.VERSION_CODES.ECLAIR) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.isTracking()
                && !event.isCanceled()) {
            onBackPressed();
            return true;
        }
    }
    return false;
}

然后ComponentActivity里复写了onBackPressed()方法

@MainThread
public void onBackPressed() {
    mOnBackPressedDispatcher.onBackPressed();
}

@NonNull
@Override
public final OnBackPressedDispatcher getOnBackPressedDispatcher() {
    return mOnBackPressedDispatcher;
}


private final OnBackPressedDispatcher mOnBackPressedDispatcher =
        new OnBackPressedDispatcher(new Runnable() {
            @Override
            public void run() {
                // Calling onBackPressed() on an Activity with its state saved can cause an
                // error on devices on API levels before 26. We catch that specific error and
                // throw all others.
                try {
                //这个就是回退fragment或者关闭activity
                    ComponentActivity.super.onBackPressed();
                } catch (IllegalStateException e) {
                    if (!TextUtils.equals(e.getMessage(),
                            "Can not perform this action after onSaveInstanceState")) {
                        throw e;
                    }
                }
            }
        });

可以看到它调用了OnBackPressedDispatcher的onBackpressed方法,下边贴出对应的方法

//构造方法,就是上边传的那个runnalbe,
public OnBackPressedDispatcher(@Nullable Runnable fallbackOnBackPressed) {
    mFallbackOnBackPressed = fallbackOnBackPressed;
}


//添加拦截回调
public void addCallback(@NonNull OnBackPressedCallback onBackPressedCallback) {
    addCancellableCallback(onBackPressedCallback);
}

//循环处理通过addCallback添加的回调,如果callbac是enable状态那么就交给它处理,完事直接return,也就是回调第一个处理,其他的就无视了。
public void onBackPressed() {
    Iterator<OnBackPressedCallback> iterator =
            mOnBackPressedCallbacks.descendingIterator();
    while (iterator.hasNext()) {
        OnBackPressedCallback callback = iterator.next();
        if (callback.isEnabled()) {
            callback.handleOnBackPressed();
            return;
        }
    }
    //默认的情况用户没有添加回调,那么走这里,这个就是构造方法里的那个runnable,系统自己处理
    if (mFallbackOnBackPressed != null) {
        mFallbackOnBackPressed.run();
    }
}

上边的代码简单明了,所以我们要拦截后退键自己处理,非常简单

可以动态修改callback的enable状态,想自己处理设置为ture,不想自己处理就设置false

OnBackPressedCallback callback=new OnBackPressedCallback(true) {
    @Override
    public void handleOnBackPressed() {
        //finish();
    }
};
getOnBackPressedDispatcher().addCallback(callback);

Fragment里拦截也一样,就是多个requireActivity()

requireActivity().onBackPressedDispatcher.addCallback(callback);

compose情况

如下组件就可以实现了,和普通的activity原理是一样的,就是enable参数和onBack回调方法分开了。

@Composable
public fun BackHandler(enabled: Boolean = true, onBack: () -> Unit) {
    // Safely update the current `onBack` lambda when a new one is provided
    val currentOnBack by rememberUpdatedState(onBack)
    // Remember in Composition a back callback that calls the `onBack` lambda
    val backCallback = remember {
    //这里又组合成了普通activity里的那个OnBackPressedCallback抽象类
        object : OnBackPressedCallback(enabled) {
            override fun handleOnBackPressed() {
                currentOnBack()
            }
        }
    }
    // On every successful composition, update the callback with the `enabled` value
    SideEffect {
        backCallback.isEnabled = enabled
    }
    val backDispatcher = checkNotNull(LocalOnBackPressedDispatcherOwner.current) {
        "No OnBackPressedDispatcherOwner was provided via LocalOnBackPressedDispatcherOwner"
    }.onBackPressedDispatcher
    //onBackPressedDispatcher就是activity里那个
    val lifecycleOwner = LocalLifecycleOwner.current
    DisposableEffect(lifecycleOwner, backDispatcher) {
        // Add callback to the backDispatcher
        backDispatcher.addCallback(lifecycleOwner, backCallback)
        // When the effect leaves the Composition, remove the callback
        onDispose {
            backCallback.remove()
        }
    }
}

看下LocalOnBackPressedDispatcherOwner.current的获取

public object LocalOnBackPressedDispatcherOwner {
    private val LocalOnBackPressedDispatcherOwner =
        compositionLocalOf<OnBackPressedDispatcherOwner?> { null }

    public val current: OnBackPressedDispatcherOwner?
        @Composable
        get() = LocalOnBackPressedDispatcherOwner.current
            ?: findOwner<OnBackPressedDispatcherOwner>(LocalContext.current)

可以看到其实最终获取的就是ComponentActivity

internal inline fun <reified T> findOwner(context: Context): T? {
    var innerContext = context
    while (innerContext is ContextWrapper) {
        if (innerContext is T) {
            return innerContext
        }
        innerContext = innerContext.baseContext
    }
    return null
}

上边忘了贴了ComponentActivity实现了OnBackPressedDispatcherOwner

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        OnBackPressedDispatcherOwner,