本文方针
Flutter集成到现有的Android使用中,并封装通讯结构,简单好用
Flutter集成到现有的Android使用中步骤
- 首要,创立Flutter module
- 为已存在 的Android 使用添加Flutter module 依靠
1.创立Flutter module
在做混合开发之前咱们首要要创立一个Flutter module项目.
假设你的Native项目是这样的: xxx/flutter_hybrid/Native项目
cd xxx/flutter_hybrid/
flutter create -t module flutter_module
上面的代码会切换到你的Android项目的上一级目录,并创立一个flutter模块
这是创立的flutter_module项目的目录结构
你会发现它里边包括.android 和 .ios,这两个文件夹是隐藏文件,也是这个flutter_module的宿主工程
- .android – flutter_module的Android宿主工程
- .ios – flutter_module的iOS宿主工程
- lib- flutter_module的Dart部分代码
- pubspec.yaml – flutter_module的项目依靠配置文件
由于宿主工程的存在,咱们这个flutter_module在不加额定的配置的情况下是能够独立运转的,经过安装了Flutter与Dart插件的AndroidStudio打开这个flutter_module项目,经过运转按钮是能够直接运转的
2.创立Native Android Studio项目(如果还没有)
新创立的 FlutterHybridAndroid项目和 flutter_module项目放在同一级
打开原生工程,照着下图操作
最低minSdkVersion 为16
minSdkVersion 16
添加Java8编译选项
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
implementation project(':flutter')
//for flutter
setBinding(new Binding([gradle: this])) // new
evaluate(new File( // new
settingsDir.parentFile, // new
'flutter_module/.android/include_flutter.groovy'// new
))
//可选,主要作用是能够在当时AS的Project下显现flutter_module以便利查看和编写Dart代码
include ':flutter_module'
project(':flutter_module').projectDir = new File('../flutter_module')
至此,咱们现已为咱们的Android项目添加了Flutter一切必要的依靠,接下来咱们来看怎么在java中调用Flutter模块
Andoid和Flutter通讯结构
准备工作现已完结,这个时分咱们要根据MethodChannel完成Flutter与Android通讯架构
Android端
1.界说IFlutterBridge顶层接口
/**
* Author: 崇奉年青
* Date: 2021-06-11 12:50
* Email: hydznsqk@163.com
* Des:以下界说的办法都是Flutter调用Android
*/
interface IFlutterBridge<P, Callback> {
/**
* 回来到上一页,一般用于Flutter点击回来按钮,然后封闭原生页面
*/
fun onBack(p: P?)
/**
* 去Android页面或许传递数据到Android这边
*/
fun goToNative(p: P)
/**
* 获取到Android这边的Header信息
*/
fun getHeaderParams(callback: Callback)
}
以上界说的接口里边的办法都是Flutter调用Android的
- fun onBack(p: P?) : 回来到上一页,一般用于Flutter点击回来按钮,然后封闭原生页面
- fun goToNative(p: P?) : 去Android页面或许传递数据到Android这边
- fun getHeaderParams(callback: Callback) : 获取到Android这边的Header信息
2.完成该IFlutterBridge接口
/**
* Author: 崇奉年青
* Date: 2021-06-11 12:54
* Email: hydznsqk@163.com
* Des: Flutter通讯桥梁,完成了MethodChannel.MethodCallHandler和IFlutterBridge接口
*/
class FlutterBridge : MethodChannel.MethodCallHandler, IFlutterBridge<Any?, MethodChannel.Result> {
//因多FlutterEngine后每个FlutterEngine需要独自注册一个MethodChannel,所以用调集将一切的MethodChannel保存起来
private var methodChannels = mutableListOf<MethodChannel>()
//单例
companion object {
@JvmStatic
var instance: FlutterBridge? = null
private set
@JvmStatic
fun init(flutterEngine: FlutterEngine): FlutterBridge? {
val methodChannel = MethodChannel(flutterEngine.dartExecutor, "FlutterBridge")
if (instance == null) {
FlutterBridge().also {
instance = it
}
}
methodChannel.setMethodCallHandler(instance)
//因多FlutterEngine后每个FlutterEngine需要独自注册一个MethodChannel,所以用调集将一切的MethodChannel保存起来
instance!!.apply {
methodChannels.add(methodChannel)
}
return instance
}
}
///////以下办法为Android调用Flutter/////////////////////////////////////////////////
/**
* Android调用flutter
*/
fun fire(method: String, argument: Any?) {
methodChannels.forEach {
it.invokeMethod(method, argument)
}
}
/**
* Android调用flutter
*/
fun fire(method: String, argument: Any, callback: MethodChannel.Result?) {
methodChannels.forEach {
it.invokeMethod(method, argument, callback)
}
}
///////以下办法为Flutter调用Android/////////////////////////////////////////////////
/**
* flutter调用原生
* 处理来自Dart的办法调用
*/
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"onBack" -> onBack(call.arguments)
"goToNative" -> goToNative(call.arguments)
"getHeaderParams" -> getHeaderParams(result)
else -> result.notImplemented()
}
}
/**
* 回来到上一页,一般用于Flutter点击回来按钮,然后封闭原生页面
*/
override fun onBack(p: Any?) {
if (ActivityManager.instance.getTopActivity(true) is MyFlutterActivity) {
(ActivityManager.instance.getTopActivity(true) as MyFlutterActivity).onBackPressed()
}
}
/**
* 去Android页面或许传递数据到Android这边
*/
override fun goToNative(p: Any?) {
if (p is Map<*, *>) {
val action = p["action"]
if (action == "goToDetail") {
val goodsId = p["goodsId"]
Toast.makeText(AppGlobals.get(),"产品ID="+goodsId,Toast.LENGTH_LONG).show();
} else if (action == "goToLogin") {
Toast.makeText(AppGlobals.get(),"去登录",Toast.LENGTH_LONG).show();
} else {
}
}
}
/**
* 获取到Android这边的Header信息
*/
override fun getHeaderParams(callback: MethodChannel.Result) {
val map = HashMap<String, String>()
map["boarding-pass"] = "boarding-pass"
map["auth-token"] = "auth-token"
callback.success(map)
}
}
Flutter通讯桥梁,完成了MethodChannel.MethodCallHandler和IFlutterBridge接口,并且是个单例,参数中承受FlutterEngine,并创立MethodChannel通讯途径,由于支撑多Flutter引擎,多FlutterEngine后每个FlutterEngine需要独自注册一个MethodChannel,所以用调集将一切的MethodChannel保存起来,
然后在override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) 办法中承受Flutter那儿的调用,
咱们要做的便是Android侧和Flutter侧两头的办法姓名保持一致
- onBack(p: Any?) 用于回来到上一页
- goToNative(p: Any?) 中的参数是个map,然后咱们能够界说几个key,首要是action,这个key表明要做的动作,举个例子,action为goToDetail表明去详情页,
action为goToLogin表明去登录页面,action界说好之后在界说详细要传递的值,比如说界说goodId这个key(用来接收从Flutter侧传过来的参数) - getHeaderParams(callback: MethodChannel.Result) 是获取Android侧这边的头信息,由于Flutter那儿也要进行网络恳求
3.创立Flutter引擎管理类
然后咱们这个时分需要创立Flutter引擎了,咱们这儿有两个需求
- 怎么让预加载不丢失”主页”功能?
- 怎么支撑多个Flutter引擎并别离加载不同的dart 进口?
/**
* Author: 崇奉年青
* Date: 2021-06-11 13:50
* Email: hydznsqk@163.com
* Des:Flutter优化提高加载速度,完成秒开Flutter模块
*/
class FlutterCacheManager private constructor() {
/**
* 伴生目标,保持单例
*/
companion object {
//喜爱页面,默许是flutter发动的主进口
const val MODULE_NAME_FAVORITE = "main"
//引荐页面
const val MODULE_NAME_RECOMMEND = "recommend"
@JvmStatic
@get:Synchronized
var instance: FlutterCacheManager? = null
get() {
if (field == null) {
field = FlutterCacheManager()
}
return field
}
private set
}
/**
* 空闲时分预加载Flutter
*/
fun preLoad(context: Context){
//在线程空闲时执行预加载任务
Looper.myQueue().addIdleHandler {
initFlutterEngine(context, MODULE_NAME_FAVORITE)
initFlutterEngine(context, MODULE_NAME_RECOMMEND)
false
}
}
fun hastCached(moduleName: String):Boolean{
return FlutterEngineCache.getInstance().contains(moduleName)
}
/**
* 初始化Flutter
*/
private fun initFlutterEngine(context: Context, moduleName: String): FlutterEngine {
val flutterLoader: FlutterLoader = FlutterInjector.instance().flutterLoader()
//flutter 引擎
val flutterEngine = FlutterEngine(context,flutterLoader, FlutterJNI())
//插件注册要紧跟引擎初始化之后,否则会有在dart中调用插件由于还未初始化完结而导致的时序问题
FlutterBridge.init(flutterEngine)
FImageViewPlugin.registerWith(flutterEngine)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint(
flutterLoader.findAppBundlePath(),
moduleName
)
)
//存到引擎缓存中
FlutterEngineCache.getInstance().put(moduleName,flutterEngine)
return flutterEngine
}
/**
* 获取缓存的flutterEngine
*/
fun getCachedFlutterEngine(context: Context?, moduleName: String):FlutterEngine{
var flutterEngine = FlutterEngineCache.getInstance()[moduleName]
if(flutterEngine==null && context!=null){
flutterEngine=initFlutterEngine(context,moduleName)
}
return flutterEngine!!
}
/**
* 毁掉FlutterEngine
*/
fun destroyCached(moduleName: String){
val map = FlutterEngineCache.getInstance()
if(map.contains(moduleName)){
map[moduleName]?.apply {
destroy()
}
map.remove(moduleName)
}
}
}
首要这个Flutter引擎管理类是怎么完成最开端的两个需求呢?
-
问题1: 怎么让预加载不丢失”主页”功能?
fun preLoad(context: Context) 该办法是在app发动的时分在Application中去调用的,咱们这儿是在在线程空闲时才去创立Flutter引擎 -
问题2: 怎么支撑多个Flutter引擎并别离加载不同的dart 进口?
咱们在初始化Flutter引擎的办法中界说一个moduleName的参数,该参数是在创立Flutter引擎的时分去使用,用以区分是哪个引擎,咱们在伴生目标中也有界说 flutter引擎发动的两个进口,main和recommend
4.创立FlutterFragment
通讯bridge和Flutter引擎管理类都创立好了,这个时分创立FlutterFragment,用以去加载详细的Flutter页面
/**
* Author: 崇奉年青
* Date: 2021-06-11 15:20
* Email: hydznsqk@163.com
* Des: fragment的基类
*/
public abstract class BaseFragment extends Fragment {
protected View mLayoutView;
@LayoutRes
public abstract int getLayoutId();
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
mLayoutView = inflater.inflate(getLayoutId(), container, false);
return mLayoutView;
}
public void showToast(String message) {
if (TextUtils.isEmpty(message)) {
Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show();
}
}
}
/**
* Author: 崇奉年青
* Date: 2021-06-11 14:32
* Email: hydznsqk@163.com
* Des:
*/
abstract class FlutterFragment(moduleName: String) : BaseFragment() {
private val flutterEngine: FlutterEngine?
private lateinit var flutterView: FlutterView
private val cached = FlutterCacheManager.instance!!.hastCached(moduleName)
init {
flutterEngine =
FlutterCacheManager.instance!!.getCachedFlutterEngine(AppGlobals.get(), moduleName)
}
override fun getLayoutId(): Int {
return R.layout.fragment_flutter
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
// 注册flutter/platform_views 插件以便能够处理native view
if(!cached){
flutterEngine?.platformViewsController?.attach(activity,flutterEngine.renderer,flutterEngine.dartExecutor)
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
(mLayoutView as ViewGroup).addView(createFlutterView(activity!!))
}
private fun createFlutterView(context: Context): FlutterView {
val flutterTextureView = FlutterTextureView(activity!!)
flutterView = FlutterView(context, flutterTextureView)
return flutterView
}
/**
* 设置标题
*/
fun setTitle(titleStr: String) {
rl_title.visibility = View.VISIBLE
title_line.visibility = View.VISIBLE
title.text = titleStr
}
/**
* 生命周期奉告flutter
*/
override fun onStart() {
flutterView.attachToFlutterEngine(flutterEngine!!)
super.onStart()
}
override fun onResume() {
super.onResume()
//for flutter >= v1.17
flutterEngine!!.lifecycleChannel.appIsResumed()
}
override fun onPause() {
super.onPause()
flutterEngine!!.lifecycleChannel.appIsInactive()
}
override fun onStop() {
super.onStop()
flutterEngine!!.lifecycleChannel.appIsPaused()
}
override fun onDetach() {
super.onDetach()
flutterEngine!!.lifecycleChannel.appIsDetached()
}
override fun onDestroy() {
super.onDestroy()
flutterView.detachFromFlutterEngine()
}
}
FlutterFragment很简单,便是承受一个moduleName的参数,然后在类初始化的时分去创立flutter引擎,然后fragment创立完毕后去添加FlutterView,这就把Flutter页面加载进来了
5.详细的fragment使用
/**
* Author: 崇奉年青
* Date: 2021-06-11 15:20
* Email: hydznsqk@163.com
* Des: 保藏页面
*/
class FavoriteFragment : FlutterFragment(FlutterCacheManager.MODULE_NAME_FAVORITE) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setTitle(getString(R.string.title_favorite))
//点击标题,Android 调用Flutter,然后Flutter回来给Android
title.setOnClickListener {
FlutterBridge.instance!!.fire(
"onRefreshFavorite",
"我是保藏的参数",
object : MethodChannel.Result {
override fun notImplemented() {
Toast.makeText(context, "dart那儿未完成", Toast.LENGTH_LONG).show()
}
override fun error(
errorCode: String?,
errorMessage: String?,
errorDetails: Any?
) {
Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show()
}
override fun success(result: Any?) {
if (result != null) {
Toast.makeText(context, result as String, Toast.LENGTH_LONG).show()
}
}
})
}
}
}
/**
* Author: 崇奉年青
* Date: 2021-06-11 15:20
* Email: hydznsqk@163.com
* Des: 引荐页面
*/
class RecommendFragment : FlutterFragment(FlutterCacheManager.MODULE_NAME_RECOMMEND) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setTitle(getString(R.string.title_recommend))
title.setOnClickListener {
FlutterBridge.instance!!.fire(
"onRefreshRecommend",
"我是引荐的参数",
object : MethodChannel.Result {
override fun notImplemented() {
Toast.makeText(context, "dart那儿未完成", Toast.LENGTH_LONG).show()
}
override fun error(
errorCode: String?,
errorMessage: String?,
errorDetails: Any?
) {
Toast.makeText(context, errorMessage, Toast.LENGTH_LONG).show()
}
override fun success(result: Any?) {
if (result != null) {
Toast.makeText(context, result as String, Toast.LENGTH_LONG).show()
}
}
})
}
}
}
至此,Android这边的类根本创立完毕
Flutter端
1.创立FlutterBridge
import 'package:flutter/services.dart';
class FlutterBridge {
static FlutterBridge _instance = FlutterBridge._();
//该称号还要Android侧的保持一致
MethodChannel _bridge = const MethodChannel("FlutterBridge");
var _listenerMap = {}; //map key:String value:MethodCall办法
var header;
FlutterBridge._() {
_bridge.setMethodCallHandler((MethodCall call) {
String method = call.method;
if (_listenerMap[method] != null) {
return _listenerMap[method](call);
}
return null;
});
}
static FlutterBridge getInstance() {
return _instance;
}
///////以下办法为Android调用Flutter///////////////////////////////////////////////
///注册
void register(String method, Function(MethodCall) callBack) {
_listenerMap[method] = callBack;
}
///解除注册
void unRegister(String method) {
if (_listenerMap.containsKey(method)) {
_listenerMap.remove(method);
}
}
///////以下办法为Flutter调用Android/////////////////////////////////////////////////
///去Android页面或许传递数据到Android这边
void goToNative(Map params) {
_bridge.invokeMethod("goToNative", params);
}
///回来到上一页,一般用于Flutter点击回来按钮,然后封闭原生页面
void onBack(Map params) {
_bridge.invokeMethod("onBack", params);
}
///获取到Android这边的Header信息
Future<Map<String, String>> getHeaderParams() async {
Map header = await _bridge.invokeMethod("getHeaderParams", {});
return this.header = Map<String, String>.from(header);
}
MethodChannel bridge() {
return _bridge;
}
}
很简单,便是创立MethodChannel通讯类,然后其途径参数姓名要和Android侧保持一致,在这儿都为FlutterBridge,
然后界说register和unRegister办法,这些办法用于Android调用Flutter的时分,Flutter这边来接收信息,当然也支撑回来给Android侧
然后一开端咱们在Android侧界说了3个办法,goToNative,onBack,getHeaderParams这个时分要在Flutter侧保持一致,这几个办法是Flutter调用Android用的
2.创立详细的Flutter页面
首要是FavoritePage 保藏页面
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_module/bridge/flutter_bridge.dart';
class FavoritePage extends StatefulWidget {
@override
_FavoritePageState createState() => _FavoritePageState();
}
class _FavoritePageState extends State<FavoritePage> {
@override
bool get wantKeepAlive => true; //保活,切换tab的时分不会从头改写页面
@override
void initState() {
_registerEvent();
super.initState();
}
var arguments;
///注册事情,登录成功后会发射事情到这儿
void _registerEvent() {
var bridge = FlutterBridge.getInstance();
//监听onRefresh音讯,登录的时分和点击当时页面标题的时分会发射到这儿,然后恳求数据进行改写
bridge.register("onRefreshFavorite", (MethodCall call) {
setState(() {
arguments = call.arguments;
});
return Future.value("Flutter 收到,我是保藏");
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
children: [
Text(
"保藏---${arguments}",
style: TextStyle(fontSize: 20),
),
MaterialButton(onPressed: (){
var map = {"action":"goToDetail","goodsId":123456};
FlutterBridge.getInstance().goToNative(map);
},child: Text("Flutter 调用 Android"),)
],
),
),
);
}
@override
void dispose() {
super.dispose();
FlutterBridge.getInstance().unRegister("onRefreshFavorite");
}
}
然后是引荐页面
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_module/bridge/flutter_bridge.dart';
class RecommendPage extends StatefulWidget {
@override
_RecommendPageState createState() => _RecommendPageState();
}
class _RecommendPageState extends State<RecommendPage> {
@override
bool get wantKeepAlive => true; //保活,切换tab的时分不会从头改写页面
@override
void initState() {
_registerEvent();
super.initState();
}
var arguments;
///注册事情,登录成功后会发射事情到这儿
void _registerEvent() {
var bridge = FlutterBridge.getInstance();
//监听onRefresh音讯,登录的时分和点击当时页面标题的时分会发射到这儿,然后恳求数据进行改写
bridge.register("onRefreshRecommend", (MethodCall call) {
setState(() {
arguments = call.arguments;
});
return Future.value("Flutter 收到,我是引荐");
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: Column(
children: [
Text(
"引荐---${arguments}",
style: TextStyle(fontSize: 20),
),
MaterialButton(onPressed: (){
var map = {"action":"goToLogin"};
FlutterBridge.getInstance().goToNative(map);
},child: Text("Flutter 调用 Android"),)
],
),
),
);
}
@override
void dispose() {
super.dispose();
FlutterBridge.getInstance().unRegister("onRefreshRecommend");
}
}
3.Flutter程序进口
import 'package:flutter/material.dart';
import 'package:flutter_module/page/favorite_page.dart';
import 'package:flutter_module/page/native_page.dart';
import 'package:flutter_module/page/recommend_page.dart';
//至少要有一个进口,并且这下面的man() 和 recommend()函数姓名 要和FlutterCacheManager中界说的对应上
void main() => runApp(MyApp(FavoritePage()));
@pragma('vm:entry-point')
void recommend() => runApp(MyApp(RecommendPage()));
class MyApp extends StatelessWidget {
final Widget page;
const MyApp(this.page);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Scaffold(
body: page,
),
);
}
}
在这儿,至少要有一个进口,并且man() 和 recommend()函数姓名 要和Android侧FlutterCacheManager中界说的对应上
到这儿就能够彻底进行通讯了,详细能够参阅demo