优化:启动速度优化
This commit is contained in:
parent
42d9cf3fd2
commit
79d4e32e3e
@ -13,7 +13,7 @@
|
||||
<application
|
||||
android:usesCleartextTraffic="${usesCleartextTraffic}"
|
||||
android:label="PetsHero AI"
|
||||
android:name="${applicationName}"
|
||||
android:name=".PetsHeroApplication"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
|
||||
@ -8,9 +8,8 @@ import io.flutter.embedding.android.FlutterActivity
|
||||
class MainActivity : FlutterActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
// 必须在 super.onCreate 之前初始化,否则 facebook_app_events 插件注册时会因 SDK 未初始化而崩溃
|
||||
FacebookSdk.sdkInitialize(applicationContext)
|
||||
// Facebook SDK 调试日志:需 buildConfigField FACEBOOK_DEBUG_LOGS=true
|
||||
super.onCreate(savedInstanceState)
|
||||
// Facebook SDK 调试日志(SDK 已在 [PetsHeroApplication] 中初始化)
|
||||
if (BuildConfig.FACEBOOK_DEBUG_LOGS) {
|
||||
try {
|
||||
FacebookSdk.setIsDebugEnabled(true)
|
||||
@ -19,6 +18,5 @@ class MainActivity : FlutterActivity() {
|
||||
FacebookSdk.addLoggingBehavior(LoggingBehavior.DEVELOPER_ERRORS)
|
||||
} catch (_: Exception) { /* ignore */ }
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,15 @@
|
||||
package com.petsheroai.app
|
||||
|
||||
import android.app.Application
|
||||
import com.facebook.FacebookSdk
|
||||
|
||||
/**
|
||||
* 将 Facebook SDK 初始化从 [MainActivity] 挪到 Application,避免阻塞 Activity.onCreate,
|
||||
* 让 Flutter 引擎与首帧更早开始(减轻「一直停在原生启动屏」的概率)。
|
||||
*/
|
||||
class PetsHeroApplication : Application() {
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
FacebookSdk.sdkInitialize(applicationContext)
|
||||
}
|
||||
}
|
||||
90
lib/app.dart
90
lib/app.dart
@ -1,5 +1,8 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:facebook_app_events/facebook_app_events.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||
|
||||
import 'core/auth/auth_service.dart';
|
||||
import 'core/nav/app_route_observer.dart';
|
||||
@ -41,6 +44,8 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
// 冷启动时进程已在 resumed,didChangeAppLifecycleState 往往收不到「变为 resumed」,需首帧后再手动打一次
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
// 去掉原生启动屏(黑底+Logo),进入 Flutter;之后由登录遮罩承接等待提示
|
||||
FlutterNativeSplash.remove();
|
||||
_reportFacebookActivateApp('first_frame');
|
||||
});
|
||||
}
|
||||
@ -96,18 +101,7 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
return Positioned.fill(
|
||||
child: AbsorbPointer(
|
||||
child: Container(
|
||||
color: Colors.black.withValues(alpha: 0.2),
|
||||
child: const Center(
|
||||
child: CircularProgressIndicator(
|
||||
color: AppColors.primary,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return const _StartupLoginOverlay();
|
||||
},
|
||||
),
|
||||
],
|
||||
@ -144,6 +138,78 @@ class _AppState extends State<App> with WidgetsBindingObserver {
|
||||
}
|
||||
}
|
||||
|
||||
/// 启动阶段等待 [AuthService.loginComplete]:久等时提示网络/服务端问题,避免用户误以为「死机」。
|
||||
class _StartupLoginOverlay extends StatefulWidget {
|
||||
const _StartupLoginOverlay();
|
||||
|
||||
@override
|
||||
State<_StartupLoginOverlay> createState() => _StartupLoginOverlayState();
|
||||
}
|
||||
|
||||
class _StartupLoginOverlayState extends State<_StartupLoginOverlay> {
|
||||
static const _longWaitAfter = Duration(seconds: 22);
|
||||
Timer? _longWaitTimer;
|
||||
bool _showNetworkHint = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_longWaitTimer = Timer(_longWaitAfter, () {
|
||||
if (mounted) setState(() => _showNetworkHint = true);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_longWaitTimer?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Positioned.fill(
|
||||
child: AbsorbPointer(
|
||||
child: Container(
|
||||
color: Colors.black.withValues(alpha: 0.22),
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 28),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const CircularProgressIndicator(color: AppColors.primary),
|
||||
if (_showNetworkHint) ...[
|
||||
const SizedBox(height: 22),
|
||||
Text(
|
||||
'网络较慢或暂时无法连接',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: AppColors.surface.withValues(alpha: 0.96),
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'请检查 Wi‑Fi 或移动数据,稍候片刻。若网络正常,可能是服务器繁忙,请过一会再试。',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: AppColors.surface.withValues(alpha: 0.78),
|
||||
fontSize: 14,
|
||||
height: 1.4,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _MainScaffold extends StatefulWidget {
|
||||
const _MainScaffold({
|
||||
required this.currentTab,
|
||||
|
||||
@ -37,4 +37,7 @@ abstract final class ApiConfig {
|
||||
|
||||
/// 代理入口完整 URL
|
||||
static String get proxyUrl => '$baseUrl$proxyPath';
|
||||
|
||||
/// 单次 HTTP 请求超时(避免弱网/服务端无响应时永久卡住启动流程)
|
||||
static const Duration httpRequestTimeout = Duration(seconds: 20);
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
@ -282,11 +283,22 @@ class ProxyClient {
|
||||
|
||||
final url = '$_baseUrl${ApiConfig.proxyPath}';
|
||||
_log('真实请求URL: $url');
|
||||
final response = await http.post(
|
||||
Uri.parse(url),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode(proxyBody),
|
||||
);
|
||||
http.Response response;
|
||||
try {
|
||||
response = await http
|
||||
.post(
|
||||
Uri.parse(url),
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: jsonEncode(proxyBody),
|
||||
)
|
||||
.timeout(ApiConfig.httpRequestTimeout);
|
||||
} on TimeoutException {
|
||||
_proxyLog.d('HTTP 超时 (${ApiConfig.httpRequestTimeout.inSeconds}s): $path');
|
||||
return ApiResponse(
|
||||
code: -1,
|
||||
msg: 'Request timed out. Check your network and try again.',
|
||||
);
|
||||
}
|
||||
|
||||
return _parseResponse(response);
|
||||
}
|
||||
|
||||
@ -290,12 +290,8 @@ class AuthService {
|
||||
UserState.setNavigate(countryCode);
|
||||
}
|
||||
|
||||
// 3. 归因:android_adjust、gg 各上报一次(均等待响应);再拉一次 common_info,extConfig 影响首页时重载分类/任务
|
||||
try {
|
||||
await _reportBothReferrersAndRefreshCommonInfo(uid!, deviceId);
|
||||
} catch (e) {
|
||||
_logMsg('referrer/common_info 流程异常: $e');
|
||||
}
|
||||
// 3. 归因与 common_info:后台执行,不阻塞 loginComplete(避免服务端慢/挂起导致一直卡在启动遮罩)
|
||||
unawaited(_runPostLoginReferrerWork(uid!, deviceId));
|
||||
} else {
|
||||
_logMsg('init: 登录失败');
|
||||
}
|
||||
@ -371,4 +367,12 @@ class AuthService {
|
||||
'common_info 失败: code=${commonRes.code} msg=${commonRes.msg}');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<void> _runPostLoginReferrerWork(String uid, String deviceId) async {
|
||||
try {
|
||||
await _reportBothReferrersAndRefreshCommonInfo(uid, deviceId);
|
||||
} catch (e, _) {
|
||||
_logMsg('referrer/common_info 后台任务失败: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -21,6 +21,12 @@ class ReferrerService {
|
||||
/// 过长会拖慢 [AuthService.init] 内首次 getReferrer;过短可能拿不到 Adjust,仍有 Play Install Referrer fallback
|
||||
static const int _adjustTimeoutMs = 4500;
|
||||
|
||||
/// 与 [Future.any] 外层兜底,避免回调 Future 永不完成时永远挂起
|
||||
static const Duration _adjustRaceTimeout = Duration(seconds: 6);
|
||||
|
||||
/// Play Install Referrer 读取超时(部分机型/系统上可能极慢)
|
||||
static const Duration _playInstallReferrerTimeout = Duration(seconds: 10);
|
||||
|
||||
/// 由 Adjust attributionCallback 调用,首次安装时归因往往通过此回调返回(晚于 getAttributionWithTimeout)
|
||||
static void receiveAttributionFromCallback(AdjustAttribution attribution) {
|
||||
if (!_attributionCallbackCompleter.isCompleted) {
|
||||
@ -79,7 +85,7 @@ class ReferrerService {
|
||||
}
|
||||
})(),
|
||||
_attributionCallbackCompleter.future,
|
||||
]);
|
||||
]).timeout(_adjustRaceTimeout, onTimeout: () => null);
|
||||
if (attribution != null) {
|
||||
final raw = _attributionToDigest(attribution);
|
||||
if (raw.isNotEmpty) {
|
||||
@ -95,7 +101,8 @@ class ReferrerService {
|
||||
_referrerSource = 'gg';
|
||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||
try {
|
||||
final details = await PlayInstallReferrer.installReferrer;
|
||||
final details = await PlayInstallReferrer.installReferrer
|
||||
.timeout(_playInstallReferrerTimeout);
|
||||
digest = details.installReferrer ?? '';
|
||||
} catch (_) {
|
||||
digest = '';
|
||||
@ -129,7 +136,8 @@ class ReferrerService {
|
||||
static Future<String> getGgReferrerDigest() async {
|
||||
if (defaultTargetPlatform != TargetPlatform.android) return '';
|
||||
try {
|
||||
final details = await PlayInstallReferrer.installReferrer;
|
||||
final details = await PlayInstallReferrer.installReferrer
|
||||
.timeout(_playInstallReferrerTimeout);
|
||||
return details.installReferrer ?? '';
|
||||
} catch (_) {
|
||||
return '';
|
||||
|
||||
@ -24,9 +24,9 @@ void main() async {
|
||||
statusBarBrightness: Brightness.light,
|
||||
),
|
||||
);
|
||||
// Adjust 初始化后执行登录,确保登录时归因数据已就绪
|
||||
AuthService.init();
|
||||
// 先进入 Flutter 首帧,尽快结束原生启动屏;登录与内购监听紧随其后
|
||||
runApp(const App());
|
||||
AuthService.init();
|
||||
// 尽早订阅 purchaseStream,否则未确认订单不会出现在 queryPastPurchases 中,补单会为空
|
||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||
GooglePlayPurchaseService.startPendingPurchaseListener();
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user