当前位置: 首页 > news >正文

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参

前言

本文主要讲解 Flutter 和 Android原生之间,页面相互跳转、传参,

但其中用到了两端相互通信的知识,非常建议先看完这篇 讲解通信的文章

Flutter 与 Android原生 相互通信:BasicMessageChannel、MethodChannel、EventChannel_flutter eventchannel methodchannel basemessagechan-CSDN博客

当前案例 Flutter SDK版本:3.13.2

Flutter使用多个轻量型引擎,进行混合开发,是从 2.0 开始的,大大的减轻了内存压力;

轻量型引擎开发-官方介绍视频:https://www.youtube.com/watch?v=p6cK_0jp2ag

Flutter和原生端的关系

混合路由栈

如果是纯Flutter开发只会有一个Flutter引擎,如果是混合开发,原生端 需要为每个从原生端跳转的Flutter页面创建独立的引擎 (也可以单例,但单例写法需要处理一些问题,这个到 FlutterEngineGroup 章节会具体讲解);

比如:

1.0 从 Android_A页面  ==== 跳转到 ==== Flutter_A页面,Android端需要为Flutter_A页面,创建Flutter引擎;

1.1 紧接着,从 Flutter_A页面 ==== 跳转到 ==== Flutter_B页面,就不需要,它俩共用一个引擎;

1.2 每个Flutter引擎都有自己的路由栈,且这个路由栈只能管理Flutter页面

1.3 使用Flutter提供的 Navigator.pop(context); 方法,可以从 Flutter_B页面 回退到 Flutter_A页面,但无法从 Flutter_A页面 回退到 Android_A,会黑屏,因为Flutter栈里面没有Android页面,可以使用 Navigator.canPop(context); 来检查Flutter路由栈中,是否还有其他路由;

1.4 如果不使用 Navigator.pop(context); 回退方法,使用手机自带的 Back按键 / 左滑屏幕 进行回退,是没有问题的,因为这种回退方式调用的是原生API,Android原生不光提供FlutterView渲染Flutter页面结果,还会创建FlutterActivityFlutterView进行绑定;

1.5 看到这,大家应该理解,为什么说Flutter只是UI框架了吧;

Android 和 Flutter 跳转

Android 跳转 Flutter

Flutter 跳转 Android

效果图

Android代码

FlutterRouterManager.kt

package com.example.flutter_nav_android.utilimport android.app.Activity
import io.flutter.embedding.android.FlutterActivity
import io.flutter.embedding.android.FlutterActivityLaunchConfigs
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.embedding.engine.dart.DartExecutorclass FlutterRouterManager(val targetRoute: String,val mEngineId: String,val mContext: Activity
) {var mEngine: FlutterEngine? = nullinit {createCachedEngine()}companion object {/*** 获取缓存中的 Flutter引擎*/@JvmStaticfun getEngineCacheInstance(engineId: String): FlutterEngine? {return FlutterEngineCache.getInstance().get(engineId)}}/*** 1、创建Flutter引擎* 2、将初始命名路由,修改为目标页面路由* 3、缓存Flutter引擎*/fun createCachedEngine(): FlutterEngine {val flutterEngine = FlutterEngine(mContext) // 创建Flutter引擎// 将初始命名路由,修改为目标页面路由flutterEngine.navigationChannel.setInitialRoute(targetRoute)// 这一步,是在执行相关Dart文件入口的 main函数,将Flutter页面渲染出结果// 原生端获取结果,进行最终渲染上屏flutterEngine.dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())// 将加载好的引擎,存储起来FlutterEngineCache.getInstance().put(mEngineId, flutterEngine)mEngine = flutterEnginereturn flutterEngine}/*** 根据引擎ID,前往指定的Flutter页面*/fun push() {// 创建新的引擎(了解即可)// mContext.startActivity(//    FlutterActivity//        .withNewEngine() // 创建引擎//        .backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent) // 背景改为透明,不然切换页面时,会闪烁黑色//        .build(mContext))// 使用缓存好的引擎(推荐)mContext.startActivity(FlutterActivity.withCachedEngine(mEngineId) // 获取缓存好的引擎.backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent) // 背景改为透明,不然切换页面时,会闪烁黑色.build(mContext))}/*** 销毁当前Flutter引擎*/fun destroy() {mEngine?.destroy()}}

PersonalActivity.kt

package com.example.flutter_nav_android.ui.activityimport android.graphics.Color
import android.os.Bundle
import android.text.SpannableString
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.example.flutter_nav_android.databinding.ActivityPersonalBinding
import com.example.flutter_nav_android.util.FlutterRouterManager
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannelclass PersonalActivity : AppCompatActivity(), MethodChannel.MethodCallHandler, View.OnClickListener {private lateinit var bind: ActivityPersonalBindingprivate lateinit var homeFlutterEngine: FlutterEngineprivate lateinit var loginRouterManager: FlutterRouterManagerprivate lateinit var loginMethodChannel: MethodChannelprivate lateinit var homeMethodChannel: MethodChannelprivate val METHOD_CHANNEL_LOGIN = "com.example.flutter_nav_android/login/method"private val METHOD_CHANNEL_HOME = "com.example.flutter_nav_android/home/method"private val NAV_FLUTTER_LOGIN_NOTICE = "navFlutterLoginNotice"private val POP_NOTICE = "popNotice"private val PERSONAL_POP_NOTICE = "personalPopNotice"override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)bind = ActivityPersonalBinding.inflate(layoutInflater)setContentView(bind.root)initView()loginRouterManager = FlutterRouterManager("/login", "login_engine", this)// 两端建立通信loginMethodChannel = MethodChannel(loginRouterManager.mEngine!!.dartExecutor, METHOD_CHANNEL_LOGIN)loginMethodChannel.setMethodCallHandler(this)// 获取 Flutter Home页面的引擎,并且建立通信homeFlutterEngine = FlutterRouterManager.getEngineCacheInstance("home_engine")!!homeMethodChannel = MethodChannel(homeFlutterEngine.dartExecutor,METHOD_CHANNEL_HOME)}/*** 监听来自 Flutter端 的消息通道** call: Android端 接收到 Flutter端 发来的 数据对象* result:Android端 给 Flutter端 执行回调的接口对象*/override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {val methodName: String = call.methodwhen (methodName) { // 销毁 Flutter Login 页面POP_NOTICE -> {val age = call.argument<Int>("age")getResult(age.toString())loginRouterManager.mEngine!!.navigationChannel.popRoute()}else -> {result.notImplemented()}}}override fun onClick(v: View?) {when (v) {bind.toFlutter -> { // 前往 Flutter Login 页面val map: MutableMap<String, String> = mutableMapOf<String, String>()map["name"] = "老王"loginMethodChannel.invokeMethod(NAV_FLUTTER_LOGIN_NOTICE,map)loginRouterManager.push()}bind.pop -> { // 销毁 Android Personal 页面val map: MutableMap<String, Int> = mutableMapOf<String, Int>()map["age"] = 18homeMethodChannel.invokeMethod(PERSONAL_POP_NOTICE,map)finish()}}}/*** 初始化页面*/private fun initView() {bind.toFlutter.setOnClickListener(this)bind.pop.setOnClickListener(this)var name = intent.getStringExtra("name")val title = "接收初始化参数:"val msg = title + nameval ss = SpannableString(msg)ss.setSpan(ForegroundColorSpan(Color.RED),title.length,msg.length,Spanned.SPAN_INCLUSIVE_EXCLUSIVE)bind.initV.text = ss}/*** 获取上一页的返回参数*/private fun getResult(age: String) {val title = "接收上一页返回参数:"val msg = title + ageval ss = SpannableString(msg)ss.setSpan(ForegroundColorSpan(Color.RED),title.length,msg.length,Spanned.SPAN_INCLUSIVE_EXCLUSIVE)bind.resultV.text = ss}override fun onDestroy() {super.onDestroy()loginRouterManager.destroy()}}

SchoolActivity.kt

package com.example.flutter_nav_android.ui.activityimport android.content.Intent
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.example.flutter_nav_android.databinding.ActivitySchoolBinding
import com.example.flutter_nav_android.util.FlutterRouterManager
import io.flutter.embedding.android.FlutterFragment
import io.flutter.embedding.android.TransparencyMode
import io.flutter.embedding.engine.FlutterEngineCache
import io.flutter.plugin.common.MethodChannelclass SchoolActivity : AppCompatActivity() {private lateinit var bind: ActivitySchoolBindingprivate lateinit var bookFragment: FlutterFragmentprivate lateinit var studentFragment: FlutterFragmentprivate val METHOD_CHANNEL_BOOK = "com.example.flutter_nav_android/book/method"private val METHOD_CHANNEL_STUDENT = "com.example.flutter_nav_android/student/method"private val NAV_FLUTTER_BOOK_NOTICE = "navFlutterBookNotice"private val NAV_FLUTTER_STUDENT_NOTICE = "navFlutterStudentNotice"override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)bind = ActivitySchoolBinding.inflate(layoutInflater)setContentView(bind.root)initView()initChannel()}/*** 建立通信*/private fun initChannel() {val bookEngine = FlutterRouterManager.getEngineCacheInstance("book_engine")val bookChannel = MethodChannel(bookEngine!!.dartExecutor,METHOD_CHANNEL_BOOK)val map: MutableMap<String, String> = mutableMapOf<String, String>()map["title"] = "Book"bookChannel.invokeMethod(NAV_FLUTTER_BOOK_NOTICE,map)val studentEngine = FlutterRouterManager.getEngineCacheInstance("student_engine")val studentChannel = MethodChannel(studentEngine!!.dartExecutor,METHOD_CHANNEL_STUDENT)val map2: MutableMap<String, String> = mutableMapOf<String, String>()map2["title"] = "Student"studentChannel.invokeMethod(NAV_FLUTTER_STUDENT_NOTICE,map2)}/*** 初始化页面*/private fun initView() {bookFragment = FlutterFragment.withCachedEngine("book_engine").transparencyMode(TransparencyMode.transparent) // 背景透明,避免切换页面,出现闪烁.shouldAttachEngineToActivity(false) // 是否让Flutter控制Activity,true:可以 false:不可以,默认值 true.build()supportFragmentManager.beginTransaction().add(bind.bookFragment.id, bookFragment).commit()studentFragment = FlutterFragment.withCachedEngine("student_engine").transparencyMode(TransparencyMode.transparent) // 背景透明,避免切换页面,出现闪烁.shouldAttachEngineToActivity(false) // 是否让Flutter控制Activity,true:可以 false:不可以,默认值 true.build()supportFragmentManager.beginTransaction().add(bind.studentFragment.id, studentFragment).commit()}// ================================ 这些是固定写法,Flutter需要这些回调 ================================override fun onPostResume() {super.onPostResume()bookFragment.onPostResume()studentFragment.onPostResume()}override fun onNewIntent(intent: Intent) {super.onNewIntent(intent)bookFragment.onNewIntent(intent)studentFragment.onNewIntent(intent)}override fun onBackPressed() {bookFragment.onBackPressed()studentFragment.onBackPressed()}override fun onRequestPermissionsResult(requestCode: Int,permissions: Array<String?>,grantResults: IntArray) {super.onRequestPermissionsResult(requestCode, permissions, grantResults)bookFragment.onRequestPermissionsResult(requestCode,permissions,grantResults)studentFragment.onRequestPermissionsResult(requestCode,permissions,grantResults)}override fun onUserLeaveHint() {bookFragment.onUserLeaveHint()studentFragment.onUserLeaveHint()}override fun onTrimMemory(level: Int) {super.onTrimMemory(level)bookFragment.onTrimMemory(level)studentFragment.onTrimMemory(level)}}

MainActivity.kt

package com.example.flutter_nav_android.ui.activityimport android.content.Intent
import android.graphics.Color
import android.text.SpannableString
import android.text.Spanned
import android.text.style.ForegroundColorSpan
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.example.flutter_nav_android.databinding.ActivityMainBinding
import com.example.flutter_nav_android.util.FlutterRouterManager
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannelclass MainActivity : AppCompatActivity(), MethodChannel.MethodCallHandler, View.OnClickListener {private lateinit var bind: ActivityMainBindingprivate lateinit var homeMethodChannel: MethodChannelprivate val METHOD_CHANNEL_HOME = "com.example.flutter_nav_android/home/method"private val NAV_ANDROID_PERSONAL_NOTICE = "navAndroidPersonalNotice"private val NAV_FLUTTER_HOME_NOTICE = "navFlutterHomeNotice"private val POP_NOTICE = "popNotice"private lateinit var homeRouterManager: FlutterRouterManagerprivate lateinit var bookRouterManager: FlutterRouterManagerprivate lateinit var studentRouterManager: FlutterRouterManageroverride fun onCreate(savedInstanceState: android.os.Bundle?) {super.onCreate(savedInstanceState)bind = ActivityMainBinding.inflate(layoutInflater)setContentView(bind.root)bind.toFlutter.setOnClickListener(this)bind.toFlutterFragment.setOnClickListener(this)homeRouterManager = FlutterRouterManager("/home", "home_engine", this)// 两端建立通信homeMethodChannel = MethodChannel(homeRouterManager.mEngine!!.dartExecutor,METHOD_CHANNEL_HOME)homeMethodChannel.setMethodCallHandler(this)// 这里Fragment案例的bookRouterManager = FlutterRouterManager("/book", "book_engine", this)studentRouterManager = FlutterRouterManager("/student", "student_engine", this)}/*** 监听来自 Flutter端 的消息通道** call: Android端 接收到 Flutter端 发来的 数据对象* result:Android端 给 Flutter端 执行回调的接口对象*/override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {val methodName: String = call.methodwhen (methodName) {NAV_ANDROID_PERSONAL_NOTICE -> { // Flutter Home 页面 前往 Android Personal 页面val intent = Intent(this, PersonalActivity::class.java)intent.putExtra("name",call.argument<String>("name"))startActivity(intent)}POP_NOTICE -> { // 销毁 Flutter Home 页面val age = call.argument<Int>("age")getResult(age.toString())homeRouterManager.mEngine!!.navigationChannel.popRoute()}else -> {result.notImplemented()}}}override fun onClick(v: View?) {when (v) {bind.toFlutter -> { // 前往 Flutter Home 页面val map: MutableMap<String, String> = mutableMapOf<String, String>()map["name"] = "张三"homeMethodChannel.invokeMethod(NAV_FLUTTER_HOME_NOTICE,map)homeRouterManager.push()}bind.toFlutterFragment -> {val intent = Intent(this, SchoolActivity::class.java)startActivity(intent)}}}/*** 获取上一页的返回参数*/private fun getResult(age: String) {val title = "接收上一页返回参数:"val msg = title + ageval ss = SpannableString(msg)ss.setSpan(ForegroundColorSpan(Color.RED),title.length,msg.length,Spanned.SPAN_INCLUSIVE_EXCLUSIVE)bind.resultV.text = ss}override fun onDestroy() {super.onDestroy()homeRouterManager.destroy()bookRouterManager.destroy()studentRouterManager.destroy()}}

activity_personal.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><data></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/app_bar"android:layout_width="match_parent"android:layout_height="60dp"android:layout_marginBottom="16dp"android:background="@color/cardview_shadow_start_color"android:gravity="center_vertical"android:paddingStart="16dp"android:text="Android Personal"android:textColor="@color/material_dynamic_primary60"android:textSize="26sp"android:textStyle="bold"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/init_v"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center_vertical"android:text="接收初始化参数:"android:textColor="@color/material_dynamic_primary60"android:textSize="20sp"android:textStyle="bold"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/result_v"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:text="接收上一页返回参数:"android:textColor="@color/material_dynamic_primary60"android:textSize="20sp"android:textStyle="bold"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@id/init_v" /><Buttonandroid:id="@+id/to_flutter"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:text="前往 Flutter Login"android:textSize="20sp"android:textAllCaps="false"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@id/result_v" /><Buttonandroid:id="@+id/pop"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="16dp"android:text="返回 上一页"android:textSize="20sp"android:textAllCaps="false"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toBottomOf="@id/to_flutter" /></androidx.constraintlayout.widget.ConstraintLayout></layout>

activity_school.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"><data></data><LinearLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:paddingTop="20dp"android:orientation="vertical"><TextViewandroid:id="@+id/app_bar"android:layout_width="match_parent"android:layout_height="60dp"android:background="@color/cardview_shadow_start_color"android:gravity="center_vertical"android:paddingStart="16dp"android:text="Android School"android:textColor="@color/material_dynamic_primary60"android:textSize="26sp"android:textStyle="bold" /><FrameLayoutandroid:id="@+id/book_fragment"android:layout_weight="1"android:layout_width="match_parent"android:layout_height="0dp" /><FrameLayoutandroid:id="@+id/student_fragment"android:layout_weight="1"android:layout_width="match_parent"android:layout_height="0dp" /></LinearLayout>
</layout>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"><data></data><androidx.constraintlayout.widget.ConstraintLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><TextViewandroid:id="@+id/app_bar"android:layout_width="match_parent"android:layout_height="60dp"android:layout_marginBottom="16dp"android:background="@color/cardview_shadow_start_color"android:gravity="center_vertical"android:paddingStart="16dp"android:text="Android Main"android:textColor="@color/material_dynamic_primary60"android:textSize="26sp"android:textStyle="bold"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/result_v"android:layout_width="wrap_content"android:layout_height="wrap_content"android:gravity="center_vertical"android:text="接收上一页返回参数:"android:textColor="@color/material_dynamic_primary60"android:textSize="20sp"android:textStyle="bold"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintBottom_toBottomOf="parent"/><Buttonandroid:id="@+id/to_flutter"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:text="前往 Flutter Home"android:textSize="20sp"android:textAllCaps="false"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toBottomOf="@id/result_v"app:layout_constraintRight_toRightOf="parent" /><Buttonandroid:id="@+id/to_flutter_fragment"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:text="前往 Flutter Fragment"android:textSize="20sp"android:textAllCaps="false"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toBottomOf="@id/to_flutter"app:layout_constraintRight_toRightOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout></layout>

AndroidManifest.xml

<manifest xmlns:android="http://schemas.android.com/apk/res/android"><applicationandroid:name="${applicationName}"android:icon="@mipmap/ic_launcher"android:label="flutter_nav_android"android:theme="@style/AppTheme"><activityandroid:name="io.flutter.embedding.android.FlutterActivity"android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"android:hardwareAccelerated="true"android:windowSoftInputMode="adjustResize" /><activity android:name=".ui.activity.SchoolActivity" android:exported="true"/><activity android:name=".ui.activity.PersonalActivity" android:exported="true"/><activityandroid:name=".ui.activity.MainActivity"android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"android:exported="true"android:hardwareAccelerated="true"android:launchMode="singleTop"android:windowSoftInputMode="adjustResize"><!-- Specifies an Android theme to apply to this Activity as soon asthe Android process has started. This theme is visible to the userwhile the Flutter UI initializes. After that, this theme continuesto determine the Window background behind the Flutter UI. --><meta-dataandroid:name="io.flutter.embedding.android.NormalTheme"android:resource="@style/NormalTheme" /><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><!-- Don't delete the meta-data below.This is used by the Flutter tool to generate GeneratedPluginRegistrant.java --><meta-dataandroid:name="flutterEmbedding"android:value="2" /></application><!-- Required to query activities that can process text, see:https://developer.android.com/training/package-visibility?hl=en andhttps://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. --><queries><intent><action android:name="android.intent.action.PROCESS_TEXT" /><data android:mimeType="text/plain" /></intent></queries>
</manifest>

Flutter代码

book.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';class Book extends StatefulWidget {const Book({super.key});@overrideState<Book> createState() => _BookState();
}class _BookState extends State<Book> {String title = '';static const String METHOD_CHANNEL_BOOK = 'com.example.flutter_nav_android/book/method';static const String NAV_FLUTTER_BOOK_NOTICE = 'navFlutterBookNotice';@overridevoid initState() {super.initState();MethodChannel bookMethodChannel = const MethodChannel(METHOD_CHANNEL_BOOK);bookMethodChannel.setMethodCallHandler(methodHandler);}/// 监听来自 Android端 的消息通道/// Android端调用了函数,这个handler函数就会被触发Future<dynamic> methodHandler(MethodCall call) async {final String methodName = call.method;switch (methodName) {case NAV_FLUTTER_BOOK_NOTICE: // 进入当前页面{title = call.arguments['title'];setState(() {});return 0;}default:{return PlatformException(code: '-1',message: '未找到Flutter端具体实现函数',details: '具体描述'); // 返回给Android端}}}@overrideWidget build(BuildContext context) {return Scaffold(backgroundColor: Colors.amberAccent,body: Container(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,alignment: Alignment.center,child: Text('Flutter $title',style: const TextStyle(fontWeight: FontWeight.bold,color: Colors.red,fontSize: 20,),),),);}}

home.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';class Home extends StatefulWidget {const Home({super.key});@overrideState<Home> createState() => _HomeState();
}class _HomeState extends State<Home> {late MethodChannel homeMethodChannel;String name = '';String age = '';static const String METHOD_CHANNEL_HOME = 'com.example.flutter_nav_android/home/method';static const String NAV_ANDROID_PERSONAL_NOTICE = 'navAndroidPersonalNotice';static const String NAV_FLUTTER_HOME_NOTICE = 'navFlutterHomeNotice';static const String POP_NOTICE = 'popNotice';static const String PERSONAL_POP_NOTICE = 'personalPopNotice';@overridevoid initState() {super.initState();homeMethodChannel = const MethodChannel(METHOD_CHANNEL_HOME);homeMethodChannel.setMethodCallHandler(methodHandler);}/// 监听来自 Android端 的消息通道/// Android端调用了函数,这个handler函数就会被触发Future<dynamic> methodHandler(MethodCall call) async {final String methodName = call.method;switch (methodName) {case NAV_FLUTTER_HOME_NOTICE: // 进入当前页面{name = call.arguments['name'];setState(() {});return 0;}case PERSONAL_POP_NOTICE: // Android Personal 页面 销毁了{age = '${call.arguments['age']}';setState(() {});return 0;}default:{return PlatformException(code: '-1',message: '未找到Flutter端具体实现函数',details: '具体描述'); // 返回给Android端}}}/// 销毁当前页面popPage() {if (Navigator.canPop(context)) { // 检查Flutter路由栈中,是否还有其他路由Navigator.pop(context);} else {Map<String, int> map = {'age': 12};homeMethodChannel.invokeMethod(POP_NOTICE, map);}}/// 前往 Android Personal 页面navAndroidPersonal() {Map<String, String> map = {'name': '李四'};homeMethodChannel.invokeMethod(NAV_ANDROID_PERSONAL_NOTICE, map);}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(backgroundColor: Colors.blue,title: const Text('Flutter Home',style: TextStyle(fontWeight: FontWeight.w500,fontSize: 26,color: Colors.yellow),)),body: SizedBox(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,child: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [Padding(padding: const EdgeInsets.only(bottom: 16),child: RichText(text: TextSpan(text: '接收初始化参数:',style: const TextStyle(color: Colors.black, fontSize: 20),children: [TextSpan(text: name,style: const TextStyle(color: Colors.red,fontWeight: FontWeight.bold),)])),),Padding(padding: const EdgeInsets.only(bottom: 16),child: RichText(text: TextSpan(text: '接收上一页返回参数:',style: const TextStyle(color: Colors.black, fontSize: 20),children: [TextSpan(text: age,style: const TextStyle(color: Colors.red,fontWeight: FontWeight.bold),)])),),Padding(padding: const EdgeInsets.only(bottom: 8),child: ElevatedButton(onPressed: navAndroidPersonal,child: const Text('前往 Android Personal',style: TextStyle(fontSize: 20),),),),ElevatedButton(onPressed: popPage,child: const Text('返回 上一页',style: TextStyle(fontSize: 20),),),],),),);}
}

login.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';class Login extends StatefulWidget {const Login({super.key});@overrideState<Login> createState() => _LoginState();
}class _LoginState extends State<Login> {late MethodChannel loginMethodChannel;String name = '';final String METHOD_CHANNEL_LOGIN = 'com.example.flutter_nav_android/login/method';static const String NAV_FLUTTER_LOGIN_NOTICE = 'navFlutterLoginNotice';final String POP_NOTICE = 'popNotice';@overridevoid initState() {super.initState();loginMethodChannel = MethodChannel(METHOD_CHANNEL_LOGIN);loginMethodChannel.setMethodCallHandler(methodHandler);}/// 监听来自 Android端 的消息通道/// Android端调用了函数,这个handler函数就会被触发Future<dynamic> methodHandler(MethodCall call) async {final String methodName = call.method;switch (methodName) {case NAV_FLUTTER_LOGIN_NOTICE: // 进入当前页面{name = call.arguments['name'];setState(() {});return 0;}default:{return PlatformException(code: '-1', message: '未找到Flutter端具体实现函数', details: '具体描述'); // 返回给Android端}}}/// 销毁当前页面popPage() {if (Navigator.canPop(context)) { // 检查Flutter路由栈中,是否还有其他路由Navigator.pop(context);} else {Map<String, int> map = {'age': 28};loginMethodChannel.invokeMethod(POP_NOTICE, map);}}@overrideWidget build(BuildContext context) {return Scaffold(appBar: AppBar(backgroundColor: Colors.blue,title: const Text('Flutter Login',style: TextStyle(fontWeight: FontWeight.w500,fontSize: 26,color: Colors.yellow),)),body: SizedBox(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,child: Column(mainAxisAlignment: MainAxisAlignment.center,crossAxisAlignment: CrossAxisAlignment.center,children: [Padding(padding: const EdgeInsets.only(bottom: 16),child: RichText(text: TextSpan(text: '接收初始化参数:',style: const TextStyle(color: Colors.black, fontSize: 20),children: [TextSpan(text: name,style: const TextStyle(color: Colors.red,fontWeight: FontWeight.bold),)])),),ElevatedButton(onPressed: popPage,child: const Text('返回 上一页',style: TextStyle(fontSize: 20),),),],),),);}
}

student.dart

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';class Student extends StatefulWidget {const Student({super.key});@overrideState<Student> createState() => _StudentState();
}class _StudentState extends State<Student> {String title = '';static const String METHOD_CHANNEL_STUDENT = 'com.example.flutter_nav_android/student/method';static const String NAV_FLUTTER_STUDENT_NOTICE = 'navFlutterStudentNotice';@overridevoid initState() {super.initState();MethodChannel bookMethodChannel = const MethodChannel(METHOD_CHANNEL_STUDENT);bookMethodChannel.setMethodCallHandler(methodHandler);}/// 监听来自 Android端 的消息通道/// Android端调用了函数,这个handler函数就会被触发Future<dynamic> methodHandler(MethodCall call) async {final String methodName = call.method;switch (methodName) {case NAV_FLUTTER_STUDENT_NOTICE: // 进入当前页面{title = call.arguments['title'];setState(() {});return 0;}default:{return PlatformException(code: '-1',message: '未找到Flutter端具体实现函数',details: '具体描述'); // 返回给Android端}}}@overrideWidget build(BuildContext context) {return Scaffold(backgroundColor: Colors.cyan,body: Container(width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,alignment: Alignment.center,child: Text('Flutter $title',style: const TextStyle(fontWeight: FontWeight.bold,color: Colors.white,fontSize: 20,),),),);}}

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_nav_android/book.dart';
import 'package:flutter_nav_android/student.dart';import 'home.dart';
import 'login.dart';void main() {runApp(const MyApp());
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',debugShowCheckedModeBanner: false,theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple,),useMaterial3: true,),routes: {'/home': (context) => const Home(),'/login': (context) => const Login(),'/book': (context) => const Book(),'/student': (context) => const Student(),},initialRoute: '/home',);}
}

踩坑

  • 一个路由坑,Flutter 加载 根命名路由标识,默认是 " / ",如果你在Flutter端使用了 " / " 作为根路由页面,有时候,从Android 跳转 Flutter页面时,它会将 根路由页面 先push进栈,再push你的目标页面进栈,会多出一个页面
  • 所有我直接不用 " / " 标识,而使用页面对应的路由标识字符串,来指定根路由。
// 原来带坑的写法
// routes: {
//    '/': (context) => const Home(),
//    '/login': (context) => const Login(),
//    '/personal': (context) => const Personal()
// },
// initialRoute: '/',// 解决方式的写法
routes: {'/home': (context) => const Home(),'/login': (context) => const Login(),'/personal': (context) => const Personal()},
initialRoute: '/home',

奇技淫巧

在案例中,我使用的是 命名路由Channel方式 进行 页面跳转、传参,下面直接使用引擎进行 页面跳转、传参,但实用价值不高,因为弊端太大;

比如:

  • 只提供了List<String>类型,进行传参;
  • 目标Flutter页面内的widget,默认参数会全部失效;
  • Flutter的Widget默认参数,是由主文件内的 MaterialApp 组件提供的,一个Flutter应用,一般只会使用一个 MaterialApp,它代表返回一个App,如果将目标Flutter页面套上 MaterialApp ,可以解决这个默认参数问题,但会引发 路由问题
  • 不过也有适用场景:让每个Flutter页面完全独立,之间没有任何交互,比如跳转、数据共享等等,这样路由就不需要了,让每个Flutter页面都使用 MaterialApp,当作App和原生交互;

综上所述,大家将这种方式当作扩展知识就好了。

Android代码

class MainActivity : AppCompatActivity(), View.OnClickListener {... ... override fun onCreate(savedInstanceState: android.os.Bundle?) {super.onCreate(savedInstanceState)... ... val flutterEngine = FlutterEngine(this)// 定义参数val dartEntrypointArgs = mutableListOf<String>()dartEntrypointArgs.add("张三")// 这种方式传参数,会导致这个Flutter页面中的Widget默认参数,全部失效,// 这种只有 Dart主入口文件,比如main.dart的 main函数才能接收到参数// void main(List<String args>) {}// flutterEngine.dartExecutor.executeDartEntrypoint(//    DartExecutor.DartEntrypoint.createDefault(),//    dartEntrypointArgs// )// 这种可以指定页面接收,但需要在主入口文件里先声明       // void showPersonal(List<String> args) {}flutterEngine.dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint(FlutterInjector.instance().flutterLoader().findAppBundlePath(),"showPersonal"), // 找到目标Flutter页面提供的 暴露函数dartEntrypointArgs)FlutterEngineCache.getInstance().put("personal_engine", flutterEngine)}override fun onClick(v: View?) {... ... val map: MutableMap<String, String> = mutableMapOf<String, String>()map["name"] = "张三"startActivity(FlutterActivity.withCachedEngine("personal_engine") // 获取缓存好的引擎.backgroundMode(FlutterActivityLaunchConfigs.BackgroundMode.transparent) // 背景改为透明,不然切换页面时,会闪烁黑色.build(this))}}

Flutter代码

main.dart(主文件)

import 'package:flutter/material.dart';
import 'package:flutter_nav_android/personal.dart';void main(List<String> args) {debugPrint('args:$args');runApp(const MyApp());
}/// 注解说明文档:https://mrale.ph/dartvm/compiler/aot/entry_point_pragma.html
/// 注解:表明它可以在 AOT 模式下直接从本机或 VM 代码解析、分配或调用
@pragma("vm:entry-point")
void showPersonal(List<String> args) { // 在主文件入口暴露出来debugPrint('args:$args');runApp(Personal(title: args.first));
}class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {return MaterialApp(title: 'Flutter Demo',debugShowCheckedModeBanner: false,theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple,),useMaterial3: true,),);}
}

personal.dart(目标页面)

import 'package:flutter/material.dart';class Personal extends StatefulWidget {final String? title;const Personal({super.key,this.title});@overrideState<Personal> createState() => _PersonalState();
}class _PersonalState extends State<Personal> {/// 在这个页面中,使用的Widget 默认参数全部失效@overrideWidget build(BuildContext context) {return Container(color: Colors.white,width: MediaQuery.of(context).size.width,height: MediaQuery.of(context).size.height,alignment: Alignment.center,child: Directionality(textDirection: TextDirection.ltr,child: Text(widget.title ?? '',style: const TextStyle(color: Colors.lightBlue,fontSize: 30,),)),);}
}

FlutterEngineGroup

  • 如果你接受以上缺点,可以使用 FlutterEngineGroup,这个东西就是换成了单例写法,我们自己写单例,需要处理一个问题,比如这个需求:同时获取两个Flutter引擎,这里就要通过判空新建一个Flutter引擎;
  • 而 FlutterEngineGroup 已经处理了这个问题,自首个Flutter引擎起,后面多出的Flutter引擎,都是其子类,如果只剩下最后一个,那么这个Flutter引擎将和首个Flutter引擎性能特征相同;
  • FlutterEngineGroup官方文档:多个 Flutter 页面或视图 - Flutter 中文文档 - Flutter 中文开发者网站 - Flutter
  • 我个人体验上来说,不使用单例,性能也足够了,如果有很多混合的Flutter页面,那当我没说😐。
  • 没找到创建子类Flutter引擎的相关代码,是从注解中发现的

Debug 和 Release

我用真机做测试,发现在Debug模式下,第一次从 Android 跳转 Flutter 会出现黑屏现象,但 打完包 或在 Release模式 就看不出来了;

我们默认的运行模式就是 Debug模式,可以通过 修改IDE运行命令 --release,切换为 Release模式

源码地址

GitHub - LanSeLianMa/flutter_nav_android: Flutter 和 Android原生(Activity、Fragment)相互跳转、传参

相关文章:

Flutter 和 Android原生(Activity、Fragment)相互跳转、传参

前言 本文主要讲解 Flutter 和 Android原生之间&#xff0c;页面相互跳转、传参&#xff0c; 但其中用到了两端相互通信的知识&#xff0c;非常建议先看完这篇 讲解通信的文章&#xff1a; Flutter 与 Android原生 相互通信&#xff1a;BasicMessageChannel、MethodChannel、…...

Kubernetes基础(十一)-CNI网络插件用法和对比

1 CNI概述 1.1 什么是CNI&#xff1f; Kubernetes 本身并没有实现自己的容器网络&#xff0c;而是借助 CNI 标准&#xff0c;通过插件化的方式来集成各种网络插件&#xff0c;实现集群内部网络相互通信。 CNI&#xff08;Container Network Interface&#xff0c;容器网络的…...

yo!这里是单例模式相关介绍

目录 前言 特殊类设计 只能在堆上创建对象的类 1.方法一&#xff08;构造函数下手&#xff09; 2.方法二&#xff08;析构函数下手&#xff09; 只能在栈上创建对象的类 单例模式 饿汉模式实现 懒汉模式实现 后记 前言 在面向找工作学习c的过程中&#xff0c;除了基本…...

2023年上-未来几年我要做什么

1月份&#xff0c;离职。 2月份&#xff0c;春节休假回来&#xff0c;中旬去参加了一个月的瑜伽培训&#xff0c;学会了倒立、鹤蝉。。。。 3月份&#xff0c;瑜伽培训结束&#xff0c;开始收拾房子&#xff0c;并调研各类项目。 4月份&#xff0c;参与了朋友的区块链项目 …...

智能汽车竞赛摄像头处理(3)——动态阈值二值化(大津法)

前言 &#xff08;1&#xff09;在上一节中&#xff0c;我们学习了对图像的固定二值化处理&#xff0c;可以将原始图像处理成二值化的黑白图像&#xff0c;这里面的本质就是将原来的二维数组进行了处理&#xff0c;处理后的二维数组里的元素都是0和255两个值。 &#xff08;2…...

BGP协议

1.BGP相关概念 1.1 BGP的起源 不同自治系统&#xff08;路由域&#xff09;间路由交换与管理的需求推动了EGP的发展&#xff0c;但是EGP的算法简单&#xff0c;无法选路&#xff0c;从而被BGP取代。 自治系统&#xff1a;&#xff08;AS&#xff09; IGP&#xff1a;自治系统…...

一个完整工作流管理系统的组成部分

一个完整工作流管理系统的组成部分 一个完整的工作流管理系统通常由工作流引擎、工作流设计器、流程操作、工作流客户端程序、流程监控、表单设计器、与表单的集成以及与应用程序的集成八个部分组成。 一、工作流组成 1. 工作流引擎 工作流引擎作为工作流管理系统的核心部分&…...

鱼和熊掌如何兼得?一文解析RDS数据库存储架构升级

在2023年云栖大会上&#xff0c;阿里云数据库产品事业部负责人李飞飞在主题演讲中提到&#xff0c;瑶池数据库推出“DB存储”一体化能力&#xff0c;结合人工智能、机器学习、存储等方法和创新能力&#xff0c;实现Buffer Pool Extension能力和智能冷温热数据分层能力。在大会的…...

中科大计网学习记录笔记(五):协议层次和服务模型

前言&#xff1a; 学习视频&#xff1a;中科大郑烇、杨坚全套《计算机网络&#xff08;自顶向下方法 第7版&#xff0c;James F.Kurose&#xff0c;Keith W.Ross&#xff09;》课程 该视频是B站非常著名的计网学习视频&#xff0c;但相信很多朋友和我一样在听完前面的部分发现信…...

同构异机迁移方案2_目标服务器仅安装数据库软件scp物理文件

源端和目标端的数据库版本需要保持一致&#xff0c;补丁版本可以不一致&#xff0c;目标端磁盘空间不能小于源端空间&#xff0c;目标端只需要安装 Oracle 软件即可。 特别说明&#xff1a;本文档案例Oracle的安装路径不同&#xff0c;数据目录一致&#xff0c;采用scp的方式实…...

华为机考入门python3--(6)牛客6-质数因子

分类&#xff1a;质数、素数 知识点&#xff1a; 取余符号% 5%3 2 取整符号// 5//3 1 list中int元素转str map(str, list) 题目来自【牛客】 def prime_factors(n): """ 输入一个正整数n&#xff0c;输出它的所有质因子&#xff08;重复的也…...

11月最新版付费进群源码自动定位+开源

Nginx 1.22.1 php5.6 mysql5.6 数据库配置&#xff1a;/config/database.php 配置后台域名&#xff1a;config/extra/ip.php 设置伪静态thinkphp 后台账号88886666 密码12345 代码结构 关键代码剖析 <?php // ----------------------------------------------------…...

Python算法题集_旋转图像

Python算法题集_旋转图像 题目48&#xff1a;旋转图像1. 示例说明2. 题目解析- 题意分解- 优化思路- 测量工具 3. 代码展开1) 标准求解【矩阵复本】2) 改进版一【矩阵转置矩阵反转】3) 改进版二【四值旋转】 4. 最优算法 题目48&#xff1a;旋转图像 本文为Python算法题集之一…...

[ChatGPT们】ChatGPT 如何辅助编程初探

主页&#xff1a;元存储的博客 全文 9000 字&#xff0c; 原创请勿转载。 我没有写过诗&#xff0c;但有人说我的代码像诗一样优雅 -- 雷军 图片来源&#xff1a;https://www.bilibili.com/video/BV1zL411X7oS/ 1. 引言 作为一个程序员&#xff0c;我们不仅要熟悉各种编程语…...

深入Spring MVC的工作流程

深入Spring MVC的工作流程 在Spring MVC的面试问题中&#xff0c;常常被询问到的一个问题。Spring MVC的程序中&#xff0c;HTTP请求是如何从开始到结束被处理的。为了研究这个问题&#xff0c;我们将需要深入学习一下Spring MVC框架的核心过程和工作流程。 1. 启动请求生命周…...

我的数据结构c(给自己用的)

目录 顺序表&#xff1a; 链表&#xff1a; 栈&#xff1a; 队列&#xff1a; 我想在之后的大学数据结构课上需要自己写来做题&#xff0c;但每次都自己写&#xff0c;那太麻烦了&#xff0c;所以我就将这个博客来把所有的C语言的数据结构弄上去&#xff0c; 问我为什么不…...

使用Arcgis对欧洲雷达高分辨率降水数据重投影

当前需要使用欧洲高分辨雷达降水数据&#xff0c;但是这个数据的投影问题非常头疼。实际的投影应该长这样&#xff08;https://gist.github.com/kmuehlbauer/645e42a53b30752230c08c20a9c964f9?permalink_comment_id2954366https://gist.github.com/kmuehlbauer/645e42a53b307…...

[Python] scikit-learn中数据集模块介绍和使用案例

sklearn.datasets模块介绍 在scikit-learn中&#xff0c;可以使用sklearn.datasets模块中的函数来构建数据集。这个模块提供了用于加载和生成数据集的函数。 API Reference — scikit-learn 1.4.0 documentation 以下是一些常用的sklearn.datasets模块中的函数 load_iris() …...

Qt-互斥量-临界区-QMutex-QMutexLocker-QReadWriteLock

文章目录 1.QMutex2.QMutexLocker3.QReadWriteLock 在Qt中&#xff0c;互斥量&#xff08;Mutex&#xff09;是用于同步多线程访问共享资源的一种机制。临界区&#xff08;Critical Section&#xff09;是指一段必须由单个线程执行的代码区域&#xff0c;防止多个线程同时执行这…...

《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述(6)

接前一篇文章&#xff1a;《PCI Express体系结构导读》随记 —— 第II篇 第4章 PCIe总线概述&#xff08;5&#xff09; 4.1 PCIe总线的基础知识 与PCI总线不同&#xff0c;PCIe总线使用端到端的连接方式&#xff0c;在一条PCIe链路的两端只能各连接一个设备&#xff0c;这两个…...

7.4.分块查找

一.分块查找的算法思想&#xff1a; 1.实例&#xff1a; 以上述图片的顺序表为例&#xff0c; 该顺序表的数据元素从整体来看是乱序的&#xff0c;但如果把这些数据元素分成一块一块的小区间&#xff0c; 第一个区间[0,1]索引上的数据元素都是小于等于10的&#xff0c; 第二…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

为什么需要建设工程项目管理?工程项目管理有哪些亮点功能?

在建筑行业&#xff0c;项目管理的重要性不言而喻。随着工程规模的扩大、技术复杂度的提升&#xff0c;传统的管理模式已经难以满足现代工程的需求。过去&#xff0c;许多企业依赖手工记录、口头沟通和分散的信息管理&#xff0c;导致效率低下、成本失控、风险频发。例如&#…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

零基础在实践中学习网络安全-皮卡丘靶场(第九期-Unsafe Fileupload模块)(yakit方式)

本期内容并不是很难&#xff0c;相信大家会学的很愉快&#xff0c;当然对于有后端基础的朋友来说&#xff0c;本期内容更加容易了解&#xff0c;当然没有基础的也别担心&#xff0c;本期内容会详细解释有关内容 本期用到的软件&#xff1a;yakit&#xff08;因为经过之前好多期…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

计算机基础知识解析:从应用到架构的全面拆解

目录 前言 1、 计算机的应用领域&#xff1a;无处不在的数字助手 2、 计算机的进化史&#xff1a;从算盘到量子计算 3、计算机的分类&#xff1a;不止 “台式机和笔记本” 4、计算机的组件&#xff1a;硬件与软件的协同 4.1 硬件&#xff1a;五大核心部件 4.2 软件&#…...