源码分析系列:app启动流程-Application&Activity的创建
作为一个Android开发者,不了解Android启动流程,app启动流程,难免有点掉相,而且面试中也是经常被问到这一块的内容,掌握这些知识必将为自己增加竞争的筹码。之前断断续续看了一些文章,但大多都比较古老,并且很多都是抄来抄去的,很多关键的细节都没有讲清楚,比如zygote进程是如何调用app进程的main()方法的。此系列文章将带你彻底掌握Android启动->app启动。
本篇主要分析app的启动流程--application和Activity的创建,基于Android 11。
整体流程预览
Application创建
上篇文章《源码分析系列:Android11 app启动流程-进程创建》分析到app进程创建后执行ActivityThread.main()
开始创建application和activity,现在我们继续来分析源码
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
//省略部分代码
//准备主线程looper
Looper.prepareMainLooper();
//省略部分代码
//创建ActivityThread
ActivityThread thread = new ActivityThread();
//执行attach()方法
thread.attach(false, startSeq);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//开启主线程循环
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
先创建ActivityThread对象,执行attach()方法,然后开启主线程的Looper循环,这里有一个小知识点,面试会经常被问到,为什么主线程里面的Looper循环不会导致anr,其原因有两方面解释
- Android程序本身也是个java程序,只要一个方法的代码执行完了,程序也就终止了,所以不可能让main方法执行完后app就退出了,所以这里的死循环是为了让app程序继续保持运行。
- Android是基于事件驱动的,所有的上层事件都由Looper里面的死循环来捕获,如果循环停止或者循环被阻塞,那么上层就无法响应任何事件了,因此,必须让这个循环流畅的执行下去,如果在这个循环中间再添加一个死循环,比如我们在Activity当中写一个死循环,此时才会导致anr,因为Looper一直卡在当前循环,进入不了下一次循环。
继续看ActivityThread.attach()
@UnsupportedAppUsage
private void attach(boolean system, long startSeq) {
sCurrentActivityThread = this;
mSystemThread = system;
if (!system) {
android.ddm.DdmHandleAppName.setAppName("<pre-initialized>",
UserHandle.myUserId());
//给zygote进程设置ApplicationThread引用,用于zygote与system_server通信
RuntimeInit.setApplicationObject(mAppThread.asBinder());
//获取AMS的binder引用
final IActivityManager mgr = ActivityManager.getService();
try {
//执行AMS的attachApplication()
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
//省略部分代码
} else {
//省略部分代码
}
//省略部分代码
}
通过binder调用AMS的attachApplication()
方法
@Override
public final void attachApplication(IApplicationThread thread, long startSeq) {
if (thread == null) {
throw new SecurityException("Invalid application interface");
}
synchronized (this) {
//获取app进程id
int callingPid = Binder.getCallingPid();
final int callingUid = Binder.getCallingUid();
final long origId = Binder.clearCallingIdentity();
//将applicationThread与当前app进程绑定
attachApplicationLocked(thread, callingPid, callingUid, startSeq);
Binder.restoreCallingIdentity(origId);
}
}
@GuardedBy("this")
private boolean attachApplicationLocked(@NonNull IApplicationThread thread,
int pid, int callingUid, long startSeq) {
//省略部分代码
try {
final ProviderInfoList providerList = ProviderInfoList.fromList(providers);
if (app.isolatedEntryPoint != null) {
// This is an isolated process which should just call an entry point instead of
// being bound to an application.
thread.runIsolatedEntryPoint(app.isolatedEntryPoint, app.isolatedEntryPointArgs);
} else if (instr2 != null) {
//执行ApplicationThread的bindApplication()
thread.bindApplication(processName, appInfo, providerList,
instr2.mClass,
profilerInfo, instr2.mArguments,
instr2.mWatcher,
instr2.mUiAutomationConnection, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions,
app.mDisabledCompatChanges);
} else {
thread.bindApplication(processName, appInfo, providerList, null, profilerInfo,
null, null, null, testMode,
mBinderTransactionTrackingEnabled, enableTrackAllocation,
isRestrictedBackupMode || !normalMode, app.isPersistent(),
new Configuration(app.getWindowProcessController().getConfiguration()),
app.compat, getCommonServicesLocked(app.isolated),
mCoreSettingsObserver.getCoreSettingsLocked(),
buildSerial, autofillOptions, contentCaptureOptions,
app.mDisabledCompatChanges);
}
//省略部分代码
} catch (Exception e) {
//省略部分代码
}
//省略部分代码
// See if the top visible activity is waiting to run in this process...
if (normalMode) {
try {
//代码1,启动launch activity
didSomething = mAtmInternal.attachApplication(app.getWindowProcessController());
} catch (Exception e) {
Slog.wtf(TAG, "Exception thrown launching activities in " + app, e);
badApp = true;
}
}
//省略部分代码
return true;
}
AMS准备一些配置工作后又调用了ApplicationThread.bindApplication()
@Override
public final void bindApplication(String processName, ApplicationInfo appInfo,
ProviderInfoList providerList, ComponentName instrumentationName,
ProfilerInfo profilerInfo, Bundle instrumentationArgs,
IInstrumentationWatcher instrumentationWatcher,
IUiAutomationConnection instrumentationUiConnection, int debugMode,
boolean enableBinderTracking, boolean trackAllocation,
boolean isRestrictedBackupMode, boolean persistent, Configuration config,
CompatibilityInfo compatInfo, Map services, Bundle coreSettings,
String buildSerial, AutofillOptions autofillOptions,
ContentCaptureOptions contentCaptureOptions, long[] disabledCompatChanges) {
if (services != null) {
//省略部分代码
// Setup the service cache in the ServiceManager
//初始化service缓存,第一次启动并绑定时才会执行
ServiceManager.initServiceCache(services);
}
//设置核心配置
setCoreSettings(coreSettings);
//构建Application对象所需要的
AppBindData data = new AppBindData();
data.processName = processName;
data.appInfo = appInfo;
data.providers = providerList.getList();
data.instrumentationName = instrumentationName;
data.instrumentationArgs = instrumentationArgs;
data.instrumentationWatcher = instrumentationWatcher;
data.instrumentationUiAutomationConnection = instrumentationUiConnection;
data.debugMode = debugMode;
data.enableBinderTracking = enableBinderTracking;
data.trackAllocation = trackAllocation;
data.restrictedBackupMode = isRestrictedBackupMode;
data.persistent = persistent;
data.config = config;
data.compatInfo = compatInfo;
data.initProfilerInfo = profilerInfo;
data.buildSerial = buildSerial;
data.autofillOptions = autofillOptions;
data.contentCaptureOptions = contentCaptureOptions;
data.disabledCompatChanges = disabledCompatChanges;
//创建Application
sendMessage(H.BIND_APPLICATION, data);
}
通过Handler发送一个BIND_APPLICATION
的消息,最终消息处理在handleBindApplication()
中
public void handleMessage(Message msg) {
if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
switch (msg.what) {
case BIND_APPLICATION:
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
AppBindData data = (AppBindData)msg.obj;
//在handleBindApplication()中处理消息
handleBindApplication(data);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
break;
}
//省略部分代码
}
@UnsupportedAppUsage
private void handleBindApplication(AppBindData data) {
//省略部分代码
//获取LoadedApk,由PMS解析apk安装包得到
data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);
//省略部分代码
// Instrumentation info affects the class loader, so load it before
// setting up the app context.
//将AppBindData的数据复制到InstrumentationInfo
final InstrumentationInfo ii;
if (data.instrumentationName != null) {
try {
ii = new ApplicationPackageManager(
null, getPackageManager(), getPermissionManager())
.getInstrumentationInfo(data.instrumentationName, 0);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
"Unable to find instrumentation info for: " + data.instrumentationName);
}
//省略部分代码
mInstrumentationPackageName = ii.packageName;
mInstrumentationAppDir = ii.sourceDir;
mInstrumentationSplitAppDirs = ii.splitSourceDirs;
mInstrumentationLibDir = getInstrumentationLibrary(data.appInfo, ii);
mInstrumentedAppDir = data.info.getAppDir();
mInstrumentedSplitAppDirs = data.info.getSplitAppDirs();
mInstrumentedLibDir = data.info.getLibDir();
} else {
ii = null;
}
//省略部分代码
// Continue loading instrumentation.
if (ii != null) {
//省略部分代码
//创建Instrumentation
try {
final ClassLoader cl = instrContext.getClassLoader();
mInstrumentation = (Instrumentation)
cl.loadClass(data.instrumentationName.getClassName()).newInstance();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate instrumentation "
+ data.instrumentationName + ": " + e.toString(), e);
}
//初始化Instrumentation
final ComponentName component = new ComponentName(ii.packageName, ii.name);
mInstrumentation.init(this, instrContext, appContext, component,
data.instrumentationWatcher, data.instrumentationUiAutomationConnection);
//省略部分代码
} else {
mInstrumentation = new Instrumentation();
mInstrumentation.basicInit(this);
}
//省略部分代码
// Allow disk access during application and provider setup. This could
// block processing ordered broadcasts, but later processing would
// probably end up doing the same disk access.
Application app;
//省略部分代码
try {
// If the app is being launched for full backup or restore, bring it up in
// a restricted environment with the base application class.
//创建Application
app = data.info.makeApplication(data.restrictedBackupMode, null);
//省略部分代码
// Do this after providers, since instrumentation tests generally start their
// test thread at this point, and we don't want that racing.
try {
//执行Instrumentation的onCreate方法
mInstrumentation.onCreate(data.instrumentationArgs);
}
catch (Exception e) {
throw new RuntimeException(
"Exception thrown in onCreate() of "
+ data.instrumentationName + ": " + e.toString(), e);
}
try {
//通过Instrumentation调用Application的onCreate()方法
mInstrumentation.callApplicationOnCreate(app);
} catch (Exception e) {
if (!mInstrumentation.onException(app, e)) {
throw new RuntimeException(
"Unable to create application " + app.getClass().getName()
+ ": " + e.toString(), e);
}
}
} finally {
//省略部分代码
}
//省略部分代码
}
handleBindApplication()
的核心思路如下
- 获取解析后的apk数据,即LoadeApk对象,此过程由PMS在系统启动之后完成。
- 创建Instrumentation,此对象管理Application和所有Activity的生命周期,一个app进程只有一个Instrumentation对象
- 创建Application
- 通过Instrumentation调用Application的onCreate()方法
我们看下makeApplication()中是如何创建Application的
@UnsupportedAppUsage
public Application makeApplication(boolean forceDefaultAppClass,
Instrumentation instrumentation) {
//创建过之后就不再创建了
if (mApplication != null) {
return mApplication;
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "makeApplication");
Application app = null;
//获取Application的全路径包名
String appClass = mApplicationInfo.className;
if (forceDefaultAppClass || (appClass == null)) {
appClass = "android.app.Application";
}
try {
final java.lang.ClassLoader cl = getClassLoader();
//省略部分代码
ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);
// The network security config needs to be aware of multiple
// applications in the same process to handle discrepancies
NetworkSecurityConfigProvider.handleNewApplication(appContext);
//通过反射创建application对象
app = mActivityThread.mInstrumentation.newApplication(
cl, appClass, appContext);
appContext.setOuterContext(app);
} catch (Exception e) {
//省略部分代码
}
mActivityThread.mAllApplications.add(app);
mApplication = app;
//省略部分代码
return app;
}
可以看到,Application是通过反射创建出来的,这很好理解为什么要使用反射,我们在app项目中的AndroidManifest.xml
中去注册application,PMS解析清单文件时,拿到application的全路径名称,使用全路径包名创建对应的对象,自然而然想到的是通过反射。创建完Application,mInstrumentation
便会执行application的onCreate()
方法,app开发中很多第三方sdk的初始化都在这个方法里面执行的。
创建完Application对象并执行其onCreate()方法之后,就要开始创建launch Activity了,也就是清单文件中的入口Activity,在ActivityManagerService
的attachApplicationLocked()
方法中,bindApplication()执行完就开始执行mAtmInternal.attachApplication()
,见代码1处,此代码注释也说得很明白,查看顶部可见的activity是否在当前进程中等待运行,如果是,就启动它。而我们知道Activity是由ActivityTack管理的,因此,这里也是由ActivityTackManagerService
来处理
@HotPath(caller = HotPath.PROCESS_CHANGE)
@Override
public boolean attachApplication(WindowProcessController wpc) throws RemoteException {
synchronized (mGlobalLockWithoutBoost) {
return mRootActivityContainer.attachApplication(wpc);
}
}
boolean attachApplication(WindowProcessController app) throws RemoteException {
final String processName = app.mName;
boolean didSomething = false;
//遍历找到当前app的activitystack,然后启动它的top visible activity
for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {
final ActivityDisplay display = mActivityDisplays.get(displayNdx);
final ActivityStack stack = display.getFocusedStack();
if (stack != null) {
stack.getAllRunningVisibleActivitiesLocked(mTmpActivityList);
final ActivityRecord top = stack.topRunningActivityLocked();
final int size = mTmpActivityList.size();
for (int i = 0; i < size; i++) {
final ActivityRecord activity = mTmpActivityList.get(i);
if (activity.app == null && app.mUid == activity.info.applicationInfo.uid
&& processName.equals(activity.processName)) {
try {
//真正启动Activity
if (mStackSupervisor.realStartActivityLocked(activity, app,
top == activity /* andResume */, true /* checkConfig */)) {
didSomething = true;
}
} catch (RemoteException e) {
Slog.w(TAG, "Exception in new application when starting activity "
+ top.intent.getComponent().flattenToShortString(), e);
throw e;
}
}
}
}
}
if (!didSomething) {
ensureActivitiesVisible(null, 0, false /* preserve_windows */);
}
return didSomething;
}
真正创建启动activity的逻辑在mStackSupervisor.realStartActivityLocked()
中
boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,
boolean andResume, boolean checkConfig) throws RemoteException {
//省略部分代码
// Create activity launch transaction.
//创建ClientTransaction,用于控制Activity的生命周期方法执行
final ClientTransaction clientTransaction = ClientTransaction.obtain(
proc.getThread(), r.appToken);
final DisplayContent dc = r.getDisplay().mDisplayContent;
//首先需要启动activity,所以先添加LaunchActivityItem
clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),
System.identityHashCode(r), r.info,
// TODO: Have this take the merged configuration instead of separate global
// and override configs.
mergedConfiguration.getGlobalConfiguration(),
mergedConfiguration.getOverrideConfiguration(), r.compat,
r.launchedFromPackage, task.voiceInteractor, proc.getReportedProcState(),
r.icicle, r.persistentState, results, newIntents,
dc.isNextTransitionForward(), proc.createProfilerInfoIfNeeded(),
r.assistToken));
// Set desired final state.
//然后添加ResumeActivityItem或者PauseActivityItem,根据andResume参数来
final ActivityLifecycleItem lifecycleItem;
if (andResume) {
lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());
} else {
lifecycleItem = PauseActivityItem.obtain();
}
clientTransaction.setLifecycleStateRequest(lifecycleItem);
// Schedule transaction.
//提交事务
mService.getLifecycleManager().scheduleTransaction(clientTransaction);
//省略部分代码
}
这里是通过提交事务的方式来启动activity,首先创建Activity,是在LaunchActivityItem
中执行的,然后执行Activity的生命周期方法,这些依次对应ResumeActivityItem
、PauseActivityItem
。最后提交事务,事务开始执行,也就是Activity的启动。先看LaunchActivityItem
@Override
public void execute(ClientTransactionHandler client, IBinder token,
PendingTransactionActions pendingActions) {
Trace.traceBegin(TRACE_TAG_ACTIVITY_MANAGER, "activityStart");
ActivityClientRecord r = new ActivityClientRecord(token, mIntent, mIdent, mInfo,
mOverrideConfig, mCompatInfo, mReferrer, mVoiceInteractor, mState, mPersistentState,
mPendingResults, mPendingNewIntents, mIsForward,
mProfilerInfo, client, mAssistToken);
//ActivityThread继承了ClientTransactionHandler,这里交由ActivityThread处理
client.handleLaunchActivity(r, pendingActions, null /* customIntent */);
Trace.traceEnd(TRACE_TAG_ACTIVITY_MANAGER);
}
/**
* Extended implementation of activity launch. Used when server requests a launch or relaunch.
*/
@Override
public Activity handleLaunchActivity(ActivityClientRecord r,
PendingTransactionActions pendingActions, Intent customIntent) {
// 省略部分代码
//得到Activity对象
final Activity a = performLaunchActivity(r, customIntent);
// 省略部分代码
return a;
}
/** Core implementation of activity launch. */
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
//得到Activity相关配置信息
ActivityInfo aInfo = r.activityInfo;
//得到包信息
if (r.packageInfo == null) {
r.packageInfo = getPackageInfo(aInfo.applicationInfo, r.compatInfo,
Context.CONTEXT_INCLUDE_CODE);
}
//获取组件名称,也就是Activity的名称
ComponentName component = r.intent.getComponent();
if (component == null) {
component = r.intent.resolveActivity(
mInitialApplication.getPackageManager());
r.intent.setComponent(component);
}
if (r.activityInfo.targetActivity != null) {
component = new ComponentName(r.activityInfo.packageName,
r.activityInfo.targetActivity);
}
//创建context对象,用于获取类加载器
ContextImpl appContext = createBaseContextForActivity(r);
Activity activity = null;
try {
java.lang.ClassLoader cl = appContext.getClassLoader();
//通过反射创建Activity对象
activity = mInstrumentation.newActivity(
cl, component.getClassName(), r.intent);
StrictMode.incrementExpectedActivityCount(activity.getClass());
r.intent.setExtrasClassLoader(cl);
r.intent.prepareToEnterProcess();
if (r.state != null) {
r.state.setClassLoader(cl);
}
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to instantiate activity " + component
+ ": " + e.toString(), e);
}
}
try {
//获取application对象,因为第一次已经创建了,所以这里直接得到上一次创建的application对象
Application app = r.packageInfo.makeApplication(false, mInstrumentation);
if (localLOGV) Slog.v(TAG, "Performing launch of " + r);
if (localLOGV) Slog.v(
TAG, r + ": app=" + app
+ ", appName=" + app.getPackageName()
+ ", pkg=" + r.packageInfo.getPackageName()
+ ", comp=" + r.intent.getComponent().toShortString()
+ ", dir=" + r.packageInfo.getAppDir());
if (activity != null) {
CharSequence title = r.activityInfo.loadLabel(appContext.getPackageManager());
Configuration config = new Configuration(mCompatConfiguration);
if (r.overrideConfig != null) {
config.updateFrom(r.overrideConfig);
}
if (DEBUG_CONFIGURATION) Slog.v(TAG, "Launching activity "
+ r.activityInfo.name + " with config " + config);
Window window = null;
if (r.mPendingRemoveWindow != null && r.mPreserveWindow) {
window = r.mPendingRemoveWindow;
r.mPendingRemoveWindow = null;
r.mPendingRemoveWindowManager = null;
}
appContext.setOuterContext(activity);
//执行Activity的attach()方法
activity.attach(appContext, this, getInstrumentation(), r.token,
r.ident, app, r.intent, r.activityInfo, title, r.parent,
r.embeddedID, r.lastNonConfigurationInstances, config,
r.referrer, r.voiceInteractor, window, r.configCallback,
r.assistToken);
if (customIntent != null) {
activity.mIntent = customIntent;
}
r.lastNonConfigurationInstances = null;
checkAndBlockForNetworkAccess();
activity.mStartedActivity = false;
int theme = r.activityInfo.getThemeResource();
if (theme != 0) {
activity.setTheme(theme);
}
activity.mCalled = false;
//执行Activity的onCreate方法
if (r.isPersistable()) {
mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);
} else {
mInstrumentation.callActivityOnCreate(activity, r.state);
}
if (!activity.mCalled) {
throw new SuperNotCalledException(
"Activity " + r.intent.getComponent().toShortString() +
" did not call through to super.onCreate()");
}
r.activity = activity;
}
r.setState(ON_CREATE);
// updatePendingActivityConfiguration() reads from mActivities to update
// ActivityClientRecord which runs in a different thread. Protect modifications to
// mActivities to avoid race.
synchronized (mResourcesManager) {
mActivities.put(r.token, r);
}
} catch (SuperNotCalledException e) {
throw e;
} catch (Exception e) {
if (!mInstrumentation.onException(activity, e)) {
throw new RuntimeException(
"Unable to start activity " + component
+ ": " + e.toString(), e);
}
}
return activity;
}
上述源码分析可知,Activity最终也是根据清单文件里面的全路径名称,然后通过反射创建的,这跟Application的创建如出一辙,紧接着根据事务的顺序执行依次执行Activity的生命周期方法
/** Transition the client through previously initialized state sequence. */
private void performLifecycleSequence(ActivityClientRecord r, IntArray path,
ClientTransaction transaction) {
final int size = path.size();
for (int i = 0, state; i < size; i++) {
state = path.get(i);
if (DEBUG_RESOLVER) {
Slog.d(TAG, tId(transaction) + "Transitioning activity: "
+ getShortActivityName(r.token, mTransactionHandler)
+ " to state: " + getStateName(state));
}
switch (state) {
case ON_CREATE:
mTransactionHandler.handleLaunchActivity(r, mPendingActions,
null /* customIntent */);
break;
case ON_START:
mTransactionHandler.handleStartActivity(r, mPendingActions);
break;
case ON_RESUME:
mTransactionHandler.handleResumeActivity(r.token, false /* finalStateRequest */,
r.isForward, "LIFECYCLER_RESUME_ACTIVITY");
break;
case ON_PAUSE:
mTransactionHandler.handlePauseActivity(r.token, false /* finished */,
false /* userLeaving */, 0 /* configChanges */, mPendingActions,
"LIFECYCLER_PAUSE_ACTIVITY");
break;
case ON_STOP:
mTransactionHandler.handleStopActivity(r.token, false /* show */,
0 /* configChanges */, mPendingActions, false /* finalStateRequest */,
"LIFECYCLER_STOP_ACTIVITY");
break;
case ON_DESTROY:
mTransactionHandler.handleDestroyActivity(r.token, false /* finishing */,
0 /* configChanges */, false /* getNonConfigInstance */,
"performLifecycleSequence. cycling to:" + path.get(size - 1));
break;
case ON_RESTART:
mTransactionHandler.performRestartActivity(r.token, false /* start */);
break;
default:
throw new IllegalArgumentException("Unexpected lifecycle state: " + state);
}
}
}
时序图
总结
关于Application和Activity的创建流程,面试中会经常问到,只要自己跟着源码走几遍,大概就能懂了,具体不必深究。
参考资料
转载自:https://juejin.cn/post/7252586674578817061