当前位置: 首页 > news >正文

需要做网站建设的公司/企业seo案例

需要做网站建设的公司,企业seo案例,如何做网站标题不含关键词的排名,做商城网站要请程序员吗前言 在Android系统中WindowConfiguration这个类用于管理与窗口相关的设置,该类存储了当前窗口的显示区域、屏幕的旋转方向、窗口模式等参数,应用程序通过该类提供的信息可以更好的适配不同的屏幕布局和窗口环境,以提高用户体验。 一、类定…

前言

在Android系统中WindowConfiguration这个类用于管理与窗口相关的设置,该类存储了当前窗口的显示区域、屏幕的旋转方向、窗口模式等参数,应用程序通过该类提供的信息可以更好的适配不同的屏幕布局和窗口环境,以提高用户体验。

一、类定义

frameworks/base/core/java/android/app/WindowConfiguration.java

public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> {//包含装饰窗口在内的窗口显示区域private Rect mBounds = new Rect();//不包含装饰窗口在内的窗口显示区域private Rect mAppBounds;//可显示的最大区域private final Rect mMaxBounds = new Rect();//当前屏幕设备的旋转角度private int mRotation = ROTATION_UNDEFINED;//当前窗口模式private @WindowingMode int mWindowingMode;//屏幕窗口模式private @WindowingMode int mDisplayWindowingMode;/** @hide */@IntDef(prefix = { "WINDOWING_MODE_" }, value = {WINDOWING_MODE_UNDEFINED,//未定义WINDOWING_MODE_FULLSCREEN,//全屏WINDOWING_MODE_MULTI_WINDOW,//多窗口WINDOWING_MODE_PINNED,WINDOWING_MODE_SPLIT_SCREEN_PRIMARY,WINDOWING_MODE_SPLIT_SCREEN_SECONDARY,WINDOWING_MODE_FREEFORM,})public @interface WindowingMode {}//Activity的类型private @ActivityType int mActivityType;/** @hide */@IntDef(prefix = { "ACTIVITY_TYPE_" }, value = {ACTIVITY_TYPE_UNDEFINED,ACTIVITY_TYPE_STANDARD,ACTIVITY_TYPE_HOME,ACTIVITY_TYPE_RECENTS,ACTIVITY_TYPE_ASSISTANT,ACTIVITY_TYPE_DREAM,})public @interface ActivityType {}//窗口是否总是位于最上层private @AlwaysOnTop int mAlwaysOnTop;/** @hide */@IntDef(prefix = { "ALWAYS_ON_TOP_" }, value = {ALWAYS_ON_TOP_UNDEFINED,ALWAYS_ON_TOP_ON,ALWAYS_ON_TOP_OFF,})private @interface AlwaysOnTop {}
}

在这里插入图片描述
该类主要有以下几个关键属性:

  • mBounds: 屏幕尺寸
  • mAppBounds:不包含装饰窗口在内的窗口显示区域**(根据源码发现mAppBounds只排除了导航栏这个装饰窗口所在的区域,状态栏和输入法等装饰窗口所在的区域是被包含在内的)**
  • mMaxBounds:窗口可显示的最大区域
  • mRotation:当前屏幕设备的旋转角度
  • mWindowingMode:当前窗口的窗口模式,例如未定义、全屏、分屏、多窗口等
  • ActivityType:页面类型,例如未定义、标准、首页、最近任务等
  • mAlwaysOnTop:窗口是否总是位于最上层

二、WindowConfiguration的属性设置

2.1 Configuration类

WindowConfiguration在Android系统中基本都是作为Configuration类的内部属性出现的。

frameworks/base/core/java/android/content/res/Configuration.java

public final class Configuration implements Parcelable, Comparable<Configuration> {public final WindowConfiguration windowConfiguration = new WindowConfiguration();}

2.2 计算当前屏幕尺寸和当前窗口可显示的最大区域

这里我们主要是结合WMS模块的相关代码来分析WindowConfiguration的各个属性的来源;系统主要是通过DisplayContent的computeScreenConfiguration方法来计算当前屏幕对应的窗口配置信息的。

frameworks/base/services/core/java/com/android/server/wm/DisplayContent.java

class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>implements WindowManagerPolicy.DisplayContentInfo {DisplayInfo computeScreenConfiguration(Configuration outConfig, int rotation) {final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);//屏幕旋转角度final int dw = rotated ? mBaseDisplayHeight : mBaseDisplayWidth;//屏幕宽度final int dh = rotated ? mBaseDisplayWidth : mBaseDisplayHeight;//屏幕高度//注释1,对屏幕的实际宽高进行存储outConfig.windowConfiguration.setMaxBounds(0, 0, dw, dh);outConfig.windowConfiguration.setBounds(outConfig.windowConfiguration.getMaxBounds());final int uiMode = getConfiguration().uiMode;//UI模式final DisplayCutout displayCutout = calculateDisplayCutoutForRotation(rotation).getDisplayCutout();//计算屏幕的显示切口//注释2,调用computeScreenAppConfiguration方法computeScreenAppConfiguration(outConfig, dw, dh, rotation, uiMode, displayCutout);...代码省略...   }}

在注释1处根据屏幕旋转角度和基本显示尺寸,确定屏幕的实际宽高,并将其存储到WindowConfiguration的mMaxBounds属性和mBounds属性中。
在注释2处将屏幕实际宽度、高度、旋转角度、UI模式、屏幕显示切口作为参数,调用computeScreenAppConfiguration方法计算当前窗口可显示的安全区域。

2.3 计算当前窗口可显示的安全区域

class DisplayContent extends WindowContainer<DisplayContent.DisplayChildWindowContainer>implements WindowManagerPolicy.DisplayContentInfo {private final DisplayPolicy mDisplayPolicy;private void computeScreenAppConfiguration(Configuration outConfig, int dw, int dh,int rotation, int uiMode, DisplayCutout displayCutout) {//注释1,获取不包含系统装饰窗口的可显示屏幕区域final Point appSize = mDisplayPolicy.getNonDecorDisplaySize(dw, dh, rotation, uiMode, displayCutout);//注释2,获取不包含系统装饰窗口的可显示屏幕边界mDisplayPolicy.getNonDecorInsetsLw(rotation, dw, dh, displayCutout, mTmpRect);final int leftInset = mTmpRect.left;final int topInset = mTmpRect.top;//注释3,存储应用的可显示的安全区域outConfig.windowConfiguration.setAppBounds(leftInset /* left */, topInset /* top */,leftInset + appSize.x /* right */, topInset + appSize.y /* bottom */);//存储屏幕旋转角度outConfig.windowConfiguration.setRotation(rotation);//存储屏幕是横屏还是竖屏outConfig.orientation = (dw <= dh) ? ORIENTATION_PORTRAIT : ORIENTATION_LANDSCAPE;//屏幕像素密度final float density = mDisplayMetrics.density;final Point configSize = mDisplayPolicy.getConfigDisplaySize(dw, dh, rotation, uiMode,displayCutout);outConfig.screenWidthDp = (int) (configSize.x / density);outConfig.screenHeightDp = (int) (configSize.y / density);outConfig.compatScreenWidthDp = (int) (outConfig.screenWidthDp / mCompatibleScreenScale);outConfig.compatScreenHeightDp = (int) (outConfig.screenHeightDp / mCompatibleScreenScale);final boolean rotated = (rotation == ROTATION_90 || rotation == ROTATION_270);outConfig.compatSmallestScreenWidthDp = computeCompatSmallestWidth(rotated, uiMode, dw,dh);}}

frameworks/base/services/core/java/com/android/server/wm/DisplayPolicy.java

public class DisplayPolicy {Point getNonDecorDisplaySize(int fullWidth, int fullHeight, int rotation, int uiMode,DisplayCutout displayCutout) {int width = fullWidth;int height = fullHeight;int navBarReducedHeight = 0;int navBarReducedWidth = 0;//获取导航栏的位置final int navBarPosition = navigationBarPosition(fullWidth, fullHeight, rotation);if (hasNavigationBar()) {if (navBarPosition == NAV_BAR_BOTTOM) {navBarReducedHeight = getNavigationBarHeight(rotation, uiMode);} else if (navBarPosition == NAV_BAR_LEFT || navBarPosition == NAV_BAR_RIGHT) {navBarReducedWidth = getNavigationBarWidth(rotation, uiMode, navBarPosition);}}if (mExtraNavBarAlt != null) {final LayoutParams altBarParams = mExtraNavBarAlt.getLayoutingAttrs(rotation);final int altBarPosition = getAltBarPosition(altBarParams);if (altBarPosition == ALT_BAR_BOTTOM || altBarPosition == ALT_BAR_TOP) {if (altBarPosition == navBarPosition) {navBarReducedHeight = Math.max(navBarReducedHeight,getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR));} else {navBarReducedHeight += getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR);}} else if (altBarPosition == ALT_BAR_LEFT || altBarPosition == ALT_BAR_RIGHT) {if (altBarPosition == navBarPosition) {navBarReducedWidth = Math.max(navBarReducedWidth,getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR));} else {navBarReducedWidth += getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR);}}}//当前窗口的安全显示区域为屏幕宽高减去导航栏所在的区域height -= navBarReducedHeight;width -= navBarReducedWidth;//如果屏幕显示切口对象不为空,还要减去该区域if (displayCutout != null) {height -= displayCutout.getSafeInsetTop() + displayCutout.getSafeInsetBottom();width -= displayCutout.getSafeInsetLeft() + displayCutout.getSafeInsetRight();}return new Point(width, height);}public void getNonDecorInsetsLw(int displayRotation, int displayWidth, int displayHeight,DisplayCutout displayCutout, Rect outInsets) {outInsets.setEmpty();//系统存在导航栏if (hasNavigationBar()) {final int uiMode = mService.mPolicy.getUiMode();//获取导航栏的位置,最终返回的边界区域要去掉导航栏所在的区域int position = navigationBarPosition(displayWidth, displayHeight, displayRotation);if (position == NAV_BAR_BOTTOM) {outInsets.bottom = getNavigationBarHeight(displayRotation, uiMode);} else if (position == NAV_BAR_RIGHT) {outInsets.right = getNavigationBarWidth(displayRotation, uiMode, position);} else if (position == NAV_BAR_LEFT) {outInsets.left = getNavigationBarWidth(displayRotation, uiMode, position);}}if (mExtraNavBarAlt != null) {final LayoutParams extraNavLayoutParams =mExtraNavBarAlt.getLayoutingAttrs(displayRotation);final int position = getAltBarPosition(extraNavLayoutParams);if (position == ALT_BAR_BOTTOM) {outInsets.bottom = Math.max(outInsets.bottom,getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR));} else if (position == ALT_BAR_RIGHT) {outInsets.right = Math.max(outInsets.right,getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR));} else if (position == ALT_BAR_LEFT) {outInsets.left = Math.max(outInsets.left,getAltBarWidth(ITYPE_EXTRA_NAVIGATION_BAR));} else if (position == ALT_BAR_TOP) {outInsets.top = Math.max(outInsets.top,getAltBarHeight(ITYPE_EXTRA_NAVIGATION_BAR));}}//如果屏幕显示切口对象不为空,还要减去该区域if (displayCutout != null) {outInsets.left += displayCutout.getSafeInsetLeft();outInsets.top += displayCutout.getSafeInsetTop();outInsets.right += displayCutout.getSafeInsetRight();outInsets.bottom += displayCutout.getSafeInsetBottom();}}}

在注释1处调用DisplayPolicy的getNonDecorDisplaySize方法,获取不包含系统装饰窗口的可显示屏幕区域,将结果存放在类型为Point的appSize对象中,结合getNonDecorDisplaySize方法源码可以发现该方法只是去除了导航栏窗口所在的区域,另外如果有屏幕显示切口区域,还会去除屏幕显示切口区域。
在注释2处调用DisplayPolicy的getNonDecorInsetsLw方法,获取不包含系统装饰窗口的可显示屏幕边界,将结果存放在类型为Rect的mTmpRect对象中,结合getNonDecorInsetsLw方法源码可以发现该方法只是去除了导航栏窗口所在的区域,另外如果有屏幕显示切口区域,还会去除屏幕显示切口区域。
在注释3处会结合appSize和mTmpRect,将当前窗口可显示的安全区域存储到Configuration的WindowConfiguration中。

三、WindowConfiguration的作用

WindowConfiguration的toString方法包含了此类的所有关键信息。

public class WindowConfiguration implements Parcelable, Comparable<WindowConfiguration> {@Overridepublic String toString() {return "{ mBounds=" + mBounds+ " mAppBounds=" + mAppBounds+ " mMaxBounds=" + mMaxBounds+ " mWindowingMode=" + windowingModeToString(mWindowingMode)+ " mDisplayWindowingMode=" + windowingModeToString(mDisplayWindowingMode)+ " mActivityType=" + activityTypeToString(mActivityType)+ " mAlwaysOnTop=" + alwaysOnTopToString(mAlwaysOnTop)+ " mRotation=" + (mRotation == ROTATION_UNDEFINED? "undefined" : rotationToString(mRotation))+ "}";}public static String windowingModeToString(@WindowingMode int windowingMode) {switch (windowingMode) {case WINDOWING_MODE_UNDEFINED: return "undefined";case WINDOWING_MODE_FULLSCREEN: return "fullscreen";case WINDOWING_MODE_MULTI_WINDOW: return "multi-window";case WINDOWING_MODE_PINNED: return "pinned";case WINDOWING_MODE_SPLIT_SCREEN_PRIMARY: return "split-screen-primary";case WINDOWING_MODE_SPLIT_SCREEN_SECONDARY: return "split-screen-secondary";case WINDOWING_MODE_FREEFORM: return "freeform";}return String.valueOf(windowingMode);}public static String activityTypeToString(@ActivityType int applicationType) {switch (applicationType) {case ACTIVITY_TYPE_UNDEFINED: return "undefined";case ACTIVITY_TYPE_STANDARD: return "standard";case ACTIVITY_TYPE_HOME: return "home";case ACTIVITY_TYPE_RECENTS: return "recents";case ACTIVITY_TYPE_ASSISTANT: return "assistant";case ACTIVITY_TYPE_DREAM: return "dream";}return String.valueOf(applicationType);}public static String alwaysOnTopToString(@AlwaysOnTop int alwaysOnTop) {switch (alwaysOnTop) {case ALWAYS_ON_TOP_UNDEFINED: return "undefined";case ALWAYS_ON_TOP_ON: return "on";case ALWAYS_ON_TOP_OFF: return "off";}return String.valueOf(alwaysOnTop);}
}
//frameworks/base/core/java/android/view/Surface.java
public class Surface implements Parcelable {public static String rotationToString(int rotation) {switch (rotation) {case Surface.ROTATION_0: {return "ROTATION_0";}case Surface.ROTATION_90: {return "ROTATION_90";}case Surface.ROTATION_180: {return "ROTATION_180";}case Surface.ROTATION_270: {return "ROTATION_270";}default: {return Integer.toString(rotation);}}}
}

当我们调用如下方法

 Log.i(TAG, "getWindowInfo: config = " + getResources().getConfiguration());

或者通过dumpsys window windows导出当前所有窗口的堆栈信息,都可以得到和Configuration类相关的以下信息:

config={1.0 ?mcc?mnc [zh_CN] ldltr sw360dp w764dp h324dp 480dpi nrml long land finger -keyb/h/h -nav/h winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(107, 0 - 2400, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mDisplayRotation=ROTATION_90 mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_90} s.2 fontWeightAdjustment=0mThemeChanged= 0, mThemeChangedFlags= 0, mFlipFont= 0, mAccessibleChanged= -1, mUxIconConfig= 3468921665126662176, mMaterialColor= 0, mUserId= 0, mFontUserId= 0, mFontVariationSettings= 226, mFoldingAngle = -1.0, mIconPackName= , mDarkModeBackgroundMaxL= 0.0, mDarkModeDialogBgMaxL= 27.0, mDarkModeForegroundMinL= 100.0, mOplusConfigType= 1, mOplusChangedConfigs= 0, OpSans= 0, mBurmeseFontFlag= 2, mFlag= 0, mPuttDisplayFlag= -1}

这里我们重点关注和WindowConfiguration相关的信息:

winConfig={ mBounds=Rect(0, 0 - 2400, 1080) mAppBounds=Rect(107, 0 - 2400, 1080) mMaxBounds=Rect(0, 0 - 2400, 1080) mWindowingMode=fullscreen mDisplayWindowingMode=fullscreen mActivityType=standard mAlwaysOnTop=undefined mRotation=ROTATION_90}
  • mBounds=Rect(0, 0 - 2400, 1080):屏幕尺寸
  • mAppBounds=Rect(107, 0 - 2400, 1080):窗口可显示的安全区域,此属性会影响应用具体加载那个layout下面的布局文件,系统会优先选择尺寸最接近2293x1080的布局文件。
  • mMaxBounds=Rect(0, 0 - 2400, 1080):窗口可显示的最大区域
  • mWindowingMode=fullscreen:窗口为全屏模式
  • mDisplayWindowingMode=fullscreen:屏幕设备窗口为全屏模式
  • mActivityType=standard:页面类型未定义
  • mAlwaysOnTop=undefined:窗口悬浮模式未定义
  • mRotation=ROTATION_90:屏幕设备的旋转角度为90度

借助这些属性,开发者能够更好地适配不同的设备配置和屏幕状态,确保应用在不同环境下的一致性和优化。

四、修改WindowConfiguration的配置信息,刷新窗口UI视图

4.1 通过adb 修改屏幕旋转角度

我们可以通过以下指令获取当前屏幕的旋转角度

adb shell settings get system user_rotation #0:自然方向(竖屏)1:右旋转 90 度(横屏)2:倒转 180 度(反向竖屏)3:左旋转 270 (横屏)

还可以通过如下配置修改当前屏幕的旋转角度

adb shell settings put system user_rotation <value>

4.2 实现原理

当我们修改system数据库中的user_rotation字段的时候,会触发以下代码逻辑。

frameworks/base/services/core/java/com/android/server/wm/DisplayRotation.java

//frameworks/base/core/java/android/provider/Settings.java
public final class Settings {public static final String USER_ROTATION = "user_rotation";
}
public class DisplayRotation {private final WindowManagerService mService;private class SettingsObserver extends ContentObserver {SettingsObserver(Handler handler) {super(handler);}void observe() {final ContentResolver resolver = mContext.getContentResolver();...代码省略...//注释1,监听system数据库user_rotation字段的变化resolver.registerContentObserver(Settings.System.getUriFor(Settings.System.USER_ROTATION), false, this,UserHandle.USER_ALL);updateSettings();}@Overridepublic void onChange(boolean selfChange) {//注释2,判断设置是否发生了变化,如果发生了变化调用WMS的updateRotation方法if (updateSettings()) {mService.updateRotation(true /* alwaysSendConfiguration */,false /* forceRelayout */);}}}private boolean updateSettings() {final ContentResolver resolver = mContext.getContentResolver();boolean shouldUpdateRotation = false;synchronized (mLock) {...代码省略...//获取当前屏的旋转角度final int userRotation = Settings.System.getIntForUser(resolver,Settings.System.USER_ROTATION, Surface.ROTATION_0,UserHandle.USER_CURRENT);if (mUserRotation != userRotation) {mUserRotation = userRotation;shouldUpdateRotation = true;}...代码省略...return shouldUpdateRotation;}}

在注释1处DisplayRotation类会监听system数据库的user_rotation字段的变化,当该字段发生变化的时候,会在注释2处调用WindowManagerServices的updateRotation方法来通知窗口当前屏幕的旋转角度发生了变化。

4.3 WindowManagerService的updateRotation方法

frameworksbase/services/core/java/com/android/server/wm/WindowManagerService.java

public class WindowManagerService extends IWindowManager.Stubimplements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {//更新当前的屏幕的旋转角度@Overridepublic void updateRotation(boolean alwaysSendConfiguration, boolean forceRelayout) {updateRotationUnchecked(alwaysSendConfiguration, forceRelayout);}private void updateRotationUnchecked(boolean alwaysSendConfiguration, boolean forceRelayout) {...代码省略...try {synchronized (mGlobalLock) {boolean layoutNeeded = false;final int displayCount = mRoot.mChildren.size();for (int i = 0; i < displayCount; ++i) {final DisplayContent displayContent = mRoot.mChildren.get(i);Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "updateRotation: display");final boolean rotationChanged = displayContent.updateRotationUnchecked();Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);if (rotationChanged) {mAtmService.getTaskChangeNotificationController().notifyOnActivityRotation(displayContent.mDisplayId);}if (!rotationChanged || forceRelayout) {displayContent.setLayoutNeeded();layoutNeeded = true;}if (rotationChanged || alwaysSendConfiguration) {//更新屏幕设备的配置信息displayContent.sendNewConfiguration();}}...代码省略...}} finally {Binder.restoreCallingIdentity(origId);Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);}} }    
class DisplayContent extends RootDisplayArea implements WindowManagerPolicy.DisplayContentInfo {void setLayoutNeeded() {mLayoutNeeded = true;}void sendNewConfiguration() {if (!isReady()) {return;}if (mDisplayRotation.isWaitingForRemoteRotation()) {return;}//更新屏幕设备的配置信息final boolean configUpdated = updateDisplayOverrideConfigurationLocked();if (configUpdated) {return;}}boolean updateDisplayOverrideConfigurationLocked() {final RecentsAnimationController recentsAnimationController =mWmService.getRecentsAnimationController();if (recentsAnimationController != null) {recentsAnimationController.cancelAnimationForDisplayChange();}Configuration values = new Configuration();computeScreenConfiguration(values);mAtmService.mH.sendMessage(PooledLambda.obtainMessage(ActivityManagerInternal::updateOomLevelsForDisplay, mAtmService.mAmInternal,mDisplayId));Settings.System.clearConfiguration(values);updateDisplayOverrideConfigurationLocked(values, null /* starting */,false /* deferResume */, mAtmService.mTmpUpdateConfigurationResult);return mAtmService.mTmpUpdateConfigurationResult.changes != 0;}}
http://www.whsansanxincailiao.cn/news/32016252.html

相关文章:

  • 诸塈市建设局网站/网推是什么
  • 装修网站建设优缺点/辅导班培训机构
  • 淘宝店做网站建设不能开直通车/短视频培训要多少学费
  • 南昌做网站的公司有哪些/竞价广告推广
  • 在线兼容测试网站/百度招商加盟推广
  • 域名解析后网站怎么建设/自媒体有哪些平台
  • 自己建设网站的利弊/百度seo新站优化
  • 网站服务器租用一般费用/建立网站需要多少钱
  • 南京网站设计开发/山东网页定制
  • 上海各区的网站有哪些公司/网络营销活动案例
  • 做网站 网络科技公司/技术培训机构
  • 香港美国服务器/合肥百度seo排名
  • 测试页面网站建设/情感营销的十大案例
  • 房山营销型网站制作开发/公众号怎么开通
  • dw软件个人简历网站怎么做/制作一个网站需要多少费用
  • 百度网盘网页版登录/seo公司厦门
  • 旅游网站流程图/白酒最有效的推广方式
  • 织梦换wordpress/长沙网站seo推广
  • 网站建设预期周期/口碑营销的主要手段有哪些
  • 三级分销网站建设/二级子域名ip地址查询
  • 太原网站制作企业/推广app大全
  • 南昌做网站要多少钱/网站页面怎么优化
  • 大连网站建设怎么做/西安优化seo
  • 乐辰科技网站建设/视频号的链接在哪
  • 做网站优化时代码结构关系大吗/网络推广好做吗?
  • 网站管理 官网/杭州百度推广优化排名
  • 深圳做网站乐云seo费用优惠/免费投放广告的平台
  • 贴吧 wordpress/沈阳seo技术
  • 能买源码的网站有哪些/廊坊关键词优化平台
  • 昆明做网站找启搜网络/怎样在百度上发布免费广告