相信大家在学习Flutter的开始阶段都看过Flutter的架构图,如下
我们知道Flutter的应用层代码由Dart编写,Framework层提供了一系列Widget和其它API,那么这些Dart编写的代码是如何在特定平台上执行的呢,这就要从Flutter的启动过程说起了,了解了Flutter的启动过程,这个问题便迎刃而解。
我们通过架构图可以看出Embedder是由特定的平台实现,它其实就是将Flutter移植到各平台的中间层代码。Embedder层是Flutter启动的关键,其在应用启动后,由平台原生模块通过调用该层的API执行一系列操作,比如渲染层体系的设置、相关线程的创建等,最主要的是通过Embedder层初始化Flutter Engine,Engine中会创建DartVM、各种服务协议的初始化、Platform Channels的初始化等等,而后就会在DartVM中执行dart编写的入口方法main方法。至此,Flutter模块就启动成功了。
安卓平台对应的Embedder层代码在engine源码的 /flutter/shell/platform/android/ 目录下。
我们来根据flutter create my_app
命令创建的Flutter项目demo来分析。
首先,my_app应用启动会先执行FlutterApplication
,我们来看下该类中的生命周期方法onCreate方法的实现
@Override
@CallSuper
public void onCreate() {
super.onCreate();
FlutterMain.startInitialization(this);
}
FlutterMain类即是Embedder层的代码,该类的startInitialization方法实现如下
public static void startInitialization(Context applicationContext) {
startInitialization(applicationContext, new Settings());
}
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);
System.loadLibrary("flutter");
long initTimeMillis = SystemClock.uptimeMillis() - initStartTimestampMillis;
nativeRecordStartTimestamp(initTimeMillis);
}
由以上源码可知startInitialization方法需要在主线程中执行,该方法主要是初始化配置信息、初始化AOT模式下的变量(Debug下是JIT模式)、资源文件的初始化(主要是将asset目录下的flutter相关资源文件copy到私有目录下)等,最后会将以上初始化所用时间通过JNI方法传递到c++层做记录。
static void RecordStartTimestamp(JNIEnv* env,
jclass jcaller,
jlong initTimeMillis) {
int64_t initTimeMicros =
static_cast<int64_t>(initTimeMillis) * static_cast<int64_t>(1000);
blink::engine_main_enter_ts = Dart_TimelineGetMicros() - initTimeMicros;
}
关键java类的UML类图
FlutterApplication执行完onCreate方法后会执行启动页面MainActivity的生命周期方法。我们发现MainActivity的onCreate方法中并没有通过setContentView来设置显示的视图,由于MainActivity继承了FlutterActivity并重载了父类的onCreate方法,所以我们看下FlutterActivity的onCreate方法
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);
}
FlutterActivity继承自Activity,那么视图的设置我们推测应该是在FlutterActivityDelegate的onCreate方法中,我们看下它的实现
@Override
public void onCreate(Bundle savedInstanceState) {
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());
FlutterMain.ensureInitializationComplete(activity.getApplicationContext(), args);
flutterView = viewFactory.createFlutterView(activity);
if (flutterView == null) {
FlutterNativeView nativeView = viewFactory.createFlutterNativeView();
flutterView = new FlutterView(activity, null, nativeView);
flutterView.setLayoutParams(matchParent);
activity.setContentView(flutterView);
launchView = createLaunchView();
if (launchView != null) {
addLaunchView();
}
}
if (loadIntent(activity.getIntent())) {
return;
}
String appBundlePath = FlutterMain.findAppBundlePath(activity.getApplicationContext());
if (appBundlePath != null) {
runBundle(appBundlePath);
}
}
我们发现代码中有一句activity.setContentView(flutterView);
即为当前MainActivity设置显示视图,那么flutterView是如何创建的呢,我们分析该语句之前的代码。
我们先来分析第3步骤执行FlutterMain的ensureInitializationComplete方法,先看一下具体实现
public static void ensureInitializationComplete(Context applicationContext, String[] args) {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("ensureInitializationComplete must be called on the main thread");
}
...
if (sInitialized) {
return;
}
try {
sResourceExtractor.waitForCompletion();
List<String> shellArgs = new ArrayList<>();
shellArgs.add("--icu-data-file-path=" + sIcuDataPath);
...
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;
} catch (Exception e) {
Log.e(TAG, "Flutter initialization failed.", e);
throw new RuntimeException(e);
}
}
我们发现该方法也要求必须在主线程中执行,且只执行一次,一旦执行过会通过sInitialized变量来进行标识下次不再执行。try-catch代码块中第一句sResourceExtractor.waitForCompletion()
表示要等待初始化时的资源初始化完毕后才会向下执行,否则会一直阻塞。下面会初始化一些参数配置信息、flutter打包出的appBundle路径、应用存储目录、引擎缓存目录等信息,然后会调用JNI方法在c++层初始化这些信息,JNI方法对应的c++方法如下
void FlutterMain::Init(JNIEnv* env,
jclass clazz,
jobject context,
jobjectArray jargs,
jstring bundlePath,
jstring appStoragePath,
jstring engineCachesPath) {
std::vector<std::string> args;
args.push_back("flutter");
for (auto& arg : fml::jni::StringArrayToVector(env, jargs)) {
args.push_back(std::move(arg));
}
auto command_line = fml::CommandLineFromIterators(args.begin(), args.end());
auto settings = SettingsFromCommandLine(command_line);
settings.assets_path = fml::jni::JavaStringToString(env, bundlePath);
...
g_flutter_main.reset(new FlutterMain(std::move(settings)));
}
c++层会将传入的参数保存到settings对象中,然后根据settings对象创建FlutterMain对象并保存到全局静态变量g_flutter_main
中,供后续flutter引擎初始化使用。
接着,会通过viewFactory创建FlutterView对象,viewFactory就是实现了ViewFactory接口的FlutterActivity对象,查看其createFlutterView方法的实现发现返回null,此时就会执行if中的代码块,同样的,会通过viewFactory创建FlutterNativeView对象,我们查看源码发现同样返回null,这两个方法在FlutterActivity中的实现如下
@Override
public FlutterView createFlutterView(Context context) {
return null;
}
@Override
public FlutterNativeView createFlutterNativeView() {
return null;
}
紧接着,会开始使用FlutterView的带参数的构造方法创建FlutterView对象,其具体实现如下
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;
}
...
mNativeView.attachViewAndActivity(this, activity);
mSurfaceCallback = new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder holder) {
assertAttached();
mNativeView.getFlutterJNI().onSurfaceCreated(holder.getSurface());
}
...
};
getHolder().addCallback(mSurfaceCallback);
...
// Configure the platform plugins and flutter channels.
mFlutterLocalizationChannel = new MethodChannel(this, "flutter/localization", JSONMethodCodec.INSTANCE);
...
}
这里参数nativeView上文已经得出结论为null,要先通过FlutterNativeView的构造方法创建mNativeView对象,然后通过mNativeView调用attachViewAndActivity方法将FlutterView和当前的Activity做连接。
接着创建当前FlutterView(要知道它继承自SurfaceView)的mSurfaceCallback对象并添加到当前SurfaceHolder中以监听Surface的变化(如Surface的创建、改变和销毁等),这些变化会执行对应的回调方法,然后通过FlutterJNI的相关方法传递数据给Flutter engine层。
该方法中还会创建各种必要的平台插件和platform channel(用于flutter和原生之间的各种数据传递)。
接下来我们看下FlutterNativeView的构造方法实现
public FlutterNativeView(Context context) {
this(context, false);
}
public FlutterNativeView(Context context, boolean isBackgroundView) {
mContext = context;
mPluginRegistry = new FlutterPluginRegistry(this, context);
mFlutterJNI = new FlutterJNI();
mFlutterJNI.setRenderSurface(new RenderSurfaceImpl());
mFlutterJNI.setPlatformMessageHandler(new PlatformMessageHandlerImpl());
mFlutterJNI.addEngineLifecycleListener(new EngineLifecycleListenerImpl());
attach(this, isBackgroundView);
assertAttached();
mMessageHandlers = new HashMap<>();
}
方法中会初始化mFlutterJNI对象,该对象的作用是通过JNI方法来传递信息给c++层,以便其根据不同的指令来通知flutter engine执行对应的操作。包括创建并启动Flutter engine、当前FlutterView的Surface生命周期的通知、传递platform数据给dart层、回传dart层调用platform层方法返回的结果数据等等。
该构造方法中有一个关键方法调用就是attach方法,其实现如下
private void attach(FlutterNativeView view, boolean isBackgroundView) {
mFlutterJNI.attachToNative(isBackgroundView);
}
通过mFlutterJNI的attachToNative方法实现java层和c++层的连接,实现如下
@UiThread
public void attachToNative(boolean isBackgroundView) {
ensureNotAttachedToNative();
nativePlatformViewId = nativeAttach(this, isBackgroundView);
}
private native long nativeAttach(FlutterJNI flutterJNI, boolean isBackgroundView);
attachToNative方法中通过调用JNI方法nativeAttach将当前flutterJNI对象传递给c++层(后续一些dart层调用java层的方法就是通过该flutterJNI对象调用对应的方法实现的,比如上一篇分享的platform channel就使用到了),得到c++层返回的nativePlatformViewId(该值非常重要,是c++层AndroidShellHolder的对象指针值,后续会通过该值调用一系列c++层的方法执行操作)并将其保存以供后续使用。
这个阶段关键java类的UML类图
接着看下nativeAttach方法在c++中的实现,该方法非常重要,一系列Flutter engine的初始化就是在这里做的。
static jlong AttachJNI(JNIEnv* env,
jclass clazz,
jobject flutterJNI,
jboolean is_background_view) {
fml::jni::JavaObjectWeakGlobalRef java_object(env, flutterJNI);
auto shell_holder = std::make_unique<AndroidShellHolder>(
FlutterMain::Get().GetSettings(), java_object, is_background_view);
if (shell_holder->IsValid()) {
return reinterpret_cast<jlong>(shell_holder.release());
} else {
return 0;
}
}
我们发现该方法中通过之前初始化的保存在gfluttermain对象中的settings值和传入的java对象flutterJNI创建std::uniqueptr对象(该对象通过指针占有并管理AndroidShellHolder对象),该对象有效的情况下会调用release方法返回其管理对象的指针并释放对象的所有权,reinterpretcast()方法将该AndroidShellHolder对象指针强制转化为long类型的值并返回java层保存。
接下来我们通过AndroidShellHolder构造函数的实现来分析创建对象时都做了哪些操作
AndroidShellHolder::AndroidShellHolder(
blink::Settings settings,
fml::jni::JavaObjectWeakGlobalRef java_object,
bool is_background_view)
: settings_(std::move(settings)), java_object_(java_object) {
static size_t shell_count = 1;
auto thread_label = std::to_string(shell_count++);
...
if (is_background_view) {
thread_host_ = {thread_label, ThreadHost::Type::UI};
} else {
thread_host_ = {thread_label, ThreadHost::Type::UI | ThreadHost::Type::GPU |
ThreadHost::Type::IO};
}
...
fml::WeakPtr<PlatformViewAndroid> weak_platform_view;
Shell::CreateCallback<PlatformView> on_create_platform_view =
[is_background_view, java_object, &weak_platform_view](Shell& shell) {
...
return platform_view_android;
};
Shell::CreateCallback<Rasterizer> on_create_rasterizer = [](Shell& shell) {
return std::make_unique<Rasterizer>(shell.GetTaskRunners());
};
// The current thread will be used as the platform thread. Ensure that the
// message loop is initialized.
fml::MessageLoop::EnsureInitializedForCurrentThread();
fml::RefPtr<fml::TaskRunner> gpu_runner;
fml::RefPtr<fml::TaskRunner> ui_runner;
fml::RefPtr<fml::TaskRunner> io_runner;
fml::RefPtr<fml::TaskRunner> platform_runner =
fml::MessageLoop::GetCurrent().GetTaskRunner();
if (is_background_view) {
...
} else {
gpu_runner = thread_host_.gpu_thread->GetTaskRunner();
ui_runner = thread_host_.ui_thread->GetTaskRunner();
io_runner = thread_host_.io_thread->GetTaskRunner();
}
blink::TaskRunners task_runners(thread_label, // label
platform_runner, // platform
gpu_runner, // gpu
ui_runner, // ui
io_runner // io
);
shell_ =
Shell::Create(task_runners, // task runners
settings_, // settings
on_create_platform_view, // platform view create callback
on_create_rasterizer // rasterizer create callback
);
platform_view_ = weak_platform_view;
FML_DCHECK(platform_view_);
...
}
}
传入的参数is_background_view
值为false,通过前半部分代码我们发现会新建三个线程保存到thread_host_
中,分别为gpu_thread
、ui_thread
和io_thread
,而当前的线程也即是platform层的UI主线程作为platform_thread
存在,四个线程分别持有一个TaskRunner对象,对应gpu_runner
、ui_runner
、io_runner
和platform_runner
,后续会通过这些TaskRunner来将一些操作放到对应的线程中去执行,下面大致列一下各线程在Flutter engine中的主要职责。
Platform Thread:Flutter Engine的接口调用
GPU Thread:执行设备GPU的指令
UI Thread:执行Dart root isolate代码
IO Thread:读取并处理图片数据
接下来会根据四个线程对应的TaskRunner创建task_runners
对象,然后通过Shell::Create()
方法创建shell_
对象,看下该方法的具体实现
std::unique_ptr<Shell> Shell::Create(
blink::TaskRunners task_runners,
blink::Settings settings,
Shell::CreateCallback<PlatformView> on_create_platform_view,
Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
PerformInitializationTasks(settings);
auto vm = blink::DartVM::ForProcess(settings);
FML_CHECK(vm) << "Must be able to initialize the VM.";
return Shell::Create(std::move(task_runners), //
std::move(settings), //
vm->GetIsolateSnapshot(), //
blink::DartSnapshot::Empty(), //
std::move(on_create_platform_view), //
std::move(on_create_rasterizer) //
);
}
首先执行初始化任务(包括初始化绑定到skia的跟踪事件、skia引擎的初始化、国际化组件初始化等),接着根据settings值创建DartVM对象初始化Dart虚拟机,最后创建std::unique_ptr对象并返回。我们先看一下DartVM的创建过程
fml::RefPtr<DartVM> DartVM::ForProcess(Settings settings) {
return ForProcess(settings, nullptr, nullptr, nullptr);
}
static std::once_flag gVMInitialization;
static std::mutex gVMMutex;
static fml::RefPtr<DartVM> gVM;
fml::RefPtr<DartVM> DartVM::ForProcess(
Settings settings,
fml::RefPtr<DartSnapshot> vm_snapshot,
fml::RefPtr<DartSnapshot> isolate_snapshot,
fml::RefPtr<DartSnapshot> shared_snapshot) {
std::lock_guard<std::mutex> lock(gVMMutex);
std::call_once(gVMInitialization, [settings, //
vm_snapshot, //
isolate_snapshot, //
shared_snapshot //
]() mutable {
if (!vm_snapshot) {
vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings);
}
...
if (!isolate_snapshot) {
isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings);
}
...
gVM = fml::MakeRefCounted<DartVM>(settings, //
std::move(vm_snapshot), //
std::move(isolate_snapshot), //
std::move(shared_snapshot) //
);
});
return gVM;
}
此块代码表示创建DartVM对象的代码块只执行一次,即使从多个线程中被调用也是执行一次,保证DartVM只初始化一次,下面看下DartVM的构造方法实现
DartVM::DartVM(const Settings& settings,
fml::RefPtr<DartSnapshot> vm_snapshot,
fml::RefPtr<DartSnapshot> isolate_snapshot,
fml::RefPtr<DartSnapshot> shared_snapshot)
: settings_(settings),
vm_snapshot_(std::move(vm_snapshot)),
isolate_snapshot_(std::move(isolate_snapshot)),
shared_snapshot_(std::move(shared_snapshot)),
weak_factory_(this) {
...
{
TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo");
dart::bin::BootstrapDartIo();
...
}
...
DartUI::InitForGlobal();
Dart_SetFileModifiedCallback(&DartFileModifiedCallback);
{
TRACE_EVENT0("flutter", "Dart_Initialize");
Dart_InitializeParams params = {};
...
params.create = reinterpret_cast<decltype(params.create)>(
DartIsolate::DartIsolateCreateCallback);
...
char* init_error = Dart_Initialize(¶ms);
...
}
...
}
第一步,通过执行dart::bin::BootstrapDartIo()
方法引导启动"dart:io"事件处理程序,具体方法调用如下
void BootstrapDartIo() {
// Bootstrap 'dart:io' event handler.
TimerUtils::InitOnce();
EventHandler::Start();
}
第二步,通过执行DartUI::InitForGlobal()
方法注册dart的各种本地方法,这些方法的注册类似于java的JNI方法注册,主要用于dart层调用c++方法(上一篇文章中通过platform channel执行dart调用platform方法就使用到了Window相关的本地方法调用),相关源码如下
void DartUI::InitForGlobal() {
if (!g_natives) {
g_natives = new tonic::DartLibraryNatives();
Canvas::RegisterNatives(g_natives);
...
FrameInfo::RegisterNatives(g_natives);
...
Window::RegisterNatives(g_natives);
// Secondary isolates do not provide UI-related APIs.
g_natives_secondary = new tonic::DartLibraryNatives();
DartRuntimeHooks::RegisterNatives(g_natives_secondary);
IsolateNameServerNatives::RegisterNatives(g_natives_secondary);
}
}
这里仅看一下Window相关的本地方法注册
void Window::RegisterNatives(tonic::DartLibraryNatives* natives) {
natives->Register({
{"Window_defaultRouteName", DefaultRouteName, 1, true},
{"Window_scheduleFrame", ScheduleFrame, 1, true},
{"Window_sendPlatformMessage", _SendPlatformMessage, 4, true},
{"Window_respondToPlatformMessage", _RespondToPlatformMessage, 3, true},
{"Window_render", Render, 2, true},
{"Window_updateSemantics", UpdateSemantics, 2, true},
{"Window_setIsolateDebugName", SetIsolateDebugName, 2, true},
});
}
第三步,通过执行Dart_Initialize(¶ms)
方法来初始化Dart运行时环境,具体实现如下,紧贴出部分代码,其它代码请自行查看源码。
DART_EXPORT char* Dart_Initialize(Dart_InitializeParams* params) {
...
return Dart::Init(params->vm_snapshot_data, params->vm_snapshot_instructions,
params->create, params->shutdown, params->cleanup,
params->thread_exit, params->file_open, params->file_read,
params->file_write, params->file_close,
params->entropy_source, params->get_service_assets,
params->start_kernel_isolate);
}
具体的初始化工作会由Dart::Init()方法实现
char* Dart::Init(const uint8_t* vm_isolate_snapshot,
const uint8_t* instructions_snapshot,
Dart_IsolateCreateCallback create,
Dart_IsolateShutdownCallback shutdown,
Dart_IsolateCleanupCallback cleanup,
Dart_ThreadExitCallback thread_exit,
Dart_FileOpenCallback file_open,
Dart_FileReadCallback file_read,
Dart_FileWriteCallback file_write,
Dart_FileCloseCallback file_close,
Dart_EntropySource entropy_source,
Dart_GetVMServiceAssetsArchive get_service_assets,
bool start_kernel_isolate) {
...
FrameLayout::Init();
...
至此,DartVM的初始化就完成了,最后DartVM对象会返回给Shell,Shell通过如下方法创建Shell对象
std::unique_ptr<Shell> Shell::Create(
blink::TaskRunners task_runners,
blink::Settings settings,
fml::RefPtr<blink::DartSnapshot> isolate_snapshot,
fml::RefPtr<blink::DartSnapshot> shared_snapshot,
Shell::CreateCallback<PlatformView> on_create_platform_view,
Shell::CreateCallback<Rasterizer> on_create_rasterizer) {
...
fml::AutoResetWaitableEvent latch;
std::unique_ptr<Shell> shell;
fml::TaskRunner::RunNowOrPostTask(
task_runners.GetPlatformTaskRunner(),
[&latch, //
&shell, //
task_runners = std::move(task_runners), //
settings, //
isolate_snapshot = std::move(isolate_snapshot), //
shared_snapshot = std::move(shared_snapshot), //
on_create_platform_view, //
on_create_rasterizer //
]() {
shell = CreateShellOnPlatformThread(std::move(task_runners), //
settings, //
std::move(isolate_snapshot), //
std::move(shared_snapshot), //
on_create_platform_view, //
on_create_rasterizer //
);
latch.Signal();
});
latch.Wait();
return shell;
}
最终会通过Shell中的CreateShellOnPlatformThread方法在Platform Thread中创建Shell对象,由于该方法中代码比较多,我们分块来进行分析,首先看一下Shell对象的创建
auto shell = std::unique_ptr<Shell>(new Shell(task_runners, settings));
通过构造方法创建shell对象,紧接着开始执行四个关键的代码块
// Create the platform view on the platform thread (this thread).
auto platform_view = on_create_platform_view(*shell.get());
if (!platform_view || !platform_view->GetWeakPtr()) {
return nullptr;
}
fml::WeakPtr<PlatformViewAndroid> weak_platform_view;
Shell::CreateCallback<PlatformView> on_create_platform_view =
[is_background_view, java_object, &weak_platform_view](Shell& shell) {
std::unique_ptr<PlatformViewAndroid> platform_view_android;
if (is_background_view) {
...
} else {
platform_view_android = std::make_unique<PlatformViewAndroid>(
shell, // delegate
shell.GetTaskRunners(), // task runners
java_object, // java object handle for JNI interop
shell.GetSettings()
.enable_software_rendering // use software rendering
);
}
weak_platform_view = platform_view_android->GetWeakPtr();
return platform_view_android;
};
关键代码块一:在platform thread中根据传入的on_create_platform_view
函数创建PlatformViewAndroid对象并交由platform_view管理,如上该函数在AndroidShellHolder的构造函数中声明。
// Create the IO manager on the IO thread.
fml::AutoResetWaitableEvent io_latch;
std::unique_ptr<IOManager> io_manager;
auto io_task_runner = shell->GetTaskRunners().GetIOTaskRunner();
fml::TaskRunner::RunNowOrPostTask(
io_task_runner,
[&io_latch, //
&io_manager, //
&platform_view, //
io_task_runner //
]() {
io_manager = std::make_unique<IOManager>(
platform_view->CreateResourceContext(), io_task_runner);
io_latch.Signal();
});
io_latch.Wait();
关键代码块二:在IO thread中创建IOManager对象,并交由io_manager管理。
// Create the rasterizer on the GPU thread.
fml::AutoResetWaitableEvent gpu_latch;
std::unique_ptr<Rasterizer> rasterizer;
fml::WeakPtr<blink::SnapshotDelegate> snapshot_delegate;
fml::TaskRunner::RunNowOrPostTask(
task_runners.GetGPUTaskRunner(), [&gpu_latch, //
&rasterizer, //
on_create_rasterizer, //
shell = shell.get(), //
&snapshot_delegate //
]() {
if (auto new_rasterizer = on_create_rasterizer(*shell)) {
rasterizer = std::move(new_rasterizer);
snapshot_delegate = rasterizer->GetSnapshotDelegate();
}
gpu_latch.Signal();
});
gpu_latch.Wait();
Shell::CreateCallback<Rasterizer> on_create_rasterizer = [](Shell& shell) {
return std::make_unique<Rasterizer>(shell.GetTaskRunners());
};
关键代码块三:在GPU thread中根据传入的on_create_rasterizer
函数创建Rasterizer对象并交由rasterizer管理,如上该函数也在AndroidShellHolder的构造函数中声明。
// Create the engine on the UI thread.
fml::AutoResetWaitableEvent ui_latch;
std::unique_ptr<Engine> engine;
fml::TaskRunner::RunNowOrPostTask(
shell->GetTaskRunners().GetUITaskRunner(),
fml::MakeCopyable([&ui_latch, //
&engine, //
shell = shell.get(), //
isolate_snapshot = std::move(isolate_snapshot), //
shared_snapshot = std::move(shared_snapshot), //
vsync_waiter = std::move(vsync_waiter), //
snapshot_delegate = std::move(snapshot_delegate), //
io_manager = io_manager->GetWeakPtr() //
]() mutable {
const auto& task_runners = shell->GetTaskRunners();
// The animator is owned by the UI thread but it gets its vsync pulses
// from the platform.
auto animator = std::make_unique<Animator>(*shell, task_runners,
std::move(vsync_waiter));
engine = std::make_unique<Engine>(*shell, //
shell->GetDartVM(), //
std::move(isolate_snapshot), //
std::move(shared_snapshot), //
task_runners, //
shell->GetSettings(), //
std::move(animator), //
std::move(snapshot_delegate), //
std::move(io_manager) //
);
ui_latch.Signal();
}));
ui_latch.Wait();
关键代码块四:在UI thread中创建Engine对象,并交由engine管理。
最后会通过shell的Setup方法调用将platform_view
、io_manager
、rasterizer
和engine
四个unique_ptr
保存到Shell对象中交由Shell对象管理
if (!shell->Setup(std::move(platform_view), //
std::move(engine), //
std::move(rasterizer), //
std::move(io_manager)) //
) {
return nullptr;
}
Shell对象通过Shell::Create()创建完成后返回给AndroidShellHolder持有。至此,Embedder层就通过Shell对象与engine层建立了连接,后续的一切操作都可以通过Shell对象来进行。而创建好的AndroidShellHolder对象指针值又返回给了java层,最终java层便可以使用该指针值通过JNI方法调用拿到Embedder层的AndroidShellHolder对象,进而通过Shell对象向engine层发送一系列操作指令。
这个阶段关键c++类的UML类图
以上流程已经为dart层代码执行创建好了运行时环境,那么接下来就应该加载dart层相关的代码执行了,这样我们就可以看到dart编写的widget显示在MainActivity界面上了。
我们回到上面分析的FlutterActivityDelegate的onCreate()方法中,当FlutterView和FlutterNativeView创建成功后,会通过activity.setContentView(flutterView);
将FlutterView作为activity的内容视图,而flutter层的UI就是被渲染到FlutterView上的,所以当前MainActivity展示出来的就是我们的flutter UI界面。
当然,代码走到此时,dart层代码还没有运行,所以界面上还是显示空白,我们看onCreate()代码块的最后部分,找到appBundle然后通过runBundle方法开始执行,runBundle方法如下
private void runBundle(String appBundlePath) {
if (!flutterView.getFlutterNativeView().isApplicationRunning()) {
FlutterRunArguments args = new FlutterRunArguments();
ArrayList<String> bundlePaths = new ArrayList<>();
ResourceUpdater resourceUpdater = FlutterMain.getResourceUpdater();
if (resourceUpdater != null) {
File patchFile = resourceUpdater.getInstalledPatch();
JSONObject manifest = resourceUpdater.readManifest(patchFile);
if (resourceUpdater.validateManifest(manifest)) {
bundlePaths.add(patchFile.getPath());
}
}
bundlePaths.add(appBundlePath);
args.bundlePaths = bundlePaths.toArray(new String[0]);
args.entrypoint = "main";
flutterView.runFromBundle(args);
}
}
第一次启动flutter页面isApplicationRunning()为false,执行if语句后的代码块,先检查是否有更新的flutter相关资源(用于动态更新,2019年flutter团队的一个目标之一,这里是先预埋了代码,应该还没有起作用),没有更新的bundle包,则设置对应的运行参数,然后使用flutterView.runFromBundle()
方法开始执行。
public void runFromBundle(FlutterRunArguments args) {
assertAttached();
preRun();
mNativeView.runFromBundle(args);
postRun();
}
调用FlutterNativeView的runFromBundle方法执行
public void runFromBundle(FlutterRunArguments args) {
boolean hasBundlePaths = args.bundlePaths != null && args.bundlePaths.length != 0;
...
if (hasBundlePaths) {
runFromBundleInternal(args.bundlePaths, args.entrypoint, args.libraryPath);
} else {
...
}
}
private void runFromBundleInternal(String[] bundlePaths, String entrypoint,
String libraryPath) {
...
mFlutterJNI.runBundleAndSnapshotFromLibrary(
bundlePaths,
entrypoint,
libraryPath,
mContext.getResources().getAssets()
);
applicationIsRunning = true;
}
最终是通过FlutterJNI的方法来调用JNI方法执行
@UiThread
public void runBundleAndSnapshotFromLibrary(
@NonNull String[] prioritizedBundlePaths,
@Nullable String entrypointFunctionName,
@Nullable String pathToEntrypointFunction,
@NonNull AssetManager assetManager
) {
ensureAttachedToNative();
nativeRunBundleAndSnapshotFromLibrary(
nativePlatformViewId,
prioritizedBundlePaths,
entrypointFunctionName,
pathToEntrypointFunction,
assetManager
);
}
private native void nativeRunBundleAndSnapshotFromLibrary(
long nativePlatformViewId,
@NonNull String[] prioritizedBundlePaths,
@Nullable String entrypointFunctionName,
@Nullable String pathToEntrypointFunction,
@NonNull AssetManager manager
);
到这里,我们发现会调用JNI方法传入nativePlatformViewId参数,这个就是我们前面提到的AndroidShellHolder对象的指针值,此时的prioritizedBundlePaths数组中只有一个值类似"/data/data/包名/flutter/flutter_assets/"的路径值,entrypointFunctionName为"main",pathToEntrypointFunction为null。接下来看下JNI对应c++方法的实现
static void RunBundleAndSnapshotFromLibrary(JNIEnv* env,
jobject jcaller,
jlong shell_holder,
jobjectArray jbundlepaths,
jstring jEntrypoint,
jstring jLibraryUrl,
jobject jAssetManager) {
auto asset_manager = std::make_shared<blink::AssetManager>();
for (const auto& bundlepath :
fml::jni::StringArrayToVector(env, jbundlepaths)) {
...
const auto file_ext_index = bundlepath.rfind(".");
if (bundlepath.substr(file_ext_index) == ".zip") {
...
} else {
asset_manager->PushBack(
std::make_unique<blink::DirectoryAssetBundle>(fml::OpenDirectory(
bundlepath.c_str(), false, fml::FilePermission::kRead)));
...
}
}
auto isolate_configuration = CreateIsolateConfiguration(*asset_manager);
...
RunConfiguration config(std::move(isolate_configuration),
std::move(asset_manager));
{
auto entrypoint = fml::jni::JavaStringToString(env, jEntrypoint);
auto libraryUrl = fml::jni::JavaStringToString(env, jLibraryUrl);
if ((entrypoint.size() > 0) && (libraryUrl.size() > 0)) {
...
} else if (entrypoint.size() > 0) {
config.SetEntrypoint(std::move(entrypoint));
}
}
ANDROID_SHELL_HOLDER->Launch(std::move(config));
}
该段代码首先将循环jbundlepaths中的信息将根据bundlepath创建DirectoryAssetBundle对象放到交由assetmanager管理,然后创建运行配置对象config,最后通过ANDROID_SHELL_HOLDER->Launch(std::move(config));
根据运行配置信息启动。注意ANDROIDSHELL_HOLDER是一个宏,具体实现为
#define ANDROID_SHELL_HOLDER \
(reinterpret_cast<shell::AndroidShellHolder*>(shell_holder))
即是将传过来的java层持有的AndroidShellHolder指针值强转为AndroidShellHolder对象指针,此时就可以通过对象指针调用其方法执行所需要的操作了。接下来看下Launch方法的实现
void AndroidShellHolder::Launch(RunConfiguration config) {
...
shell_->GetTaskRunners().GetUITaskRunner()->PostTask(
fml::MakeCopyable([engine = shell_->GetEngine(), //
config = std::move(config) //
]() mutable {
...
if (!engine || engine->Run(std::move(config)) ==
shell::Engine::RunStatus::Failure) {
...
} else {
...
}
}));
}
我们可以看到engine运行dart层代码是通过UITaskRunner在UI Thread中执行的,这就是前面说的创建UI Thread的主要作用,下面看下engine的Run方法
Engine::RunStatus Engine::Run(RunConfiguration configuration) {
...
auto isolate_launch_status =
PrepareAndLaunchIsolate(std::move(configuration));
...
return isolate_running ? Engine::RunStatus::Success
: Engine::RunStatus::Failure;
}
shell::Engine::RunStatus Engine::PrepareAndLaunchIsolate(
RunConfiguration configuration) {
...
auto isolate_configuration = configuration.TakeIsolateConfiguration();
std::shared_ptr<blink::DartIsolate> isolate =
runtime_controller_->GetRootIsolate().lock();
...
if (configuration.GetEntrypointLibrary().empty()) {
if (!isolate->Run(configuration.GetEntrypoint())) {
...
}
} else {
...
}
return RunStatus::Success;
}
最终会通过DartIsolate的Run方法来执行
bool DartIsolate::Run(const std::string& entrypoint_name) {
...
Dart_Handle entrypoint =
Dart_GetField(Dart_RootLibrary(), tonic::ToDart(entrypoint_name.c_str()));
...
Dart_Handle isolate_lib = Dart_LookupLibrary(tonic::ToDart("dart:isolate"));
if (tonic::LogIfError(isolate_lib)) {
return false;
}
Dart_Handle isolate_args[] = {
entrypoint,
Dart_Null(),
};
if (tonic::LogIfError(Dart_Invoke(
isolate_lib, tonic::ToDart("_startMainIsolate"),
sizeof(isolate_args) / sizeof(isolate_args[0]), isolate_args))) {
return false;
}
...
return true;
}
通过entrypoint_name创建执行入口Dart句柄entrypoint,通过Dart_LookupLibrary方法查找"dart:isolate"库的句柄isolate_lib,这里需要注意isolate_args[]句柄数组,第一个值为entrypoint,第二个值为Dart_Null(),然后通过Dart_Invoke方法调用执行
DART_EXPORT Dart_Handle Dart_Invoke(Dart_Handle target,
Dart_Handle name,
int number_of_arguments,
Dart_Handle* arguments) {
...
String& function_name =
String::Handle(Z, Api::UnwrapStringHandle(Z, name).raw());
...
const Object& obj = Object::Handle(Z, Api::UnwrapHandle(target));
...
if (obj.IsType()) {
...
} else if (obj.IsNull() || obj.IsInstance()) {
...
} else if (obj.IsLibrary()) {
// Check whether class finalization is needed.
const Library& lib = Library::Cast(obj);
...
if (Library::IsPrivate(function_name)) {
function_name = lib.PrivateName(function_name);
}
...
return Api::NewHandle(
T, lib.Invoke(function_name, args, arg_names, respect_reflectable));
} else {
...
}
}
通过上面isolate_lib的创建方法Dart_LookupLibrary的实现可知obj为一个Library对象,最后通过lib.Invoke()
方法来执行dart方法,dart中对应的具体方法实现为
@pragma("vm:entry-point")
void _startMainIsolate(Function entryPoint, List<String> args) {
_startIsolate(
null, // no parent port
entryPoint,
args,
null, // no message
true, // isSpawnUri
null, // no control port
null); // no capabilities
}
第一个参数entryPoint即为前文通过"main"查找的main()入口函数,也即是我们编写的dart中main.dart文件中的main()函数,args为null最终通过调用以下_startIsolate方法运行。
@pragma("vm:entry-point")
void _startIsolate(
SendPort parentPort,
Function entryPoint,
List<String> args,
var message,
bool isSpawnUri,
RawReceivePort controlPort,
List capabilities) {
if (controlPort != null) {
controlPort.handler = (_) {}; // Nobody home on the control port.
}
...
RawReceivePort port = new RawReceivePort();
port.handler = (_) {
port.close();
if (isSpawnUri) {
if (entryPoint is _BinaryFunction) {
(entryPoint as dynamic)(args, message);
} else if (entryPoint is _UnaryFunction) {
(entryPoint as dynamic)(args);
} else {
entryPoint();
}
} else {
entryPoint(message);
}
};
// Make sure the message handler is triggered.
port.sendPort.send(null);
}
我们在mait.dart中定义的main()函数并没有任何参数,最后直接通过entryPoint()
将main()函数调起。到此,我们的dart层代码就运行起来了。后续就是通过main()函数中的runApp()方法调用开始执行各种Widget相关绑定、Element的创建、RenderObject的创建,然后合成帧数据供下一次gsync信号接收时渲染数据到SurfaceView上。
iOS平台对应的Embedder层代码在engine源码的/flutter/shell/platform/darwin/
目录下
我们同样根据flutter create my_app
命令创建的Flutter项目demo来分析iOS平台上flutter的启动流程。
AppDelegate继承自FlutterAppDelegate,我们看下FlutterAppDelegate的生命周期执行情况
- (instancetype)init {
if (self = [super init]) {
_lifeCycleDelegate = [[FlutterPluginAppLifeCycleDelegate alloc] init];
}
return self;
}
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
return [_lifeCycleDelegate application:application willFinishLaunchingWithOptions:launchOptions];
}
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
return [_lifeCycleDelegate application:application didFinishLaunchingWithOptions:launchOptions];
}
和安卓中类似,FluttAppDelegate生命周期方法中的处理由代理类对象_lifeCycleDelegate做具体处理,init方法中会对该对象进行初始化,我们先看下初始化和对应的生命周期代理方法都做了什么
static const char* kCallbackCacheSubDir = "Library/Caches/";
- (instancetype)init {
if (self = [super init]) {
std::string cachePath = fml::paths::JoinPaths({getenv("HOME"), kCallbackCacheSubDir});
[FlutterCallbackCache setCachePath:[NSString stringWithUTF8String:cachePath.c_str()]];
_pluginDelegates = [[NSPointerArray weakObjectsPointerArray] retain];
}
return self;
}
- (BOOL)application:(UIApplication*)application
didFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
for (id<FlutterPlugin> plugin in [_pluginDelegates allObjects]) {
if (!plugin) {
continue;
}
...
}
return YES;
}
- (BOOL)application:(UIApplication*)application
willFinishLaunchingWithOptions:(NSDictionary*)launchOptions {
blink::DartCallbackCache::LoadCacheFromDisk();
for (id<FlutterPlugin> plugin in [_pluginDelegates allObjects]) {
if (!plugin) {
continue;
}
...
}
return YES;
}
初始化方法中会获取一个缓存目录,并设置到FlutterCallbackCache中。而两个生命周期代理方法中会遍历_pluginDelegates中的对象,但此时数组中还没有信息。 接下来我们看下Main.storyboard会发现应用的rootViewController为FlutterViewController,那我们来看一下FlutterViewController的初始化和生命周期方法
- (instancetype)initWithProject:(FlutterDartProject*)projectOrNil
nibName:(NSString*)nibNameOrNil
bundle:(NSBundle*)nibBundleOrNil {
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self) {
_viewOpaque = YES;
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(self);
_engine.reset([[FlutterEngine alloc] initWithName:@"io.flutter"
project:projectOrNil
allowHeadlessExecution:NO]);
_flutterView.reset([[FlutterView alloc] initWithDelegate:_engine opaque:self.isViewOpaque]);
[_engine.get() createShell:nil libraryURI:nil];
_engineNeedsLaunch = YES;
[self loadDefaultSplashScreenView];
[self performCommonViewControllerInitialization];
}
return self;
}
初始化方法中会创建FlutterEngine对象、FlutterView对象,并根据FlutterEngine对象创建Shell对象,我们先看下FlutterEngine对象的创建
- (instancetype)initWithName:(NSString*)labelPrefix
project:(FlutterDartProject*)projectOrNil
allowHeadlessExecution:(BOOL)allowHeadlessExecution {
...
_allowHeadlessExecution = allowHeadlessExecution;
_labelPrefix = [labelPrefix copy];
_weakFactory = std::make_unique<fml::WeakPtrFactory<FlutterEngine>>(self);
if (projectOrNil == nil)
_dartProject.reset([[FlutterDartProject alloc] init]);
else
_dartProject.reset([projectOrNil retain]);
_pluginPublications = [NSMutableDictionary new];
_platformViewsController.reset(new shell::FlutterPlatformViewsController());
[self setupChannels];
return self;
}
此时传入的projectOrNil为nil,则会创建FlutterDartProject对象并保存到_dartProject中,FlutterDartProject初始化
static blink::Settings DefaultSettingsForProcess(NSBundle* bundle = nil) {
auto command_line = shell::CommandLineFromNSProcessInfo();
NSBundle* mainBundle = [NSBundle mainBundle];
NSBundle* engineBundle = [NSBundle bundleForClass:[FlutterViewController class]];
...
auto settings = shell::SettingsFromCommandLine(command_line);
settings.task_observer_add = [](intptr_t key, fml::closure callback) {
fml::MessageLoop::GetCurrent().AddTaskObserver(key, std::move(callback));
};
settings.task_observer_remove = [](intptr_t key) {
fml::MessageLoop::GetCurrent().RemoveTaskObserver(key);
};
...
return settings;
}
- (instancetype)init {
return [self initWithPrecompiledDartBundle:nil];
}
- (instancetype)initWithPrecompiledDartBundle:(NSBundle*)bundle {
self = [super init];
if (self) {
_precompiledDartBundle.reset([bundle retain]);
_settings = DefaultSettingsForProcess(bundle);
}
return self;
}
初始化方法中会调用c++代码方法DefaultSettingsForProcess来完成_settings对象的初始化,主要是各种flutter资源路径设置(国际化库、framework库等)和一些其他后续需要的信息配置。
然后回到FlutterEngine初始化方法中,继续完成各种platform channel的创建。
接着看下FlutterView对象的创建
- (instancetype)initWithDelegate:(id<FlutterViewEngineDelegate>)delegate opaque:(BOOL)opaque {
FML_DCHECK(delegate) << "Delegate must not be nil.";
self = [super initWithFrame:CGRectNull];
if (self) {
_delegate = delegate;
self.layer.opaque = opaque;
}
return self;
}
做了很少的事情,主要是传入并持有实现了FlutterViewEngineDelegate协议的FlutterEngine对象。最后看下Shell的创建
- (BOOL)createShell:(NSString*)entrypoint libraryURI:(NSString*)libraryURI {
...
static size_t shellCount = 1;
auto settings = [_dartProject.get() settings];
if (libraryURI) {
...
} else if (entrypoint) {
...
} else {
settings.advisory_script_entrypoint = std::string("main");
settings.advisory_script_uri = std::string("main.dart");
}
const auto threadLabel = [NSString stringWithFormat:@"%@.%zu", _labelPrefix, shellCount++];
fml::MessageLoop::EnsureInitializedForCurrentThread();
_threadHost = {
threadLabel.UTF8String, // label
shell::ThreadHost::Type::UI | shell::ThreadHost::Type::GPU | shell::ThreadHost::Type::IO};
shell::Shell::CreateCallback<shell::PlatformView> on_create_platform_view =
[](shell::Shell& shell) {
return std::make_unique<shell::PlatformViewIOS>(shell, shell.GetTaskRunners());
};
shell::Shell::CreateCallback<shell::Rasterizer> on_create_rasterizer = [](shell::Shell& shell) {
return std::make_unique<shell::Rasterizer>(shell.GetTaskRunners());
};
if (shell::IsIosEmbeddedViewsPreviewEnabled()) {
...
} else {
blink::TaskRunners task_runners(threadLabel.UTF8String, // label
fml::MessageLoop::GetCurrent().GetTaskRunner(), // platform
_threadHost.gpu_thread->GetTaskRunner(), // gpu
_threadHost.ui_thread->GetTaskRunner(), // ui
_threadHost.io_thread->GetTaskRunner() // io
);
// Create the shell. This is a blocking operation.
_shell = shell::Shell::Create(std::move(task_runners), // task runners
std::move(settings), // settings
on_create_platform_view, // platform view creation
on_create_rasterizer // rasterzier creation
);
}
...
return _shell != nullptr;
}
该方法的实现和安卓中的AndroidShellHolder构造方法实现类似,主要新创建了三个线程gpu_thread
、ui_thread
和io_thread
,加上平台的UI线程作为platform_thread
一共四个关键线程。线程的作用可以参考安卓中的说明。然后会通过Shell::Create()
方法创建engine层的Shell对象,后边的engine初始化、DartVM初始化和其他一系列对象创建都和安卓上面分析的一样了,这里不再赘述。
回过头来看一下FlutterViewController中初始化方法,上面一系列操作执行后,会进行各种必要的通知注册,以便接收到通知后做出响应。
至此FlutterViewController的初始化就完成了,我们可以看出整个的初始化就是对flutter engine的初始化,那么iOS平台dart层代码是在哪一步执行的呢,我们下面看一下FlutterViewController的生命周期方法viewWillAppear方法
- (void)viewWillAppear:(BOOL)animated {
TRACE_EVENT0("flutter", "viewWillAppear");
if (_engineNeedsLaunch) {
[_engine.get() launchEngine:nil libraryURI:nil];
_engineNeedsLaunch = NO;
}
[_engine.get() setViewController:self];
if (_viewportMetrics.physical_width)
[self surfaceUpdated:YES];
[[_engine.get() lifecycleChannel] sendMessage:@"AppLifecycleState.inactive"];
[super viewWillAppear:animated];
}
这里engineNeedsLaunch在FlutterViewController初始化的时候被设置为YES,则会通过engine开始启动引擎
- (void)launchEngine:(NSString*)entrypoint libraryURI:(NSString*)libraryOrNil {
// Launch the Dart application with the inferred run configuration.
self.shell.GetTaskRunners().GetUITaskRunner()->PostTask(fml::MakeCopyable(
[engine = _shell->GetEngine(),
config = [_dartProject.get() runConfigurationForEntrypoint:entrypoint
libraryOrNil:libraryOrNil] //
]() mutable {
if (engine) {
auto result = engine->Run(std::move(config));
if (result == shell::Engine::RunStatus::Failure) {
FML_LOG(ERROR) << "Could not launch engine with configuration.";
}
}
}));
}
这里engine运行dart层代码是通过UITaskRunner在UI Thread中执行的,该方法和安卓Embedder层AndroidShellHolder的Launch方法功能相同。不过我们发现该方法两个参数都为nil,而安卓中的entrypoint为"main",那么iOS中最终是如何执行Dart应用程序执行main()方法的呢,我们看下RunConfiguration类的声明中一些变量的声明
class RunConfiguration {
public:
...
RunConfiguration(RunConfiguration&&);
~RunConfiguration();
...
const std::string& GetEntrypoint() const;
const std::string& GetEntrypointLibrary() const;
...
private:
...
std::string entrypoint_ = "main";
std::string entrypoint_library_ = "";
FML_DISALLOW_COPY_AND_ASSIGN(RunConfiguration);
}
结果我们发现RunConfiguration对象创建时默认entrypoint即为"main",所以不用主动设置entrypoint,最终Dart代码执行时就会将main()方法作为入口函数执行。后续的一些engine层的操作和安卓一样了,这里也不再赘述。
这个阶段关键objective-c和c++类的UML类图
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。