溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

Android10原理機(jī)制系列之Activity窗口添加到WMS過程是什么

發(fā)布時間:2021-10-23 16:38:51 來源:億速云 閱讀:137 作者:iii 欄目:移動開發(fā)

這篇文章主要介紹“Android10原理機(jī)制系列之Activity窗口添加到WMS過程是什么”,在日常操作中,相信很多人在Android10原理機(jī)制系列之Activity窗口添加到WMS過程是什么問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”Android10原理機(jī)制系列之Activity窗口添加到WMS過程是什么”的疑惑有所幫助!接下來,請跟著小編一起來學(xué)習(xí)吧!

前言

首先看一個Android界面的布局層次結(jié)構(gòu),最直觀的看一下:

Android10原理機(jī)制系列之Activity窗口添加到WMS過程是什么

我們能清晰看到,這個界面分成了3部分:頂部狀態(tài)欄(statusbar)、底部導(dǎo)航欄(navigationbar)、應(yīng)用界面。

題外話:

查看布局的層次結(jié)構(gòu),工具或途徑可以參考下面的。

  • Android Studio:Tools->Layout Inspector->選擇要查看的進(jìn)程;

  • SDK Tools:tools/hierarchyviewer.bat。  不過最新推薦用tools/monitor.bat代替單獨的hierarchyviewer.bat;hierarchyviewer.bat在工程目錄下也存在prebuilts/devtools/tools

第二部分 綜述總結(jié) ,先將上述兩個過程的內(nèi)容做了比較簡潔的總結(jié)。

第三部分 Activity窗口添加過程 ,跟蹤源碼詳細(xì) 講述了 Activity窗口的創(chuàng)建過程 以及 添加到WMS過程。

由于第三部分  跟蹤源碼,這個過程比較長,涉及比較多,相對枯燥。所以把總結(jié)先放到了第二部分。這樣,如果了解過源碼或這個過程的,可以只看第二部分。沒了解過的,也可以通過第二部分有個大概了解,再查看第三部分,若遇到不太清楚的部分,可以再回到第二部分,對比理解。

該篇也是基于Android10的源碼。

若有不對或不足,歡迎指點。

綜述總結(jié)

前言已經(jīng)介紹了為什么將總結(jié)放在了前面。下面具體看下。

第二部分,主要介紹了下面幾個內(nèi)容:

  • Window類型:窗口類型介紹

  • 幾個重要類:窗口創(chuàng)建添加到WMS過程中常見的一些類,了解下他們之間的關(guān)系

  • Activity創(chuàng)建窗口添加到WMS綜述:簡單總結(jié)了下 Activity的創(chuàng)建過程 和 添加到WMS過程

  • Activity中的一些結(jié)構(gòu)示意圖:整個過程,Activity中關(guān)聯(lián)的一些類/結(jié)構(gòu)的 關(guān)系,理解這個個人覺得很有必要

  • Token傳遞到WMS:Token是很重要的參數(shù),參與整個過程。這里將該篇涉及的過程中的 Token的傳遞過程單獨總結(jié)了下

Window類型

//WindowManager.java public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {     public static final int FIRST_APPLICATION_WINDOW = 1;     public static final int LAST_APPLICATION_WINDOW = 99;          public static final int FIRST_SUB_WINDOW = 1000;     public static final int LAST_SUB_WINDOW = 1999;          public static final int FIRST_SYSTEM_WINDOW = 2000;     public static final int LAST_SYSTEM_WINDOW = 2999;     //狀態(tài)欄     public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;     //搜索欄     public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;     //來電顯示     public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;     //警告窗口,常見如:低電量警告     public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;     //鎖屏     public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;     //toast     public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;     public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;//顯示在所有窗口之上,覆蓋     //來電優(yōu)先,即使鎖屏狀態(tài)下     public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;     //輸入法窗口     public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;     //壁紙     public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13; }
  • 應(yīng)用窗口(1 ~ 99):FIRST_APPLICATION_WINDOW ~  LAST_APPLICATION_WINDOW。對應(yīng)一個Activity,token需設(shè)置成Activity的token。 如:Activity。

  • 子窗口(1000 ~ 1999):FIRST_SUB_WINDOW ~  LAST_SUB_WINDOW。必須要有一個父窗口,token需設(shè)置成父窗口的token。 如:PopupWindow,依附于Activity。

  • 系統(tǒng)窗口(2000 ~ 2999):FIRST_SYSTEM_WINDOW ~ LAST_SYSTEM_WINDOW。系統(tǒng)級的 不需要對應(yīng)Activity  也不需要有父窗口,應(yīng)用進(jìn)程一般沒有權(quán)限創(chuàng)建,只有系統(tǒng)進(jìn)程可以創(chuàng)建。如:上面列出了部分常見的系統(tǒng)窗口,狀態(tài)欄、來電、toast、輸入法等等。

幾個重要類

下面幾個類是后續(xù)經(jīng)??吹降?,這里主要看下他們直接的繼承關(guān)系,后面看到比較容易理解。

public abstract class Window {} public class PhoneWindow extends Window implements MenuBuilder.Callback {}  public interface WindowManagerPolicy extends WindowManagerPolicyConstants {} public class PhoneWindowManager implements WindowManagerPolicy {}  public interface ViewManager {     public void addView(View view, ViewGroup.LayoutParams params);     public void updateViewLayout(View view, ViewGroup.LayoutParams params);     public void removeView(View view); } public interface WindowManager extends ViewManager {} public final class WindowManagerImpl implements WindowManager {}  /** A window in the window manager. */ class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {}

Window是一個抽象類,Activity、Toast、Dialog等都是靠Window來呈現(xiàn)。

  • PhoneWindow是Window的具體實現(xiàn)類(幾乎是唯一實現(xiàn)類)。

  • WindowManager是個接口,繼承接口 ViewManager(ViewManager定義了3個操作:增加、更新、移除)。

  • WindowManagerImpl是WindowManager的實現(xiàn)類。

不過查看WindowManagerImpl中 關(guān)于ViewManager的3個操作可以看出,這3個實現(xiàn)  最終是交由WindowManagerGlobal完成的。

WindowState維護(hù)著窗口的所有信息。WMS通過WindowState對窗口進(jìn)行管理、保存狀態(tài)等。

Activity創(chuàng)建窗口添加到WMS綜述

這是跟蹤的代碼過程,這里匯總下 方便后續(xù)查看理解。 紅色是比較主要的幾個節(jié)點方法。

Android10原理機(jī)制系列之Activity窗口添加到WMS過程是什么

//attach() -performLaunchActivity() --activity.attach()//創(chuàng)建了PhoneWindow(mWindow)。mWindowManager保存的是 從mWindow處獲取的 setWindowManager()創(chuàng)建的WindowManagerImpl ---mWindow.setWindowManager()//PhoneWindow內(nèi)部 創(chuàng)建了WindowManagerImpl(mWindowManager),并保存了appToken、appName。  //onCreate() -setContentView() --installDecor() ---generateDecor()//創(chuàng)建了DecorView(mDecor) ---generateLayout()//將activity的布局作為子視圖(ViewGroup)添加到mDecor中  //onResume() -r.activity.makeVisible()// --wm.addView(mDecor, ...)//wm即mWindowManager(WindowManagerImpl對象) ---WindowManagerGlobal.addView()//創(chuàng)建了ViewRootImpl。addView的view是mDecor,ViewRootImpl中創(chuàng)建了mWindow(這里是一個IBinder,而非attach()中創(chuàng)建的) ----ViewRootImpl.setView()//openSession()創(chuàng)建了Session(IWindowSession的代理類),view也是mDecor。mDecor傳入到ViewRootImpl的mView -----Session.addToDisplay()//通過Session進(jìn)入system_server進(jìn)程 ------mService.addWindow()//進(jìn)入WMS,執(zhí)行addWindow()添加窗口

attach階段:

  • 一個Activity 創(chuàng)建了一個PhoneWindow對象 ,PhoneWindow通過setWindowManager()  創(chuàng)建了WindowManagerImpl 。

即Activity  對應(yīng)一個PhoneWindow,并得到了一個WindowManager(WindowManagerImpl,Window創(chuàng)建的)。

onCreate階段:

  • 創(chuàng)建了DecorView,并將 activity的布局添加到DecorView中 。

onResume階段:

  • 創(chuàng)建了ViewRootImpl,通過setView()最終由Session進(jìn)入system_server進(jìn)程。最終執(zhí)行addWindow添加窗口到WMS。

Activity中的一些結(jié)構(gòu)示意圖

下面是我學(xué)習(xí)總結(jié)中 根據(jù)理解畫的,方便自己查看時一眼可得。

(若有什么不對,多謝指點)

Android10原理機(jī)制系列之Activity窗口添加到WMS過程是什么

public final class WindowManagerImpl implements WindowManager {     private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance(); }  public final class WindowManagerGlobal {     private static IWindowManager sWindowManagerService;//WMS客戶端,     private static IWindowSession sWindowSession;//Session     private final ArrayList<View> mViews = new ArrayList<View>();     private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();     private final ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>(); }
  • 一個Activity對應(yīng)了一個PhoneWindow對象。即 每個Activity對應(yīng)一個Window (具體實現(xiàn)類是PhoneWindow)。

  • 一個PhoneWindow 持有一個 DecorView 實例,  DecorView實際是一個FrameLayout,它是Activity中所有View的根(最頂層的View)。

  • 一個PhoneWindow有一個WindowManagerImpl。WindowManagerImpl持有一個單例WindowManagerGlobal。

Token傳遞到WMS

Activity啟動時 AMS會為其創(chuàng)建一個ActivityRecord??梢詤⒖迹?AMS之應(yīng)用的第一次啟動過程 。

下面先看下ActivityRecord中關(guān)于token的幾處代碼:

final class ActivityRecord extends ConfigurationContainer {     final IApplicationToken.Stub appToken; // window manager token     // TODO: Remove after unification     AppWindowToken mAppWindowToken;              ActivityRecord(ActivityTaskManagerService _service, WindowProcessController _caller,...) {         appToken = new Token(this, _intent);     }          void createAppWindowToken() {         mAppWindowToken = createAppWindow(mAtmService.mWindowManager, appToken,...);     }          static class Token extends IApplicationToken.Stub {         Token(ActivityRecord activity, Intent intent) {             weakActivity = new WeakReference<>(activity);             name = intent.getComponent().flattenToShortString();         }     } }

ActivityRecord中的成員變量 appToken ,這個很重要,后續(xù)很多地方會一直涉及到。

ActivityRecord中有個 appToken  ,其是一個IBinder(內(nèi)部類Token繼承了IApplicationToken接口)。Token內(nèi)部持有Activity的弱引用。

在ActivityRecord中會通過createAppWindow()創(chuàng)建并保存 AppWindowToken對象  到mAppWindowToken。

mAppWindowToken:這個appToken會被封裝在其中。路徑:ActivityStack.startActivityLocked()->ActivityRecord.createAppWindowToken()。AppWindowToken是WindowToken子類。WindowToken可以標(biāo)志一個窗口。

這個appToken,會在Activity.attach()中作為參數(shù)傳遞到Activity。

Activity保存到mToken。

然后通過 Activity.attach()->mWindow.setWindowManager()  傳入到Window(PhoneWindow)中。

Window保存到mAppToken。

WindowManagerGlobal.addView()->Window.adjustLayoutParamsForSubWindow()保存到WindowManager.LayoutParams中的token變量中。

最后WindowManager.LayoutParams(其中token即ActivityRecord中的appToken)作為參數(shù)傳入ViewRootImpl.setView()。

ViewRootImpl中mWindowAttributes拷貝了WindowManager.LayoutParams,作為參數(shù)通過Session.addToDisplay()傳入WMS中,進(jìn)行后續(xù)操作。

這是整個添加窗口(到addWindow())過程 appToken參與的過程及傳遞過程。

appToken如何參與窗口的添加,這個在 “第三部分的2.8:mService.addWindow()” 注釋中能大致看到,比較詳細(xì)。

Activity窗口添加過程

這里主要介紹 Activity對應(yīng)Window的創(chuàng)建 以及 Window添加到WMS的過程。

Activity窗口創(chuàng)建

在 AMS之應(yīng)用的第一次啟動過程 中,從點擊應(yīng)用圖標(biāo)到activity創(chuàng)建并執(zhí)行onCreate()。  下面部分是后面部分的截取,不清楚可以參考下那篇文章。

1.1:handleLaunchActivity()

這里從handleLaunchActivity()開始。

//ActivityThread.java @Override public Activity handleLaunchActivity(ActivityClientRecord r,         PendingTransactionActions pendingActions, Intent customIntent) {     ...     WindowManagerGlobal.initialize();     final Activity a = performLaunchActivity(r, customIntent);     ... }  private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {     ...     try {         java.lang.ClassLoader cl = appContext.getClassLoader();         activity = mInstrumentation.newActivity(                 cl, component.getClassName(), r.intent);     }     try {         Application app = r.packageInfo.makeApplication(false, mInstrumentation);         if (activity != null) {             Window window = null;             ...             //attach(),注意這個r.token。參考1.2             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 (r.isPersistable()) {                 //callActivityOnCreate() 最終執(zhí)行到的activity的onCreate()方法。                   //參考1.4                 mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);             } else {                 mInstrumentation.callActivityOnCreate(activity, r.state);             }         }     }     ...     return activity; }

WindowManagerGlobal.initialize(); 是獲取WMS的IBinder代理類,用于與WMS通信。這里不列出代碼了。

接著要看的是 activity.attach() 。注意作為參數(shù)傳入attach()的 r.token  是個IBinder,來自ActivityClientRecord,簡單看標(biāo)識了一個Activity。

1.2:activity.attach()

//Activity.java final void attach(Context context, ActivityThread aThread,        Instrumentation instr, IBinder token, ...) {     ...     //創(chuàng)建PhoneWindow     mWindow = new PhoneWindow(this, window, activityConfigCallback);//創(chuàng)建PhoneWindow      //設(shè)置軟鍵盤     if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {         mWindow.setSoftInputMode(info.softInputMode);     }     //token保存到mToken。             mToken = token;     ...     //mToken傳入到Window,參考1.3     mWindow.setWindowManager(             (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),             mToken, mComponent.flattenToString(),             (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);     //mWindowManager即setWindowManager()中創(chuàng)建的WindowManagerImpl。     mWindowManager = mWindow.getWindowManager();     ... }

首先創(chuàng)建了Activityd對應(yīng)的Window,是PhoneWindow-Window的實現(xiàn)類。 接著看  mWindow.setWindowManager() 。

1.3:mWindow.setWindowManager()

//Window.java public void setWindowManager(WindowManager wm, IBinder appToken, String appName,         boolean hardwareAccelerated) {     //ActivityClientRecord.token     mAppToken = appToken;     mAppName = appName;     mHardwareAccelerated = hardwareAccelerated;     if (wm == null) {         wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);     }     //創(chuàng)建了WindowManagerImpl,注意WindowManagerImpl中mParentWindow是this,非空     mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this); }

這里創(chuàng)建了WindowManagerImpl對象,即WindowManager的實現(xiàn)類。并保存了appToken、appName、mWindowManager。

通過setWindowManager(),即為Window(或者PhoneWindow)設(shè)置創(chuàng)建了WindowManager(WindowManagerImpl)。

1.4:setContentView()

mInstrumentation.callActivityOnCreate() 最終有調(diào)用到Activity的onCreate()。

自定義Activity,設(shè)置布局都執(zhí)行了 setContentView() ,下面直接來看下這個方法。

//Activity.java public void setContentView(@LayoutRes int layoutResID) {     getWindow().setContentView(layoutResID);//     initWindowDecorActionBar(); }   public Window getWindow() {     return mWindow;//即PhoneWindow對象 }  //PhoneWindow.java // This is the top-level view of the window, containing the window decor. private DecorView mDecor; @Override public void setContentView(int layoutResID) {     // Note: FEATURE_CONTENT_TRANSITIONS may be set in the process of installing the window     // decor, when theme attributes and the like are crystalized. Do not check the feature     // before this happens.     if (mContentParent == null) {         installDecor();//     } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {         mContentParent.removeAllViews();     }     if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {         final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,                 getContext());         transitionTo(newScene);     } else {         mLayoutInflater.inflate(layoutResID, mContentParent);     }     ... }  private void installDecor() {     mForceDecorInstall = false;     if (mDecor == null) {         //生成DecorView,參考1.5         mDecor = generateDecor(-1);         ...     } else {         mDecor.setWindow(this);     }     if (mContentParent == null) {         //布局添加到DecorView,參考1.5         mContentParent = generateLayout(mDecor);          // Set up decor part of UI to ignore fitsSystemWindows if appropriate.         mDecor.makeOptionalFitsSystemWindows();          final DecorContentParent decorContentParent = (DecorContentParent) mDecor.findViewById(                 R.id.decor_content_parent);         if (decorContentParent != null) {         } else {             mTitleView = findViewById(R.id.title);         }         ...     } }

這里主要關(guān)注下兩個方法: mDecor = generateDecor(-1); 和 mContentParent =  generateLayout(mDecor); 。

先看下他們的相關(guān)代碼:

1.5:generateDecor()和generateLayout()

protected DecorView generateDecor(int featureId) {     // System process doesn't have application context and in that case we need to directly use     // the context we have. Otherwise we want the application context, so we don't cling to the     // activity.     Context context;     ...     return new DecorView(context, featureId, this, getAttributes());// }  public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { }  protected ViewGroup generateLayout(DecorView decor) {     ...     mDecor.startChanging();     mDecor.onResourcesLoaded(mLayoutInflater, layoutResource);     ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);     ...     mDecor.finishChanging();     return contentParent; }  //DecorView.java void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {     ...     mDecorCaptionView = createDecorCaptionView(inflater);     final View root = inflater.inflate(layoutResource, null);     if (mDecorCaptionView != null) {         if (mDecorCaptionView.getParent() == null) {             addView(mDecorCaptionView,                     new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));         }         mDecorCaptionView.addView(root,                 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));     } else {          // Put it below the color views.         addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));     }     mContentRoot = (ViewGroup) root;     initializeElevation(); }

通過 generateDecor() 創(chuàng)建了一個DecorView。DecorView實際是一個FrameLayout。

然后通過 generateLayout() ,最終將activity的布局作為子視圖(ViewGroup)添加到DecorView中。

上面可以看到,activity生成到執(zhí)行onCreate(),這個過程,activity生成了關(guān)聯(lián)的PhoneWindow,然后創(chuàng)建了WindowManagerImpl、DecorView。

下面看下Window添加到WMS的過程,看這些創(chuàng)建的對象之前如何聯(lián)系 形成的一開始介紹的結(jié)構(gòu)示意圖。

Window添加到WMS過程

在 AMS之應(yīng)用的第一次啟動過程 中主要講到onCreate,其實onResume也在其中在,這里不詳細(xì)解釋了,再次列出相關(guān)代碼:

//ActivityStackSupervisor.java: boolean realStartActivityLocked(ActivityRecord r, WindowProcessController proc,         boolean andResume, boolean checkConfig) throws RemoteException {     ...     try {         ...         try {             // Create activity launch transaction.             final ClientTransaction clientTransaction = ClientTransaction.obtain(                     proc.getThread(), r.appToken);              final DisplayContent dc = r.getDisplay().mDisplayContent;             clientTransaction.addCallback(LaunchActivityItem.obtain(new Intent(r.intent),             ...             // Set desired final state.             final ActivityLifecycleItem lifecycleItem;             if (andResume) {                 lifecycleItem = ResumeActivityItem.obtain(dc.isNextTransitionForward());             } else {                 lifecycleItem = PauseActivityItem.obtain();             }             clientTransaction.setLifecycleStateRequest(lifecycleItem);              // Schedule transaction.             mService.getLifecycleManager().scheduleTransaction(clientTransaction);             ...         }      }     ...     return true; }

通過 LaunchActivityItem 關(guān)聯(lián) 最終執(zhí)行結(jié)果是創(chuàng)建了應(yīng)用的Activity 并 執(zhí)行了attach()和onCreate()。  andResume為true(傳入的參數(shù)為true,可以參考那篇這段代碼往前看), 通過 ResumeActivityItem 關(guān)聯(lián) 最終執(zhí)行到的是  ActivityThread.handleResumeActivity()。

這里從ActivityThread.handleResumeActivity()來看。

2.1:ActivityThread.handleResumeActivity()

//ActivityThread.java @Override public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,         String reason) {     ...     // TODO Push resumeArgs into the activity for consideration     //執(zhí)行onStart()->onResume()。參考2.2     final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);     final Activity a = r.activity;     if (r.window == null && !a.mFinished && willBeVisible) {         r.window = r.activity.getWindow();         View decor = r.window.getDecorView();         decor.setVisibility(View.INVISIBLE);         ViewManager wm = a.getWindowManager();         WindowManager.LayoutParams l = r.window.getAttributes();         a.mDecor = decor;         l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;         ...     }      ...     // The window is now visible if it has been added, we are not     // simply finishing, and we are not starting another activity.     if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {         ...         r.activity.mVisibleFromServer = true;         mNumVisibleActivities++;         if (r.activity.mVisibleFromClient) {             //參考2.3             r.activity.makeVisible();         }     }     ... }

注意 View decor = r.window.getDecorView(); 獲取了DecorView對象,最后通過 a.mDecor =  decor; 將DecorView賦到了Activity中。

這里關(guān)注兩個:

performResumeActivity()  r.activity.makeVisible();

2.2:performResumeActivity()

//ActivityThread.java @VisibleForTesting public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest,         String reason) {     final ActivityClientRecord r = mActivities.get(token);     ...     try {         r.activity.onStateNotSaved();         r.activity.mFragments.noteStateNotSaved();         ...         r.activity.performResume(r.startsNotResumed, reason);          r.state = null;         r.persistentState = null;         r.setState(ON_RESUME);          reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming");     }      return r; }  //Activity.java final void performResume(boolean followedByPause, String reason) {     performRestart(true /* start */, reason);     mInstrumentation.callActivityOnResume(this); }  final void performRestart(boolean start, String reason) {     mInstrumentation.callActivityOnRestart(this); }  //Instrumentation.java public void callActivityOnRestart(Activity activity) {     activity.onRestart(); }  public void callActivityOnResume(Activity activity) {     activity.mResumed = true;     activity.onResume();     ... }

performResumeActivity()中 r.activity.performResume()  回調(diào)Activity的performResume()方法。最終執(zhí)行了Activity的onResume()方法。

performResume()在執(zhí)行onResume前,調(diào)用了 performRestart()  ,最終調(diào)用的是activity的onStart()。這里可以看出 onStart()執(zhí)行在onResume()之前。

2.3:r.activity.makeVisible()

//Activity.java void makeVisible() {     if (!mWindowAdded) {         ViewManager wm = getWindowManager();         wm.addView(mDecor, getWindow().getAttributes());         mWindowAdded = true;     }     mDecor.setVisibility(View.VISIBLE); }  //WindowManagerImpl.java public final class WindowManagerImpl implements WindowManager {     @UnsupportedAppUsage     private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();     @Override     public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {         applyDefaultToken(params);         //mParentWindow即創(chuàng)建WindowManagerImpl是 傳入的。參考2.4         mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);     } }

這里的getWindowManager()獲取到的是前面講到的  attach()時通過setWindowManager()創(chuàng)建的WindowManagerImpl對象。

前面也講過,addView()等3個操作定義實現(xiàn) 最終在WindowManagerGlobal中,這里就可以看到。

2.4:WindowManagerGlobal.addView()

//WindowManagerGlobal @UnsupportedAppUsage private final ArrayList<View> mViews = new ArrayList<View>(); @UnsupportedAppUsage private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>(); public void addView(View view, ViewGroup.LayoutParams params,         Display display, Window parentWindow) {     ...     final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;     if (parentWindow != null) {         //調(diào)整Window參數(shù),這個過程將token設(shè)置其中了         //參考2.4.1         parentWindow.adjustLayoutParamsForSubWindow(wparams);     }      ...     ViewRootImpl root;     View panelParentView = null;     synchronized (mLock) {         ...         root = new ViewRootImpl(view.getContext(), display);         view.setLayoutParams(wparams);         mViews.add(view);         mRoots.add(root);         mParams.add(wparams);         try {             //將view及相關(guān)參數(shù)設(shè)置到ViewRootImpl中。ViewRootImpl會向WMS添加新窗口、申請Surface及繪制工作等。             //參考2.6             root.setView(view, wparams, panelParentView);         }          ...     } }  //ViewRootImpl.java @UnsupportedAppUsage final IWindowSession mWindowSession; public ViewRootImpl(Context context, Display display) {     mContext = context;     //創(chuàng)建了Session(),參考2.5     mWindowSession = WindowManagerGlobal.getWindowSession();     mDisplay = display;     mBasePackageName = context.getBasePackageName();     mThread = Thread.currentThread();     mTargetSdkVersion = context.getApplicationInfo().targetSdkVersion;     //這里的mWindow不是前面Activity中的PhoneWindow,它是W extends IWindow.Stub     mWindow = new W(this);     mViewVisibility = View.GONE;     //創(chuàng)建AttachInfo     mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this,                 context);     ... }  static class W extends IWindow.Stub {...}  //ViewRootImpl.java public final class ViewRootImpl implements ViewParent,  //View.java final static class AttachInfo {     AttachInfo(IWindowSession session, IWindow window, Display display,             ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer,             Context context) {         mSession = session;         mWindow = window;         mWindowToken = window.asBinder();         mDisplay = display;         mViewRootImpl = viewRootImpl;         mHandler = handler;         mRootCallbacks = effectPlayer;         mTreeObserver = new ViewTreeObserver(context);     } }

這里主要看到,創(chuàng)建了ViewRootImpl對象。這個類實現(xiàn)了View與WindowManager之間必要的協(xié)議。

注意創(chuàng)建中的 mWindow = new W(this); ,這個W繼承IWindow.Stub。

創(chuàng)建ViewRootImpl對象時 創(chuàng)建了一個 mAttachInfo = View.AttachInfo() ,  AttachInfo是一系列綁定信息。mWindowSession、mWindow作為參數(shù)傳入。AttachInfo創(chuàng)建時注意 mWindowToken =  window.asBinder(); 。

  • mWindowSession在后續(xù)2.5/2.6/2.7中講到,它是Session對象,它是IWindowSession的代理類,  通過他可以與WMS通信的binder接口 。

  • mWindow這里是W對象,它是IWindow.Stub,通過new創(chuàng)建,后續(xù)能看到會傳入WMS, 它是WMS回調(diào)應(yīng)用(與應(yīng)用通信)的binder接口  。

  • mWindowToken,也就是W的IBinder對象, 也是WMS與應(yīng)用通信的接口 。

創(chuàng)建ViewRootImpl對象后,WindowManagerGlobal將View、ViewRootImpl、LayoutParams保存到相應(yīng)的ArrayList中。前面也講到過,WindowManagerGlobal是單例的,應(yīng)用進(jìn)程中只有一個。最后通過root.setView()將View(這里是DecorView)傳入到ViewRootImpl中。

2.4.1:adjustLayoutParamsForSubWindow()

前面看到mAppToken是從Activity的傳入的。

這里mAppToken被設(shè)置到WindowManager.LayoutParams里,后面可以看到最終傳入到WMS參與處理。

//Window.java void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {     CharSequence curTitle = wp.getTitle();     //子窗口,該篇中是應(yīng)用窗口,所以不走這,也了解下。            if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&             wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {         if (wp.token == null) {             View decor = peekDecorView();             if (decor != null) {                 wp.token = decor.getWindowToken();             }         }         ...     //系統(tǒng)窗口,也不走這     } else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&             wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {         ...     //應(yīng)用窗口,該篇走這     } else {         if (wp.token == null) {             //設(shè)置到了WindowManager.LayoutParams中             wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;         }         ...     }     ... }

2.4.2:AttachInfo在其中了解下

ViewRootImpl與各個View。通過下面的過程,AttachInfo綁定信息被設(shè)置到各個View中了,即各個View能夠獲取到各種相關(guān)信息。

2.6執(zhí)行到ViewRootImpl.setView()后,參考過程:setView()->requestLayout()->scheduleTraversals()->mTraversalRunnable->doTraversal()->performTraversals()->host.dispatchAttachedToWindow(mAttachInfo,  0)->View.dispatchAttachedToWindow()->ViewGroup.dispatchAttachedToWindow()。

屬同個ViewGroup的 AttachInfo是一樣的。

//ViewGroup.java @Override @UnsupportedAppUsage void dispatchAttachedToWindow(AttachInfo info, int visibility) {     mGroupFlags |= FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;     super.dispatchAttachedToWindow(info, visibility);     mGroupFlags &= ~FLAG_PREVENT_DISPATCH_ATTACHED_TO_WINDOW;      final int count = mChildrenCount;     final View[] children = mChildren;     for (int i = 0; i < count; i++) {         final View child = children[i];         child.dispatchAttachedToWindow(info,                 combineVisibility(visibility, child.getVisibility()));     }     final int transientCount = mTransientIndices == null ? 0 : mTransientIndices.size();     for (int i = 0; i < transientCount; ++i) {         View view = mTransientViews.get(i);         view.dispatchAttachedToWindow(info,                 combineVisibility(visibility, view.getVisibility()));     } } }

上述過程 performTraversals()  大致了解下:從上而下遍歷視圖樹,每個View繪制自己,ViewGroup通知子View進(jìn)行繪制。測量performMeasure()  執(zhí)行布局performLayout() 繪制performDraw()。

Android繪制 重要的部分就在這里,需要了解的可以仔細(xì)研究下這個方法(performTraversals()),這里不作關(guān)注。

2.5:WindowManagerGlobal.getWindowSession()

// WindowManagerGlobal.java    @UnsupportedAppUsage public static IWindowSession getWindowSession() {     synchronized (WindowManagerGlobal.class) {         if (sWindowSession == null) {             try {                 InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();                 IWindowManager windowManager = getWindowManagerService();                 //創(chuàng)建Session對象                 sWindowSession = windowManager.openSession(                         new IWindowSessionCallback.Stub() {                             @Override                             public void onAnimatorScaleChanged(float scale) {                                 ValueAnimator.setDurationScale(scale);                             }                         });             } catch (RemoteException e) {                 throw e.rethrowFromSystemServer();             }         }         return sWindowSession;     } }  //WindowManagerService.java @Override public IWindowSession openSession(IWindowSessionCallback callback) {     return new Session(this, callback); }

獲取Sessiond對象,如果沒有則通過 windowManager.openSession()  創(chuàng)建。Session是IWindowSession的代理類,然后返回給ViewRootImpl中的mWindowSession。

2.6:ViewRootImpl.setView()

//ViewRootImpl.java public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {     synchronized (this) {         if (mView == null) {             mView = view;             mWindowAttributes.copyFrom(attrs);             ...             // Schedule the first layout -before- adding to the window             // manager, to make sure we do the relayout before receiving             // any other events from the system.             requestLayout();//TODO             try {                 mOrigWindowType = mWindowAttributes.type;                 mAttachInfo.mRecomputeGlobalAttributes = true;                 collectViewAttributes();                 //參考2.7,進(jìn)入system_server進(jìn)程                 res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,                         getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,                         mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,                         mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,                         mTempInsets);                 setFrame(mTmpFrame);             }             ...         }     } }

res = mWindowSession.addToDisplay() :mWindowSession是上面返回的創(chuàng)建的Session,  mWindowSession.addToDisplay()  即通過binder進(jìn)入system_server進(jìn)程,執(zhí)行的Session.addToDisplay()。

mView即DecorView。

這里的mWindow是2.4中講到的,是 W  繼承IWindow.Stub。這是一個IBinder對象,在應(yīng)用進(jìn)程創(chuàng)建ViewRootImpl時被創(chuàng)建。

這里 mWindowSession.addToDisplay() 往后可以看到被傳入到WMS。

2.7:Session.addToDisplay()

//Session.java class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {     final WindowManagerService mService;     public Session(WindowManagerService service, IWindowSessionCallback callback) {         mService = service;         ...     }      @Override     public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,             int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,             Rect outStableInsets, Rect outOutsets,             DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,             InsetsState outInsetsState) {         //參考2.8         return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,                 outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,                 outInsetsState);     } }

進(jìn)入WMS,添加Window。

2.8:mService.addWindow()

終于到最后WMS.addWindow(),這里完成窗口添加??梢宰屑?xì)看下下面源碼及注釋,這個方法即使縮減了很多還是比較長,需要耐心。

//WindowManagerService.java public int addWindow(Session session, IWindow client, int seq,         LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,         Rect outContentInsets, Rect outStableInsets, Rect outOutsets,         DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,         InsetsState outInsetsState) {     int[] appOp = new int[1];     //檢查權(quán)限,無權(quán)限不能添加窗口     int res = mPolicy.checkAddPermission(attrs, appOp);     if (res != WindowManagerGlobal.ADD_OKAY) {         return res;     }      boolean reportNewConfig = false;     WindowState parentWindow = null;     ...     final int type = attrs.type;     synchronized (mGlobalLock) {         ...         //獲取窗口要添加到的DisplayContent。即顯示在哪個屏幕上         final DisplayContent displayContent = getDisplayContentOrCreate(displayId, attrs.token);                  if (displayContent == null) {             Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "                     + displayId + ".  Aborting.");             return WindowManagerGlobal.ADD_INVALID_DISPLAY;         }         if (!displayContent.hasAccess(session.mUid)) {             Slog.w(TAG_WM, "Attempted to add window to a display for which the application "                     + "does not have access: " + displayId + ".  Aborting.");             return WindowManagerGlobal.ADD_INVALID_DISPLAY;         }          if (mWindowMap.containsKey(client.asBinder())) {             Slog.w(TAG_WM, "Window " + client + " is already added");             return WindowManagerGlobal.ADD_DUPLICATE_ADD;         }         //添加子窗口,父窗口必須存在。         if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {             parentWindow = windowForClientLocked(null, attrs.token, false);             if (parentWindow == null) {                 Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "                       + attrs.token + ".  Aborting.");                 return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;             }             //這里可以看出WMS要求 窗口的層級 最多為兩層             if (parentWindow.mAttrs.type >= FIRST_SUB_WINDOW                     && parentWindow.mAttrs.type <= LAST_SUB_WINDOW) {                 Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "                         + attrs.token + ".  Aborting.");                 return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;             }         }         if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {             Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display.  Aborting.");             return WindowManagerGlobal.ADD_PERMISSION_DENIED;         }          AppWindowToken atoken = null;         final boolean hasParent = parentWindow != null;         //獲取WindowToken,對于子窗口使用父窗口的token。         //通過attrs.token從mTokenMap取出:private final HashMap<IBinder, WindowToken> mTokenMap = new HashMap();         //關(guān)于Activity窗口:WindowToken,前面講過ActivityRecord 創(chuàng)建時會創(chuàng)建AppWindowToken,這個過程中appToken和AppWindowToken被保存到mTokenMap中         WindowToken token = displayContent.getWindowToken(                 hasParent ? parentWindow.mAttrs.token : attrs.token);          // If this is a child window, we want to apply the same type checking rules as the         // parent window type.         final int rootType = hasParent ? parentWindow.mAttrs.type : type;         boolean addToastWindowRequiresToken = false;         //以下是WindowToken和窗口之間的關(guān)系         if (token == null) {             //以下窗口類型,WindowToken不能為空             if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {                 Slog.w(TAG_WM, "Attempted to add application window with unknown token "                       + attrs.token + ".  Aborting.");                 return WindowManagerGlobal.ADD_BAD_APP_TOKEN;             }             if (rootType == TYPE_INPUT_METHOD) {                 Slog.w(TAG_WM, "Attempted to add input method window with unknown token "                       + attrs.token + ".  Aborting.");                 return WindowManagerGlobal.ADD_BAD_APP_TOKEN;             }             if (rootType == TYPE_VOICE_INTERACTION) {                 Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "                       + attrs.token + ".  Aborting.");                 return WindowManagerGlobal.ADD_BAD_APP_TOKEN;             }             if (rootType == TYPE_WALLPAPER) {                 Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "                       + attrs.token + ".  Aborting.");                 return WindowManagerGlobal.ADD_BAD_APP_TOKEN;             }             if (rootType == TYPE_DREAM) {                 Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "                       + attrs.token + ".  Aborting.");                 return WindowManagerGlobal.ADD_BAD_APP_TOKEN;             }             if (rootType == TYPE_QS_DIALOG) {                 Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "                       + attrs.token + ".  Aborting.");                 return WindowManagerGlobal.ADD_BAD_APP_TOKEN;             }             if (rootType == TYPE_ACCESSIBILITY_OVERLAY) {                 Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "                         + attrs.token + ".  Aborting.");                 return WindowManagerGlobal.ADD_BAD_APP_TOKEN;             }             if (type == TYPE_TOAST) {                 // Apps targeting SDK above N MR1 cannot arbitrary add toast windows.                 if (doesAddToastWindowRequireToken(attrs.packageName, callingUid,                         parentWindow)) {                     Slog.w(TAG_WM, "Attempted to add a toast window with unknown token "                             + attrs.token + ".  Aborting.");                     return WindowManagerGlobal.ADD_BAD_APP_TOKEN;                 }             }             final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();             final boolean isRoundedCornerOverlay =                     (attrs.privateFlags & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0;             //token為空,除上述窗口類型,其他是允許的。此時新建WindowToken             token = new WindowToken(this, binder, type, false, displayContent,                     session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);         //token不為空,且是應(yīng)用窗口類型,token需要時AppWindowToken類型         } else if (rootType >= FIRST_APPLICATION_WINDOW && rootType <= LAST_APPLICATION_WINDOW) {             //判斷token是否是AppWindowToken類型             //前面知道,Activity創(chuàng)建的是AppWindowToken,即獲得的atoken非空             atoken = token.asAppWindowToken();             if (atoken == null) {                 Slog.w(TAG_WM, "Attempted to add window with non-application token "                       + token + ".  Aborting.");                 return WindowManagerGlobal.ADD_NOT_APP_TOKEN;             } else if (atoken.removed) {                 Slog.w(TAG_WM, "Attempted to add window with exiting application token "                       + token + ".  Aborting.");                 return WindowManagerGlobal.ADD_APP_EXITING;             } else if (type == TYPE_APPLICATION_STARTING && atoken.startingWindow != null) {                 Slog.w(TAG_WM, "Attempted to add starting window to token with already existing"                         + " starting window");                 return WindowManagerGlobal.ADD_DUPLICATE_ADD;             }         //其他各種窗口類型處理         } else if (rootType == TYPE_INPUT_METHOD) {             ...         }...         //WindowState維護(hù)了所有窗口的信息,它是WMS實際管理的“窗口”         //它與Z-Order密切相關(guān)(多個Window層疊布局),其屬性mLayer 越大,窗口越靠前。         final WindowState win = new WindowState(this, session, client, token, parentWindow,                 appOp[0], seq, attrs, viewVisibility, session.mUid,                 session.mCanAddInternalSystemWindow);         if (win.mDeathRecipient == null) {             // Client has apparently died, so there is no reason to             // continue.             Slog.w(TAG_WM, "Adding window client " + client.asBinder()                     + " that is dead, aborting.");             return WindowManagerGlobal.ADD_APP_EXITING;         }         ...         origId = Binder.clearCallingIdentity();         //創(chuàng)建SurfaceSession,實現(xiàn)與SurfaceFlinger通信。參考2.9 簡單說明下         win.attach();         //將WindowState對象加入到mWindowMap中         mWindowMap.put(client.asBinder(), win);         win.initAppOpsState();         ...         final AppWindowToken aToken = token.asAppWindowToken();         win.mToken.addWindow(win);         win.applyAdjustForImeIfNeeded();         ...     }     ...     return res; }

WMS通過WindowState對窗口進(jìn)行管理、保存狀態(tài)等。

添加窗口都需要指明其WindowToken;同時窗口需指明其DisplayContent 以確定顯示到哪個屏幕設(shè)備。

具體請看上面注釋,比較詳細(xì)了。

看完,大致能明白窗口類型、WindowToken在窗口添加中的作用。了解到token的作用。

比如:若添加的時子窗口,則必須有父窗口,且窗口的層級最多為兩層。WindowToken為null,可以明顯看出那些情況不允許添加。添加的窗口時應(yīng)用窗口時,WindowToken要是AppWindowToken。等等。

addWindow()暫時就說這些。

這個添加后的結(jié)果,通過res 最終反回到了 2.6:ViewRootImpl.setView() 中,根據(jù)結(jié)果 繼續(xù)處理。

2.9:win.attach()

為什么 win.attach() 是創(chuàng)建與SurfaceFlinger通信的?簡單了解下。

跟蹤下去是創(chuàng)建了SurfaceSession對象,這個創(chuàng)建進(jìn)入native,最終創(chuàng)建了一個與SurfaceFlinger通信的  SurfaceComposerClient。 因此,可以與SurfaceFlinger進(jìn)行通信。

ViewRootImpl創(chuàng)建時  就創(chuàng)建的mSurface,mSurface是ViewRootImpl的成員變量,此時創(chuàng)建的Surface什么都沒有,最后通過relayoutWindow()進(jìn)入WMS  一步步向SurfaceFlinger發(fā)出請求。

這時幾處相關(guān)代碼。

//WindowState.java void attach() {     if (localLOGV) Slog.v(TAG, "Attaching " + this + " token=" + mToken);     mSession.windowAddedLocked(mAttrs.packageName); }  //Session.java void windowAddedLocked(String packageName) {     mPackageName = packageName;     mRelayoutTag = "relayoutWindow: " + mPackageName;     if (mSurfaceSession == null) {         mSurfaceSession = new SurfaceSession();         ...     }     mNumWindow++; }  //ViewRootImpl.java     // These can be accessed by any thread, must be protected with a lock. // Surface can never be reassigned or cleared (use Surface.clear()). @UnsupportedAppUsage public final Surface mSurface = new Surface(); private final SurfaceControl mSurfaceControl = new SurfaceControl();

到此,關(guān)于“Android10原理機(jī)制系列之Activity窗口添加到WMS過程是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識,請繼續(xù)關(guān)注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI