2018-12-21修復(fù),F(xiàn)lutterActivity的頁(yè)面選擇錯(cuò)誤修改
自谷歌發(fā)布Flutter release版本幾天后才開(kāi)始學(xué)習(xí)Flutter,實(shí)在慚愧癣猾。在了解完一些基礎(chǔ)知識(shí)之后開(kāi)始嘗試將編寫(xiě)的簡(jiǎn)單Flutter module打包進(jìn)Android項(xiàng)目中慕蔚。本文章將嘗試過(guò)程中遇到的一些問(wèn)題和筆記記錄下來(lái)。
本篇文章只是閉門(mén)造車(chē)的結(jié)果巍佑,如有任何錯(cuò)誤很抱歉茴迁!請(qǐng)幫忙指出,多謝了
Android項(xiàng)目依賴(lài)Flutter項(xiàng)目
對(duì)于已有的Android項(xiàng)目來(lái)說(shuō)萤衰,將所有頁(yè)面都換成flutter頁(yè)面不太現(xiàn)實(shí)堕义,只能從一些簡(jiǎn)單的頁(yè)面入手逐個(gè)替換。
Flutter項(xiàng)目跟Android工程根文件夾是同級(jí)的脆栋,它不同于普通的Android module存在于Android工程根目錄下倦卖。在AndroidStudio中創(chuàng)建Flutter module,也并不會(huì)將該項(xiàng)目放到Android項(xiàng)目目錄中椿争,而是默認(rèn)選擇Android項(xiàng)目根目錄的同級(jí)目錄下怕膛。
在依賴(lài)Flutter module的時(shí)候,首先需要在項(xiàng)目的setting.gradle
加入如下依賴(lài)
include ':app'
//加入下面配置
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'flutter_module/.android/include_flutter.groovy'
))
以上配置的Flutter module的位置是出于Android根目錄同級(jí)目錄下秦踪,如果Flutter module的路徑不同需要另外設(shè)置File函數(shù)的參數(shù)褐捻。編譯項(xiàng)目,會(huì)在Android項(xiàng)目下生成名為flutter的module椅邓,正常來(lái)說(shuō)該module不需要去修改代碼柠逞,只需要在app的build.gradle中依賴(lài)該fluttermodule即可。
dependencies {
...
// 加入下面配置
implementation project(':flutter')
}
自此景馁,完成Android項(xiàng)目對(duì)Flutter項(xiàng)目的依賴(lài)
FlutterActivity
除了FlutterActivity頁(yè)面板壮,也有FlutterFragmentActivity頁(yè)面,除了基類(lèi)不同裁僧,其他實(shí)現(xiàn)均一致个束。
在創(chuàng)建的Flutter項(xiàng)目的.andorid module中,只有一個(gè)類(lèi)聊疲,那就是MainActivity類(lèi)茬底。
其繼承自FlutterActivity,運(yùn)行該Flutter工程時(shí)获洲,Android項(xiàng)目的入口就是該MainActivity類(lèi)阱表。
該FlutterActivity類(lèi)是Flutter項(xiàng)目的頁(yè)面入口,F(xiàn)lutter為Android項(xiàng)目提供了FlutterView和FlutterFragment作為展示頁(yè)面,附著在Activity上面最爬。而FlutterActivity使用的便是FlutterView涉馁。
那么,從開(kāi)發(fā)的角度爱致,接下來(lái)引出幾個(gè)問(wèn)題烤送?
- 繼承FlutterActivity只能默認(rèn)進(jìn)入Flutter設(shè)定的首頁(yè)?
- Flutter頁(yè)面的生命周期如何管理糠悯?
- Flutter頁(yè)面與Android原生頁(yè)面之間如何通訊帮坚?
- Flutter頁(yè)面是如何繪制的?
查看源碼
查看FlutterActivity的類(lèi)聲明互艾,該類(lèi)實(shí)現(xiàn)了三個(gè)接口
public class FlutterActivity extends Activity implements
Provider, PluginRegistry, ViewFactory {
...
}
這三個(gè)接口作用如下
- Provider:只有一個(gè)簡(jiǎn)單的方法试和,那就是getFlutterView()返回當(dāng)前Activity中的Flutter頁(yè)面
- PluginRegistry:插件注冊(cè)相關(guān)的類(lèi),以后的文章再詳細(xì)講述
- ViewFactory:該接口有三個(gè)方法纫普,分別是
public interface ViewFactory { FlutterView createFlutterView(Context var1); FlutterNativeView createFlutterNativeView(); boolean retainFlutterNativeView(); }
- FlutterView createFlutterView(Context context):該方法比較直觀阅悍,就是生成一個(gè)Flutter的頁(yè)面,供Activity展示昨稼。但是并沒(méi)有在源碼中找到引用它的地方节视。FlutterActivity的實(shí)現(xiàn)返回值是null。
- FlutterNativeView createFlutterNativeView():從字面意思是生成一個(gè)Flutter的原生View悦昵,但是并沒(méi)有在源碼中找到引用它的地方肴茄。FlutterActivity的實(shí)現(xiàn)返回值也是null晌畅。
- boolean retainFlutterNativeView():字面意思但指,保留Flutter原生頁(yè)面。是一個(gè)boolean類(lèi)型的值抗楔,但是并沒(méi)有在源碼中找到引用它的地方棋凳。FlutterActivity的實(shí)現(xiàn)返回值是false。
通過(guò)查看FlutterActivity所繼承的三個(gè)接口连躏,我們并沒(méi)有找到FlutterActivity中直接生成FlutterView的線索剩岳,只能從實(shí)例變量中查找。
FlutterActivityDelegate
在進(jìn)行一番閱讀之后入热,發(fā)現(xiàn)該委派類(lèi)拍棕。在Android源碼中有很多使用委派模式的地方,該處也算是一個(gè)勺良。并且绰播,在FlutterActivity中,F(xiàn)lutterActivityDelegate對(duì)象會(huì)跟隨Activity的生命周期方法被調(diào)用同名方法尚困。查看FlutterActivityDelegate的源碼
- 構(gòu)造方法
其構(gòu)造方法需要傳入一個(gè)Activity對(duì)象蠢箩,還有FlutterActivityDelegate.ViewFactory對(duì)象。但在上文已經(jīng)發(fā)現(xiàn)FlutterActivityDelegate.ViewFactory的方法并無(wú)引用的地方,這里只需要著重關(guān)注Activity對(duì)象就好了谬泌。public FlutterActivityDelegate(Activity activity, FlutterActivityDelegate.ViewFactory viewFactory) { this.activity = (Activity)Preconditions.checkNotNull(activity); this.viewFactory = (FlutterActivityDelegate.ViewFactory)Preconditions.checkNotNull(viewFactory); }
- 同名生命周期方法:查看FlutterActivityDelegate類(lèi)源碼滔韵,該類(lèi)定義了一些列對(duì)象Activity生命周期函數(shù)的同名函數(shù)。并分別運(yùn)行在FlutterActivity類(lèi)的對(duì)應(yīng)生命周期中掌实,由此可見(jiàn)Flutter頁(yè)面的生命周期是由該委托類(lèi)處理的
- onCreate:該方法中實(shí)現(xiàn)了flutterView的生成陪蜻。查看代碼
從FlutterActivity實(shí)現(xiàn)的ViewFactory方法我們已經(jīng)得知,傳遞給委托類(lèi)FlutterActivityDelegate實(shí)例的ViewFactory并沒(méi)有生成FlutterView可供FlutterActivityDelegate使用贱鼻。所以只能繼續(xù)查看public void onCreate(Bundle savedInstanceState) { ... this.flutterView = this.viewFactory.createFlutterView(this.activity); if (this.flutterView == null) { FlutterNativeView nativeView = this.viewFactory.createFlutterNativeView(); this.flutterView = new FlutterView(this.activity, (AttributeSet)null, nativeView); this.flutterView.setLayoutParams(matchParent); this.activity.setContentView(this.flutterView); this.launchView = this.createLaunchView(); if (this.launchView != null) { this.addLaunchView(); } } if (!this.loadIntent(this.activity.getIntent())) { if (!this.flutterView.getFlutterNativeView().isApplicationRunning()) { String appBundlePath = FlutterMain.findAppBundlePath(this.activity.getApplicationContext()); if (appBundlePath != null) { FlutterRunArguments arguments = new FlutterRunArguments(); arguments.bundlePath = appBundlePath; arguments.entrypoint = "main"; this.flutterView.runFromBundle(arguments); } } } }
this.flutterView = new FlutterView(this.activity, (AttributeSet)null, nativeView)
之后的代碼囱皿。
這邊需要注意的是,即使getFlutterView返回具體的FlutterView對(duì)象忱嘹,Activity也不會(huì)去將返回的view設(shè)置到頁(yè)面內(nèi)容中的嘱腥。而是會(huì)通過(guò)loadIntent
方法去讀取intent中傳遞過(guò)來(lái)的route的值,去跳轉(zhuǎn)到flutter項(xiàng)目中設(shè)定的route對(duì)應(yīng)頁(yè)面
FlutterView
public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry, AccessibilityStateChangeListener {
public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {
super(context, attrs);
...
Activity activity = (Activity)this.getContext();
if (nativeView == null) {
this.mNativeView = new FlutterNativeView(activity.getApplicationContext());
} else {
this.mNativeView = nativeView;
}
this.mNativeView.attachViewAndActivity(this, activity);
...
}
}
我們可以看到FlutterView繼承自SurfaceView拘悦,在其構(gòu)造方法中齿兔。如果傳遞的FlutterNativeView如果為空,那將會(huì)重新創(chuàng)建一個(gè)默認(rèn)的FlutterNativeView础米。接著看
public class FlutterNativeView implements BinaryMessenger {
public FlutterNativeView(Context context) {
this(context, false);
}
public FlutterNativeView(Context context, boolean isBackgroundView) {
this.mNextReplyId = 1;
this.mPendingReplies = new HashMap();
this.mContext = context;
this.mPluginRegistry = new FlutterPluginRegistry(this, context);
this.attach(this, isBackgroundView);
this.assertAttached();
this.mMessageHandlers = new HashMap();
}
}
在這里我們可以看到FlutterNativeView實(shí)現(xiàn)了BinaryMessenger接口分苇,而B(niǎo)inaryMessenger是一個(gè)數(shù)據(jù)信息交流對(duì)象,其接口聲明如下
public interface BinaryMessenger {
/**
*Sends a binary message to the Flutter application.
*Parameters:
*channel - the name String of the logical channel used for the message.
*message - the message payload, a direct-allocated ByteBuffer with the message bytes between position zero and current position, or null.
*/
void send(String var1, ByteBuffer var2);
/**
* Sends a binary message to the Flutter application, optionally expecting a reply.
* Any uncaught exception thrown by the reply callback will be caught and logged.
* <p>
* Parameters:
* channel - the name String of the logical channel used for the message.
* message - the message payload, a direct-allocated ByteBuffer with the message bytes between position zero and current position, or null.
* callback - a BinaryMessenger.BinaryReply callback invoked when the Flutter application responds to the message, possibly null.
*/
void send(String var1, ByteBuffer var2, BinaryMessenger.BinaryReply var3);
/**
* Registers a handler to be invoked when the Flutter application sends a message to its host platform.
* Registration overwrites any previous registration for the same channel name. Use a null handler to deregister.
* <p>
* If no handler has been registered for a particular channel, any incoming message on that channel will be handled silently by sending a null reply.
* <p>
* Parameters:
* channel - the name String of the channel.
* handler - a BinaryMessenger.BinaryMessageHandler to be invoked on incoming messages, or null.
*/
void setMessageHandler(String var1, BinaryMessenger.BinaryMessageHandler var2);
/**
* Binary message reply callback. Used to submit a reply to an incoming message from Flutter.
* Also used in the dual capacity to handle a reply received from Flutter after sending a message.
*/
public interface BinaryReply {
/**
* Handles the specified reply.
* Parameters:
* reply - the reply payload, a direct-allocated ByteBuffer or null.
* Senders of outgoing replies must place the reply bytes between position zero and current position.
* Reply receivers can read from the buffer directly.
*/
void reply(ByteBuffer var1);
}
/**
* Handler for incoming binary messages from Flutter.
*/
public interface BinaryMessageHandler {
/**
* Handles the specified message.
* Handler implementations must reply to all incoming messages,
* by submitting a single reply message to the given BinaryMessenger.BinaryReply.
* Failure to do so will result in lingering Flutter reply handlers. The reply may be submitted asynchronously.
* <p>
* Any uncaught exception thrown by this method will be caught by the messenger implementation and logged,
* and a null reply message will be sent back to Flutter.
* <p>
* Parameters:
* message - the message ByteBuffer payload, possibly null.
* reply - A BinaryMessenger.BinaryReply used for submitting a reply back to Flutter.
*/
void onMessage(ByteBuffer var1, BinaryMessenger.BinaryReply var2);
}
}
要命的是Flutter框架在Android中還沒(méi)有注釋可以看屁桑,只能從官網(wǎng)查看文檔医寿。
這是一個(gè)用于在Flutter和Native之間交換數(shù)據(jù)的接口類(lèi),已知FlutterView已經(jīng)實(shí)現(xiàn)了SurfaceView蘑斧,flutterNativeView負(fù)責(zé)FlutterView和Flutter之間的通訊靖秩,再使用Skia繪制頁(yè)面。
loadIntent
即使我們實(shí)現(xiàn)了getFlutterView方法
竖瘾,F(xiàn)lutterActivityDelegate類(lèi)也不會(huì)將該flutterView 添加到Activity的content中的沟突,而是通過(guò)loadIntent
方法去打開(kāi)對(duì)應(yīng)的頁(yè)面。loadIntent
的代碼如下
private boolean loadIntent(Intent intent) {
String action = intent.getAction();
if ("android.intent.action.RUN".equals(action)) {
String route = intent.getStringExtra("route");
String appBundlePath = intent.getDataString();
if (appBundlePath == null) {
appBundlePath = FlutterMain.findAppBundlePath(this.activity.getApplicationContext());
}
if (route != null) {
this.flutterView.setInitialRoute(route);
}
if (!this.flutterView.getFlutterNativeView().isApplicationRunning()) {
FlutterRunArguments args = new FlutterRunArguments();
args.bundlePath = appBundlePath;
args.entrypoint = "main";
this.flutterView.runFromBundle(args);
}
return true;
} else {
return false;
}
}
可以得出捕传,只要打開(kāi)FlutterActivity頁(yè)面時(shí)候惠拭,通過(guò)Intent傳入一個(gè)key為route
的字符串值,就可以跳轉(zhuǎn)到flutter項(xiàng)目中定義的對(duì)應(yīng)route值的頁(yè)面了庸论。
如果我們需要自己封裝帶有自定義屬性和動(dòng)作的FlutterFragmentActivity的子類(lèi)职辅,可以這樣子定義
/**
* author: wangzh
* create: 2018/12/20 19:46
* description: flutter的基類(lèi)
* version: 1.0
*/
public abstract class BaseFlutterActivity extends FlutterFragmentActivity implements LifecycleOwner {
protected Lifecycle mLifecycle;
private static final String ROUTE_ACTION ="android.intent.action.RUN";
@Override
protected void onCreate(Bundle savedInstanceState) {
getIntent().putExtra("route", getTargetPage());
mLifecycle = new LifecycleRegistry(this);
super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
}
public static <P extends BaseFlutterActivity> void toPage(Context context, Class<P> target) {
Intent intent = new Intent(context, target);
intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
intent.setAction(ROUTE_ACTION);
intent.addCategory(Intent.CATEGORY_DEFAULT);
context.startActivity(intent);
}
protected abstract String getTargetPage();
@NonNull
@Override
public Lifecycle getLifecycle() {
return mLifecycle;
}
}
加入我們?cè)贔lutter項(xiàng)目中。定義了route
為login
的頁(yè)面聂示,只需要這樣子打開(kāi)即可
/**
* author: wangzh
* create: 2018/12/20 19:44
* description: 登錄頁(yè)面
* version: 1.0
*/
public class LoginActivity extends BaseFlutterActivity {
@Override
protected String getTargetPage() {
return "login";
}
}
//打開(kāi)flutter中的loginPage
BaseFlutterActivity.toPage(getContext(), LoginActivity.class);
總結(jié)
在閱讀完FlutterActivity的部分源碼以后域携,得出了以上幾個(gè)問(wèn)題的答案。
- 打開(kāi)FlutterActivity頁(yè)面時(shí)候催什,通過(guò)Intent傳入一個(gè)key為
route
的字符串值涵亏,就可以跳轉(zhuǎn)到flutter項(xiàng)目中定義的對(duì)應(yīng)route值的頁(yè)面宰睡。 - 在FlutterActivityDelegate委托類(lèi)里,實(shí)現(xiàn)了對(duì)FlutterActivity和Flutter頁(yè)面生命周期的管理
- HelloFlutter——MethodChannel(Native&Flutter數(shù)據(jù)交互)
- FlutterView繼承了SurfaceView气筋,使用FlutterNativeView在Android和Flutter之間作為通訊的橋梁拆内,之后調(diào)用Skia框架繪制頁(yè)面。這也是其與RN和其他依賴(lài)于WebView的混合開(kāi)發(fā)的框架不同的根源宠默。
本篇文章只是閉門(mén)造車(chē)的結(jié)果麸恍,如有任何錯(cuò)誤很抱歉!請(qǐng)幫忙指出搀矫,多謝了