code花旦 两个著眼大后端应用领域的控制技术网络平台
译者:季公亥_lsy
镜像:juejin.im/post/6844904194869051405
新闻稿:责任编辑已获季公亥_lsy许可刊登,转贴等请联络原作许可这首诗将简述 Android模块化的构架构筑 及 Flutter 和 Android怎样混和合作开发 (整座App多于主页是用原生植物Android顺利完成,其它网页都是导入以后的搞好的Flutter网页) ,主寄主流程由 Android 构筑,选用了模块化的构架构筑整座 App ,完全相同销售业务,相关联完全相同的 module 工程建设,销售业务间选用USB通讯 (ARouter) ,以 module 的形式混进 Flutter,透过 MethodChannel 和 Flutter 端展开网络设备等,且那些机能同时实现源代码开放源码,钟爱的爸爸妈妈能点选至 GitHub。https://github.com/persilee/android_ctrip
下列昌明会分成4个部份简述:
工程建设项目顺利完成的机能自动更新工程建设项目模块化内部结构预测工程建设项目机能详尽简述(所制习题)Android Flutter 混和合作开发工程建设项目顺利完成的机能自动更新
具体来说,他们却是透过两个音频来加速自动更新下工程建设项目顺利完成的机能和运转效用,如下表所示
https://www.bilibili.com/video/BV1W54y1B72U/看完音频后,只不过绝大部份机能和以后的 纯flutter工程建设项目 机能完全相同,而已主页追加了4个tab所推荐网页及携程网一楼和产业布局发生改变。
他们也可扫描器,加装新体验:
工程建设项目模块化内部结构预测
工程建设项目模版自动更新其次,预测梳理下工程建设项目内部结构,工程建设项目的内部结构大致如图,还有一些细枝末节的没有体现在图里:
销售业务工程建设
把具体独立的销售业务都拆分成单独的 module 减小工程建设项目的维护压力
ft_home: 主页模块,这个模块只不过还能继续拆分,可把4个 tab (精选、附近、景点、美食) 页都拆成模块,这里我暂时没有拆分,后续会顺利完成ft_destination: 目的地模块,只不过并没有建立这个模块,因为直接导入了以后搞好的 flutter 网页ft_travel: 旅拍模块,同样也使用了 flutter 网页flutter: flutter模块,这个模块是从 flutter_module 中自动生成的,后面介绍到基础库工程建设把具体的机能都封装成独立的库供销售业务模块使用,降低工程建设项目的维护成本及代码间耦合性
lib_network: 网络库,使用 okhttp 插件二次封装,销售业务层简单的调用即可lib_webview: 打开网页的webview库,使用了 agentweb 插件二次封装,销售业务层只需要一句代码即可顺利完成网页的跳转lib_image_loader: 图片加载库,使用了 glide 插件二次封装,销售业务层只需一句代码即可加载完全相同参数的图片lib_asr: 百度AI语音库,透过 Android 集成好供 Flutter 端使用lib_common_ui: 公共UI库,重复多次使用的网页集中管理lib_base: 基础库,透过 ARouter 的 service 机能暴露USB提供服务给销售业务层,当然销售业务层也能在这里暴露USB供外界使用这里有一些使用的插件并没有在工程建设项目模版里体现出来(模版空间有限)。
插件在这里把工程建设项目使用的插件整理列举出来供他们参考:
magicindicator 强大、可定制、易扩展的 ViewPager 指示器框架,主页的4个 tab (精选、附近、景点、美食) 就是用这个同时实现的。immersionbar 一句代码轻松同时实现状态栏、导航栏沉浸式管理pagerBottomTabStrip 网页底部和侧边的导航栏,主页、目的地、旅拍、我的网页切换就是用这个同时实现的。rxjava/rxandroid 异步和链式编程butterknife view注入插件,配合Android插件使用,可加速自动生成 init view的代码,不用写一句 findViewById 的代码。gson json解析,配合Android插件使用,可加速生成实体类smartRefreshLayout 智能下拉刷新框架,携程网一楼及下拉刷新加载更多就是用这个同时实现的eventbus 发布/订阅事件总线,优雅的顺利完成模块间通讯arouter 依赖注入、路由跳转、注册service,优雅的顺利完成模块间的通讯okhttp 网络请求插件agentweb webview框架,展开简单的二次封装可优雅的展开网页跳转glide 高性能、可扩展的图片加载插件banner 图片轮播控件基本就是那些了,应该没有漏的,插件的详尽使用,请进入各插件的 GitHub 主页。
在此,把我工程建设项目的插件导入代码及版本管理的 gradle 代码贴出来,如下表所示:插件导入代码:
dependencies{
implementation fileTree(dir: libs, include: [*.jar])
implementation rootProject.depsLibs.appcompat
implementation rootProject.depsLibs.legacy
implementation rootProject.depsLibs.recyclerview
implementation rootProject.depsLibs.constraintlayout
implementation rootProject.depsLibs.cardview
//tab指示器implementation rootProject.depsLibs.magicindicator
//沉浸式implementation rootProject.depsLibs.immersionbar
//导航栏implementation rootProject.depsLibs.pagerBottomTabStrip
//rxjavaimplementation rootProject.depsLibs.rxjava
//rxandroidimplementation rootProject.depsLibs.rxandroid
//view 注入implementation rootProject.depsLibs.butterknife
//view 注入annotationProcessor rootProject.depsLibs.butterknifeCompiler
//gsonimplementation rootProject.depsLibs.gson
//bannerimplementation rootProject.depsLibs.banner
//smartRefreshLayout 上下拉刷新implementation rootProject.depsLibs.smartRefreshLayout
implementation rootProject.depsLibs.refreshHeader
implementation rootProject.depsLibs.refreshHeaderTwoLevel
implementation rootProject.depsLibs.refreshFooter
//eventbusimplementation rootProject.depsLibs.eventbus
//arouter库implementation(rootProject.depsLibs.arouterapi) {
exclude group: com.android.support}
annotationProcessor rootProject.depsLibs.aroutercompiler
//导入home模块 implementation project(:ft_home)
//导入图片加载库 implementation project(:lib_image_loader)
//导入网络库 implementation project(:lib_network)
//webview implementation project(:lib_webview)
//导入基础ui库 implementation project(:lib_common_ui)
//base库 implementation project(:lib_base)
//导入flutter模块 implementation project(:flutter)
//导入百度AI语音库 implementation project(:lib_asr)
}
版本管理代码 (统一管理版本号) :
ext {
android = [
compileSdkVersion: 29,
buildToolsVersion: “29.0.0”,
minSdkVersion : 19,
targetSdkVersion : 29,
applicationId : net.lishaoy.android_ctrip,
versionCode : 1,
versionName : 1.0,
multiDexEnabled :true,
]
depsVersion = [
appcompat : 1.1.0,
legacy : 1.0.0,
recyclerview :1.0.0,
constraintlayout : 1.1.3,
cardview : 1.0.0,
magicindicator : 1.5.0,
immersionbar :3.0.0,
pagerBottomTabStrip : 2.3.0X,
glide : 4.11.0,
glidecompiler :4.11.0,
butterknife : 10.2.1,
butterknifeCompiler : 10.2.1,
rxjava : 3.0.0,
rxandroid :3.0.0,
okhttp : 4.7.2,
okhttpLogging : 4.7.2,
gson :2.8.6,
banner : 2.0.10,
smartRefreshLayout : 2.0.1,
refreshHeader : 2.0.1,
refreshFooter :2.0.1,
refreshHeaderTwoLevel: 2.0.1,
eventbus : 3.2.0,
agentweb : 4.1.3,
arouterapi : 1.5.0,
aroutercompiler : 1.2.2,
]
depsLibs = [
appcompat : “androidx.appcompat:appcompat:${depsVersion.appcompat}”,
legacy : “androidx.legacy:legacy-support-v4:${depsVersion.legacy}”,
recyclerview : “androidx.recyclerview:recyclerview:${depsVersion.recyclerview}”,
constraintlayout : “androidx.constraintlayout:constraintlayout:${depsVersion.constraintlayout}”,
cardview : “androidx.cardview:cardview:${depsVersion.cardview}”,
magicindicator :“com.github.hackware1993:MagicIndicator:${depsVersion.magicindicator}”,
immersionbar :“com.gyf.immersionbar:immersionbar:${depsVersion.immersionbar}”,
pagerBottomTabStrip : “me.majiajie:pager-bottom-tab-strip:${depsVersion.pagerBottomTabStrip}”,
glide : “com.github.bumptech.glide:glide:${depsVersion.glide}”,
glidecompiler : “com.github.bumptech.glide:compiler:${depsVersion.glidecompiler}”,
butterknife : “com.jakewharton:butterknife:${depsVersion.butterknife}”,
butterknifeCompiler : “com.jakewharton:butterknife-compiler:${depsVersion.butterknifeCompiler}”,
rxjava : “io.reactivex.rxjava3:rxjava:${depsVersion.rxjava}”,
rxandroid :“io.reactivex.rxjava3:rxandroid:${depsVersion.rxandroid}”,
okhttp : “com.squareup.okhttp3:okhttp:${depsVersion.okhttp}”,
okhttpLogging : “com.squareup.okhttp3:logging-interceptor:${depsVersion.okhttpLogging}”,
gson : “com.google.code.gson:gson:${depsVersion.gson}”,
banner :“com.youth.banner:banner:${depsVersion.banner}”,
smartRefreshLayout : “com.scwang.smart:refresh-layout-kernel:${depsVersion.smartRefreshLayout}”,
refreshHeader : “com.scwang.smart:refresh-header-classics:${depsVersion.refreshHeader}”,
refreshHeaderTwoLevel: “com.scwang.smart:refresh-header-two-level:${depsVersion.refreshHeader}”,
refreshFooter : “com.scwang.smart:refresh-footer-classics:${depsVersion.refreshFooter}”,
eventbus : “org.greenrobot:eventbus:${depsVersion.eventbus}”,
agentweb : “com.just.agentweb:agentweb:${depsVersion.agentweb}”,
arouterapi :“com.alibaba:arouter-api:${depsVersion.arouterapi}”,
aroutercompiler : “com.alibaba:arouter-compiler:${depsVersion.aroutercompiler}”,
]
}
工程建设项目机能详尽简述(所制习题)
这里主要对主页机能及习题展开简述,由于其它网页是导入了以后的 Flutter 网页, 具体机能在 Flutter 10天刺绣品小厂App及小技巧积累总结 已经介绍过了,在这就不再阐述。
主页重点简述下列机能的同时实现:
下拉刷新、携程网一楼搜索appBar渐变色网格导航banner模块多状态的tab指示器 (滚动固定顶部)下拉刷新、携程网一楼具体来说,看看具体的效用图,如图:
下拉刷新和携程网一楼是使用 smartRefreshLayout 插件顺利完成的,同时实现代码如下表所示:
private void initRefreshMore(){
homeHeader.setRefreshHeader(new ClassicsHeader(getContext()), –1, (int) Utils.dp2px(76)); //设置下拉刷新及一楼header的高度 homeHeader.setFloorRate(1.6f); //设置一楼触发比率homeRefreshContainer.setPrimaryColorsId(R.color.colorPrimary, R.color.white);//设置下拉刷新及一楼提示文字颜色homeRefreshContainer.setOnMultiListener(newSimpleMultiListener() {
@Override public void onLoadMore(@NonNull RefreshLayout refreshLayout){
loadMore(refreshLayout); //加载更多}
@Override public void onRefresh(@NonNull RefreshLayout refreshLayout){
refreshLayout.finishRefresh(1600); //设置下拉刷新延迟}
@Override public void onHeaderMoving(RefreshHeader header,boolean isDragging, float percent, int offset, int headerHeight, int maxDragHeight){
homeSecondFloorImg.setVisibility(View.VISIBLE);//隐藏一楼背景图 homeSearchBarContainer.setAlpha(1 – Math.min(percent, 1)); //改变searchBar透明度}
@Override public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState){
if (oldState == RefreshState.ReleaseToTwoLevel) { //即将去往一楼状态处理homeSecondFloorImg.setVisibility(View.GONE);
homeHeaderContent.animate().alpha(1).setDuration(666);
} else if (newState == RefreshState.PullDownCanceled) { //下拉取消状态处理homeHeaderContent.animate().alpha(0).setDuration(666);
} else if (newState == RefreshState.Refreshing) { //正在刷新状态处理homeHeaderContent.animate().alpha(0).setDuration(666);
} else if (oldState == RefreshState.TwoLevelReleased) { // 准备去往一楼顺利完成状态处理,这里打开webview WebViewImpl.getInstance().gotoWebView(“https://m.ctrip.com/webapp/you/tsnap/secondFloorIndex.html?isHideNavBar=YES&s_guid=feb780be-c55a-4f92-a6cd-2d81e04d3241”, true);
homeHeader.finishTwoLevel();
} else if (oldState == RefreshState.TwoLevel) { //到达一楼状态处理homeCustomScrollView.setVisibility(View.GONE);
homeHeaderContent.animate().alpha(0).setDuration(666);
} else if(oldState == RefreshState.TwoLevelFinish) {//一楼顺利完成状态处理homeCustomScrollView.setVisibility(View.VISIBLE);
homeCustomScrollView.animate().alpha(1).setDuration(666);
}
}
});
}
XML 网页产业布局文件代码如下表所示:
<com.scwang.smart.refresh.layout.SmartRefreshLayout android:id=“@+id/home_refresh_container” android:layout_width=“match_parent” android:layout_height=“match_parent” android:clipChildren=“false” app:srlAccentColor=“@color/colorPrimary” app:srlPrimaryColor=“@color/colorPrimary”> <com.scwang.smart.refresh.header.TwoLevelHeader android:id=“@+id/home_header” android:layout_width=“match_parent” android:layout_height=“match_parent” android:gravity=“top”> <ImageView android:id=“@+id/home_second_floor_img” android:layout_width=“match_parent” android:layout_height=“460dp” android:layout_alignTop=“@+id/home_header” android:scaleType=“fitXY” android:src=“@drawable/second_floor” android:visibility=“gone”/> <FrameLayout android:id=“@+id/home_header_content” android:layout_width=“match_parent” android:layout_height=“match_parent” android:alpha=“0”> <ImageView android:layout_width=“match_parent” android:layout_height=“match_parent” android:scaleType=“fitXY” android:src=“@drawable/second_floor” /> </FrameLayout> </com.scwang.smart.refresh.header.TwoLevelHeader>…
<com.scwang.smart.refresh.footer.ClassicsFooter android:layout_width=“match_parent” android:layout_height=“wrap_content”/></com.scwang.smart.refresh.layout.SmartRefreshLayout>具体同时实现详情,可点选 GitHub 查看源代码。
搜索appBar搜索栏的滚动的 placeholder 文字是使用 banner 插件同时实现的,点击搜索框可跳转到搜索网页 (flutter写的搜索网页) ,跳转网页后能把 placeholder 文字带到 flutter 搜索网页。
效用如图:
滚动的placeholder文字同时实现代码如下表所示 (搜索框的同时实现就不再这里展示都是一些XML产业布局代码):
homeSearchBarPlaceholder
.setAdapter(newHomeSearchBarPlaceHolderAdapter(homeData.getSearchPlaceHolderList()))// 设置适配器.setOrientation(Banner.VERTICAL)// 设置滚动方向 .setDelayTime(3600) // 设置间隔时间 .setOnBannerListener(newOnBannerListener() {
@Override public void OnBannerClick(Object data, int position) { //点击打开 flutter 搜索网页ARouter.getInstance()
.build(“/home/search”)
.withString(“placeHolder”, ((Home.SearchPlaceHolderListBean) data).getText())
.navigation();
}
});
}
searchBar的具体机能不过多阐述,和以后的工程建设项目一致。
渐变色网格导航渐变色网格导航基本都是一些 XML 网页产业布局代码,而已我把它封装成了单独的模块,效用如图
封装之后的导入就非常简单,代码如下表所示:
<LinearLayout android:layout_width=“match_parent” android:layout_height=“wrap_content” android:orientation=“vertical” android:background=“@color/white”> <!– 网格导航 –> <net.lishaoy.ft_home.GridNavView android:id=“@+id/home_grid_nav_container” android:layout_width=“match_parent” android:layout_height=“wrap_content” />…
</LinearLayout>具体同时实现详情,可点选 GitHub 查看源代码。
banner模块banner模块也是用 banner 插件同时实现的,如图
banner同时实现代码如下表所示:
private void initBanner(){
homeBanner.addBannerLifecycleObserver(this)
.setAdapter(newHomeBannerAdapter(homeData.getBannerList()))//设置适配器 .setIndicator(new EllipseIndicator(getContext())) //设置指示器,如图的指示器是我自定义的插件里并没有提供 .setIndicatorSelectedColorRes(R.color.white) //设置指示器颜色 .setIndicatorSpace((int) BannerUtils.dp2px(10)) //设置间距 .setBannerRound(BannerUtils.dp2px(6)); //设置圆角}
多状态的tab指示器多状态的tab指示器的同时实现需要注意很多细节,因为它是在主页的 fragment 的 ScrollView 里嵌入 viewPaper,具体来说你会发现 viewPaper 不显示的问题,其次是滚动不流畅的问题,这两个问题我的解决方案是:
viewPaper 不显示的问题:使用自定义的 ViewPager 重写 onMeasure 方法,重新计算高度滚动不流畅的问题:使用自定义的 ScrollView,重写 computeScroll 和 onScrollChanged这个机能同时实现代码过多不便在这里展示,具体同时实现详情,可点选 GitHub 查看源代码。
Android Flutter 混和合作开发
这个工程建设项目的同时实现多于主页是用 Android 原生植物同时实现,其它的网页均是 Flutter 同时实现的,以后 纯Flutter工程建设项目。
Android 导入 Flutter 展开混和合作开发,需要下列几个步骤
建立两个flutter module编写flutter代码 (创建 flutter 路由)flutter 和 android 间相互通讯下面依次简述这几部份是怎样操作同时实现的。
建立两个flutter module这个应该不用过多描述,基本操作他们都会 File –> New –> New Module 如图:
flutter module新建顺利完成之后,android studio 会自动生成配置代码到 gradle 配置文件里,且生成两个 flutter 的 library 模块。
Tips:新建的时候最好 flutter module 和 android 工程建设项目放到同级目录下;新版的 android studio 才会自动生成 gradle 配置代码,老版本貌似需要手动配置
如,没有生成 gradle 配置代码,你需要在根工程建设项目的 settings.gradle 文件里手动加入如下表所示配置:
setBinding(new Binding([gradle:this]))
evaluate(new File(
settingsDir, //设置根路径,根据具体flutter module路径配置 flutter_module/.android/include_flutter.groovy))
include :flutter_module还需在寄主工程建设 (没改名的话都是app) 的 build.gradle 导入 flutter, 如下表所示:
dependencies{
…
//导入flutter模块 implementation project(:flutter)
…
}
编写flutter代码编写flutter代码,在 flutter module 里按照正常 flutter 合作开发流程编写 flutter 代码即可。(我工程建设项目里的 flutter 的代码是以后工程建设项目都写好的,复制过来,改改包的导入问题,就能运转了。)
这里需要注意的是,flutter 有且多于两个入口,就是 main() 函数,他们需要在这里处理好 flutter 网页的跳转问题。
在 android 端,创建 flutter 网页,代码如下表所示:
Flutter.createView(getActivity(),getLifecycle(),“destination”);
Flutter.createView 需要3个参数 activity 、lifecycle 、route ,这个 route 就是要传递到 flutter 端的,当然,它是 String 类型的,他们能自由发挥传递普通字符串或 json 字符串等。
他们也能透过其它的方式创建 flutter 网页,如:Flutter.createFragment() 、 FlutterActivity.withNewEngine()、 FlutterFragment.createDefault() 等。
具体的使用,可前往 Flutter官方文档 查阅。
那么,flutter 端怎样接收这个 route 参数,是透过 window.defaultRouteName,此工程建设项目里管理 flutter 端路由代码如下表所示:
void main()=> runApp(MyApp());
class MyApp extends StatelessWidget{
@override Widget build(BuildContext context){
returnMaterialApp(
debugShowCheckedModeBanner: false,
title: Flutter model,
theme: ThemeData(
primarySwatch: Colors.blue,
fontFamily: PingFang,
),
home: _widgetRoute(window.defaultRouteName), // 透过 window.defaultRouteName 接收 android 端传来的参数);
}
}
Widget _widgetRoute(String defaultRouteName){
Map<String, dynamic> params = convert.jsonDecode(defaultRouteName);//解析参数 defaultRouteName = params[routeName];
placeHolder = params[placeHolder];
switch (defaultRouteName) { // 根据参数返回相关联的网页…
case destination/search:
returnDestinationSearchPage(
hideLeft: false,
);
…
default:
returnCenter(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children:
,
textDirection: TextDirection.ltr),
],
),
);
}
}
只不过,flutter 端接收这个 route 参数,还有一种方法,就是透过 onGenerateRoute,它是 MaterialApp 里的两个方法。
代码如下表所示:
onGenerateRoute: (settings){ return_widgetRoute(settings.name);
},
flutter 和 android 间相互通信flutter 端能调用 android 端的方法及相互传递数据是怎样同时实现的,flutter 官方提供了3个方法能同时实现,分别是:
EventChannel:单向的持续通讯,如:网络变化、传感器等。MethodChannel:一次性通讯,一般适用如方法的调用。BasicMessageChannel:持续的双向通讯。此工程建设项目里选用了 MethodChannel 方法展开通讯,如:flutter 端调用 android 端的AI智能语音方法以及 flutter 打开 android 端网页就是用 MethodChannel 同时实现的。
flutter 端调用 android 端的AI智能语音方法代码如下:
class AsrManager{
static const MethodChannel _channel = const MethodChannel(lib_asr);
//开始录音 staticFuture<String>start({Map params}) async{
return await _channel.invokeMethod(start, params ?? {});
}
//停止录音…
//取消录音…
//销毁…
}
flutter 打开 android 端网页代码如下表所示:
class MethodChannelPlugin{
static constMethodChannel methodChannel = MethodChannel(MethodChannelPlugin);
static Future<void> gotoDestinationSearchPage() async{
try{
await methodChannel.invokeMethod(gotoDestinationSearchPage); //gotoDestinationSearchPage 参数会传到android端} on PlatformException {
print(Failed go to gotoDestinationSearchPage);
}
}
…
}
android 接收也是透过 MethodChannel ,具体同时实现代码如下表所示:
public class MethodChannelPlugin implements MethodChannel.MethodCallHandler{
private staticMethodChannel methodChannel;
privateActivity activity;
private MethodChannelPlugin(Activity activity){
this.activity = activity;
}
//调用方透过 registerWith 来注册flutter网页 public static void registerWith(FlutterView flutterView){
methodChannel = new MethodChannel(flutterView, “MethodChannelPlugin”);
MethodChannelPlugin instance =newMethodChannelPlugin((Activity) flutterView.getContext());
methodChannel.setMethodCallHandler(instance);
}
@Override public void onMethodCall(MethodCall methodCall, MethodChannel.Result result){
if(methodCall.method.equals(“gotoDestinationSearchPage”)) { // 收到消息展开具体操作 EventBus.getDefault().post(newGotoDestinationSearchPageEvent());
result.success(200);
}
…
else{
result.notImplemented();
}
}
}
android flutter 混和合作开发基本就是这3个步骤,其它一些细节及具体的流程请参考 GitHub 工程建设项目源代码。
最后附上工程建设项目及博客地址:
工程建设项目地址:https://github.com/persilee/android_ctrip博客地址:https://h.lishaoy.net/androidctrip
相关阅读
1 那些初学者实践 Flutter 最常出现的错误2 Flutter 2020首个稳定版 1.17 重磅发布:多个追加特性3 Flutter 合作开发小结 | Tips4 使用Flutter一年后,这是我得到的经验5 Flutter 添加到现有工程建设项目
如果你有写博客的好习惯欢迎投稿赞+在看,花旦感恩❤️