flutter调用android接口,Flutter笔记-flutter与android的互调-程序员宅基地

技术标签: flutter调用android接口  

ps:flutter版本已经更新到1.2.1

引入包

在build.gradle中可以找到这个

apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"

然后我们到该目录下找到flutter.gradle

788847e36e38

flutter.gradle

在该文件中可以找到

Path baseEnginePath = Paths.get(flutterRoot.absolutePath, "bin", "cache", "artifacts", "engine")

String targetArch = 'arm'

if (project.hasProperty('target-platform') &&

project.property('target-platform') == 'android-arm64') {

targetArch = 'arm64'

}

debugFlutterJar = baseEnginePath.resolve("android-${targetArch}").resolve("flutter.jar").toFile()

profileFlutterJar = baseEnginePath.resolve("android-${targetArch}-profile").resolve("flutter.jar").toFile()

releaseFlutterJar = baseEnginePath.resolve("android-${targetArch}-release").resolve("flutter.jar").toFile()

dynamicProfileFlutterJar = baseEnginePath.resolve("android-${targetArch}-dynamic-profile").resolve("flutter.jar").toFile()

dynamicReleaseFlutterJar = baseEnginePath.resolve("android-${targetArch}-dynamic-release").resolve("flutter.jar").toFile()

我们继续查找目录

788847e36e38

flutter.jar

这就是flutter.jar android部分编译生成的包

OnCreate流程

flutter初始化分为4个部分:

FlutterMain、FlutterNativeView、FlutterView和Flutter Bundle的初始化

先看下Flutter初始化的时序图(图片来源于阿里咸鱼,自己就不献丑了)

788847e36e38

时序图

分析过android启动流程的话,知道Application先于Activity执行,在AndroidManifest.xml中可以找到FlutterApplication

public class FlutterApplication extends Application {

@Override

@CallSuper

public void onCreate() {

super.onCreate();

//初始化

FlutterMain.startInitialization(this);

}

private Activity mCurrentActivity = null;

public Activity getCurrentActivity() {

return mCurrentActivity;

}

public void setCurrentActivity(Activity mCurrentActivity) {

this.mCurrentActivity = mCurrentActivity;

}

}

先看FlutterMain的初始化:

public class FlutterMain {

...

public static void startInitialization(Context applicationContext, Settings settings) {

if (Looper.myLooper() != Looper.getMainLooper()) {

throw new IllegalStateException("startInitialization must be called on the main thread");

}

// Do not run startInitialization more than once.

if (sSettings != null) {

return;

}

sSettings = settings;

//记录初始化资源的起始时间

long initStartTimestampMillis = SystemClock.uptimeMillis();

initConfig(applicationContext);

initAot(applicationContext);

initResources(applicationContext);

//加载libflutter.so库

if (sResourceUpdater == null) {

System.loadLibrary("flutter");

} else {

sResourceExtractor.waitForCompletion();

File lib = new File(PathUtils.getDataDirectory(applicationContext), DEFAULT_LIBRARY);

if (lib.exists()) {

System.load(lib.getAbsolutePath());

} else {

System.loadLibrary("flutter");

}

}

//初始化完成时间

long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;

nativeRecordStartTimestamp(initTimeMillis);

}

}

接下来看一下MainActivity,继承自FlutterActivity,找到其的onCreate方法

public class FlutterActivity extends Activity implements FlutterView.Provider, PluginRegistry, ViewFactory {

private final FlutterActivityDelegate delegate = new FlutterActivityDelegate(this, this);

private final FlutterActivityEvents eventDelegate = delegate;

...

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

eventDelegate.onCreate(savedInstanceState);

}

}

实际上调用的是FlutterActivityDelegate的onCreate方法

public final class FlutterActivityDelegate

implements FlutterActivityEvents,

FlutterView.Provider,

PluginRegistry {

@Override

public void onCreate(Bundle savedInstanceState) {

//sdk版本大于21,即5.0,和沉浸式状态栏有关

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

Window window = activity.getWindow();

window.addFlags(LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);

window.setStatusBarColor(0x40000000);

window.getDecorView().setSystemUiVisibility(PlatformPlugin.DEFAULT_SYSTEM_UI);

}

String[] args = getArgsFromIntent(activity.getIntent());

// 1.

FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);

flutterView = viewFactory.createFlutterView(activity);

//flutterView默认的创建结果是null,必然进入

if (flutterView == null) {

FlutterNativeView nativeView = viewFactory.createFlutterNativeView();

// 2.

flutterView = new FlutterView(activity, null, nativeView);

//界面置为全屏

flutterView.setLayoutParams(matchParent);

//flutterView为显示的主界面

activity.setContentView(flutterView);

launchView = createLaunchView();

if (launchView != null) {

addLaunchView();

}

}

if (loadIntent(activity.getIntent())) {

return;

}

String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());

if (appBundlePath != null) {

//3.

runBundle(appBundlePath);

}

}

}

assets资源已经初始化完毕,将资源路径传递给native端进行处理

public class FlutterMain {

public static void ensureInitializationComplete(Context applicationContext, String[] args) {

...

String appBundlePath = findAppBundlePath(applicationContext);

String appStoragePath = PathUtils.getFilesDir(applicationContext);

String engineCachesPath = PathUtils.getCacheDirectory(applicationContext);

nativeInit(applicationContext, shellArgs.toArray(new String[0]),

appBundlePath, appStoragePath, engineCachesPath);

sInitialized = true;

...

}

}

然后看下FlutterView是个什么

public class FlutterView extends SurfaceView implements BinaryMessenger, TextureRegistry {

...

public FlutterView(Context context, AttributeSet attrs, FlutterNativeView nativeView) {

super(context, attrs);

Activity activity = (Activity) getContext();

if (nativeView == null) {

mNativeView = new FlutterNativeView(activity.getApplicationContext());

} else {

mNativeView = nativeView;

}

dartExecutor = mNativeView.getDartExecutor();

flutterRenderer = new FlutterRenderer(mNativeView.getFlutterJNI());

mIsSoftwareRenderingEnabled = FlutterJNI.nativeGetIsSoftwareRenderingEnabled();

mMetrics = new ViewportMetrics();

mMetrics.devicePixelRatio = context.getResources().getDisplayMetrics().density;

setFocusable(true);

setFocusableInTouchMode(true);

mNativeView.attachViewAndActivity(this, activity);

mSurfaceCallback = new SurfaceHolder.Callback() {

@Override

public void surfaceCreated(SurfaceHolder holder) {

assertAttached();

mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());

}

@Override

public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {

assertAttached();

mNativeView.getFlutterJNI().onSurfaceChanged(width, height);

}

@Override

public void surfaceDestroyed(SurfaceHolder holder) {

assertAttached();

mNativeView.getFlutterJNI().onSurfaceDestroyed();

}

};

getHolder().addCallback(mSurfaceCallback);

mActivityLifecycleListeners = new ArrayList<>();

mFirstFrameListeners = new ArrayList<>();

// Create all platform channels

navigationChannel = new NavigationChannel(dartExecutor);

keyEventChannel = new KeyEventChannel(dartExecutor);

lifecycleChannel = new LifecycleChannel(dartExecutor);

localizationChannel = new LocalizationChannel(dartExecutor);

platformChannel = new PlatformChannel(dartExecutor);

systemChannel = new SystemChannel(dartExecutor);

settingsChannel = new SettingsChannel(dartExecutor);

// Create and setup plugins

PlatformPlugin platformPlugin = new PlatformPlugin(activity, platformChannel);

addActivityLifecycleListener(platformPlugin);

mImm = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);

mTextInputPlugin = new TextInputPlugin(this, dartExecutor);

androidKeyProcessor = new AndroidKeyProcessor(keyEventChannel, mTextInputPlugin);

androidTouchProcessor = new AndroidTouchProcessor(flutterRenderer);

sendLocalesToDart(getResources().getConfiguration());

sendUserPlatformSettingsToDart();

}

...

}

FlutterView是一个SurfaceView,那么再看其surfaceCreated方法,调用的是FlutterJNI中的native方法

@UiThread

public void onSurfaceCreated(@NonNull Surface surface) {

ensureAttachedToNative();

nativeSurfaceCreated(nativePlatformViewId, surface);

}

private native void nativeSurfaceCreated(long nativePlatformViewId, Surface surface);

也即是说flutter其实是自己绘制,然后在surfaceview上显示的,稍微深入一下来验证一下:

来找一下这个native方法,看一下里面到底做了什么,来到platform_view_android_jni.cc文件下(路径:engine/shell/platform/android/io/platform_view_android_jni.cc)

//platform_view_android_jni.cc

static const JNINativeMethod flutter_jni_methods[] = {

...

{

.name = "nativeSurfaceCreated",

.signature = "(JLandroid/view/Surface;)V",

.fnPtr = reinterpret_cast(&shell::SurfaceCreated),

},

...

}

这是jni中动态注册的方式,nativeSurfaceCreated对应了SurfaceCreated方法

//platform_view_android_jni.cc

static void SurfaceCreated(JNIEnv* env,

jobject jcaller,

jlong shell_holder,

jobject jsurface) {

fml::jni::ScopedJavaLocalFrame scoped_local_reference_frame(env);

//创建新的窗口用于显示

auto window = fml::MakeRefCounted(

ANativeWindow_fromSurface(env, jsurface));

ANDROID_SHELL_HOLDER->GetPlatformView()->NotifyCreated(std::move(window));

}

通过ANativeWindow与surfaceView进行绑定,再看NotifyCreated方法

//platform_view_android.cc

void PlatformViewAndroid::NotifyCreated(

fml::RefPtr native_window) {

if (android_surface_) {

InstallFirstFrameCallback();

android_surface_->SetNativeWindow(native_window);

}

PlatformView::NotifyCreated();

}

SetNativeWindow(native_window)与我们的window相关,继续追踪,因android_surface_是一个std::unique_ptr(ptr是动态指针,即AndroidSurface类型)

//android_surface.h

class AndroidSurface {

public:

static std::unique_ptr Create(bool use_software_rendering);

...

virtual bool SetNativeWindow(fml::RefPtr window) = 0;

};

}

我们找其方法实现的类

//android_surface_gi.cc

bool AndroidSurfaceGL::SetNativeWindow(

fml::RefPtr window) {

onscreen_context_ = nullptr;

if (!offscreen_context_ || !offscreen_context_->IsValid()) {

return false;

}

// Create the onscreen context.

onscreen_context_ = fml::MakeRefCounted(

offscreen_context_->Environment(),

offscreen_context_.get() );

if (!onscreen_context_->IsValid()) {

onscreen_context_ = nullptr;

return false;

}

if (!onscreen_context_->CreateWindowSurface(std::move(window))) {

onscreen_context_ = nullptr;

return false;

}

return true;

}

一切相关的信息都在CreateWindowSurface中

//android_context_gi.cc

bool AndroidContextGL::CreateWindowSurface(

fml::RefPtr window) {

window_ = std::move(window);

EGLDisplay display = environment_->Display();

const EGLint attribs[] = {EGL_NONE};

//创建一个通用的surface

surface_ = eglCreateWindowSurface(

display, config_,

reinterpret_cast(window_->handle()), attribs);

return surface_ != EGL_NO_SURFACE;

}

到这一步基本追踪完了,通过egl创建了一个surface_(EGLSurface)供c端使用

绘制

还记得之前在自定义控件是我们绘制圆形时使用的方法吗?

canvas.drawCircle(Offset(0, 0), 100, _paint);

深入源码一下

void drawCircle(Offset c, double radius, Paint paint) {

assert(_offsetIsValid(c));

assert(paint != null);

_drawCircle(c.dx, c.dy, radius, paint._objects, paint._data);

}

void _drawCircle(double x,

double y,

double radius,

List paintObjects,

ByteData paintData) native 'Canvas_drawCircle';

这里也使用了native方法,即dart调用c语言,我们也来找一下它的源码简单分析一下

Canvas_drawCircle,和jni的命名不同,这个是表示在Canvas类中的drawCircle方法

其实dart.ui包就在engine中,painting.dart文件也在其中

在canvas.cc(路径:engine/lib/ui/painting/canvas.cc)文件中找到drawCircle方法

//canvas.cc

void Canvas::drawCircle(double x,

double y,

double radius,

const Paint& paint,

const PaintData& paint_data) {

if (!canvas_)

return;

canvas_->drawCircle(x, y, radius, *paint.paint());

}

canvas_是一个SkCanvas*

//canvas.h

#include "third_party/skia/include/core/SkCanvas.h"

#include "third_party/skia/include/utils/SkShadowUtils.h"

class Canvas : public RefCountedDartWrappable {

...

private:

explicit Canvas(SkCanvas* canvas);

SkPictureRecorder::beginRecording,

SkCanvas* canvas_;

};

SkCanvas.h在engine工程中无法找到,不过我们能在github的skia工程中找到

void SkCanvas::drawCircle(SkScalar cx, SkScalar cy, SkScalar radius,

const SkPaint& paint) {

if (radius < 0) {

radius = 0;

}

SkRect r;

r.set(cx - radius, cy - radius, cx + radius, cy + radius);

this->drawOval(r, paint);

}

到这就不再分析下去了,flutter是通过skia引擎直接渲染的,所以在绘制速度上要比原生还快些

互调接口

AndroidView 来调用android原生的控件(Ios使用 UiKitView)

目前还存在一些bug,感兴趣的可以查看一下flutter_webview_plugin开源库的实现源码

EventChannel 传递和接收事件流

MethodChannel 传递和接收方法

BasicMessageChannel 传递和接收字符串

相关文章

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_34975022/article/details/117776835

智能推荐

Eclipse安装lombok插件_eclipse配置lombok-程序员宅基地

文章浏览阅读408次。1.下载lombok插件jar包 下载地址:https://www.projectlombok.org/download2.将下载的lombok.jar复制到eclipse根目录下3.在eclipse.ini文件的末尾添加两行配置 -Xbootclasspath/a:lombok.jar -javaagent:lombok.jar 4.重启eclipse,即可生效..._eclipse配置lombok

用installshield打包时在注册表里注册项和键值的动态设定方法_注册表new - > key-程序员宅基地

文章浏览阅读1.1w次。在11.5版本里,注册表的函数变化不多我在这里要记录的是打包的时候注册表的问题,注册表分项和键,打开注册表(开始->运行,输入regedit,确定),可以在左边的树形里看见很多文件夹的图标,这些是项,点击一个项,可以在右边看见他的键值,每个项都有一个“默认”,有的项还有很多键值在打包的时候,我们有时候要记录很多东西,嗯,我们要记录安装文件的类型(服务器端程序还是客户端程序),我们要记录软件_注册表new - > key

全志平台lichee启动时间优化_initial_debug-程序员宅基地

文章浏览阅读490次。1. 前言全志平台默认的SDK系统启动+相机出图的时间较长,普遍是十几秒,使用体验较差; 这里尝试通过一些方法优化整体启动时间2.统计内核耗时模块打开这个宏initial_debug,每个驱动的初始化起始时间和结束时间都打印出来了。有了这个时间,基本就可以确定哪些部分需要优化了。 我的做法是只关注耗时10000us以上的驱动。根据上面统计,可根据使用场景的需求, 尝试可以关闭相应不需要的模块3. 降低打印等级开发过程中往往会把系统的打印等级设置为最高,而实际上发布出的固件_initial_debug

《HelloGitHub》第 67 期-程序员宅基地

文章浏览阅读1.5k次。兴趣是最好的老师,HelloGitHub 让你对编程感兴趣!简介分享 GitHub 上有趣、入门级的开源项目。https://github.com/521xueweihan/HelloGi..._开源 lexer

如何做代码评审(code review)_code review怎么做-程序员宅基地

文章浏览阅读5.7k次。Code Review 即日常所说的代码评审或代码回顾,主要是在软件开发的过程中,对功能源代码进行评审,其目的是找出并修正软件开发过程中出现的错误的过程,提高和改进代码质量的过程。_code review怎么做

SSM9==SSM项目启动过程、xml配置SSM项目及需要的3大配置文件、原生SSM未前后端分离的电商网站项目(角色管理员、购买者)只使用了最基础的注解,Model传参_ssm框架 启动传参-程序员宅基地

文章浏览阅读2.1k次。SSM项目的启动过程:ssm框架中,项目启动过程以及web.xml配置详解_菜鸟不会飞-程序员宅基地_ssm项目启动入口是什么本篇主要在基于SSM的框架,深入讲解web.xml的配置web.xml 每个javaEE项目中都会有,web.xml文件是用来初始化配置信息:比如Welcome页面、servlet、servlet-mapping、filter、listener、启动加载级别等。 web.xml配置文件内容如下:&lt;!DOCTYPE web-app PUBLIC..._ssm框架 启动传参

随便推点

Windows 11 蓝屏 Stop code - SYSTEM SERVICE EXCEPTION What failed - igdkmd64.sys_win11system_service_exception-程序员宅基地

文章浏览阅读2.4k次,点赞3次,收藏2次。Windows 11 蓝屏时,操作系统会生成一个名为 minidump(.dmp)的文件。这个文件包含了蓝屏发生时的系统信息、硬件状态、内存数据等。笔者使用的 Lenovo P53 工作站,原本只有 32GB 的内存,我又自己购买了一根 32 GB 的内存条插上去使用。这个错误表明系统服务发生了异常,而igdkmd64.sys是英特尔显卡驱动程序文件名。3.检查您的硬件是否正常工作,例如检查显卡是否插好,风扇是否正常运转等。2.删除所有显卡驱动程序,然后重新安装最新版本的驱动程序。1.驱动程序过时或损坏。_win11system_service_exception

ExtJS梦想之旅(一)--第一个ExtJS程序(HelloWorld)_extjs 第一个项目-程序员宅基地

文章浏览阅读1.7k次。前两天一直在学习ExtJS之javascript基础,现在正式开始ExtJS梦想之旅,进入了ExtJS的学习!一、ExtJS简介 ExtJS是一种RIA(富因特网应用程序)Ajax框架,它天生丽质,使用它开发出来的web应用可以不用美工的参与,也能做很炫丽的页面效果。它使用Javascript作为开发语言,借鉴了Java中Swing的设计思想,集成了桌面应用的交互性和传统WEB_extjs 第一个项目

MongoDB 的简介、特点以及详解_mongodb特点-程序员宅基地

文章浏览阅读1.8k次,点赞2次,收藏2次。1、概念_mongodb特点

Main class [org.apache.oozie.action.hadoop.HiveMain], exit code [10]_attempt recovered after rm restartmain class [org.-程序员宅基地

文章浏览阅读3k次。oozie执行hiveql,发生这样的异常!Main class [org.apache.oozie.action.hadoop.HiveMain], exit code [10]_attempt recovered after rm restartmain class [org.apache.oozie.action.hadoop

计算机毕业设计springboot网上排课系统的设计与实现w0d059【附源码+数据库+部署+LW】_排课系统数据库设计-程序员宅基地

文章浏览阅读255次。选题背景:随着互联网的快速发展,各行各业都在积极探索数字化转型的道路。教育领域也不例外,传统的排课方式已经无法满足现代学生和教师的需求。为了提高教学效率、优化资源利用以及提供更好的学习体验,许多学校和机构开始引入网上排课系统。这种系统通过在线平台实现课程的安排和管理,为学生和教师提供了更加便捷和灵活的排课方式。选题意义:网上排课系统的出现对于教育领域具有重要的意义。首先,它能够提高教学效率。传统的排课方式需要大量的人力和时间,而网上排课系统可以自动化地完成排课过程,减少了繁琐的手工操作,提高了排课的_排课系统数据库设计

对IO流关闭的思考_c++ 流不关闭-程序员宅基地

文章浏览阅读7.9k次,点赞3次,收藏13次。流必须要关闭的原因java相对C,C++来说不需要手动释放内存,在对象引用被消除之后,正常情况下内存资源是会被垃圾回收,那么在使用完IO流之后为什么需要手动关闭. 这是为了回收系统资源,比如释放占用的端口,文件句柄,网络操作数据库应用等.对Unix系统来说,所有的资源都可以抽象成文件,所以可以通过lsof来观察。看下面这个例子,我们创建许多的IO流但是不关闭public clas..._c++ 流不关闭