Window的创建

Window的创建

上一篇说到了Window和WindowManager的关系并且讲述了WindowManager如何添加Window与Window内部的三个方法的实现

这篇主要讲几个常见的Window的创建比如Activity,Dialog和Toast

其中Activity属于应用Window

Dialog属于子Window

Toast属于系统Window

z-ordered越来越大,它的优先级就越来越大

Activity的Window的创建

Activity的启动最终是由ActivityThreadperformLaunchActivity,这个方法在内部会通过内加载器创建一个Activity的实例变量。

然后调用attach,将一系列上下文环境变量关联起来,确保 Activity 可以正常运行并与应用程序的其他组件进行通信。

我们可以看看attach关联了哪些

我们看看它的源码

通过ctrl+n

 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.activityConfigCallback,r.assistToken, r.shareableActivityToken);

并且在attach的过程中还会创建Activity所属的Window

记得我们在View的工作原理的时候说到过:

Activity创建完成后,会把DecorView添加到Window中,并创建相应的ViewRootImpl,再把ViewRootImpl与DecorView关联起来

而Activity的Window的创建就是在performLaunchActivity中创建Activity后调用attach来创建的

1.Window的创建

问题:2个Window的不同

attach

《艺术开发探索》中说在attach中通过PolicyManagermakeNewWindow获得window,但我找了半天没找到那段源码

只在attach里面找到了

 final void attach(Context context, ActivityThread aThread,Instrumentation instr, IBinder token, int ident,Application application, Intent intent, ActivityInfo info,CharSequence title, Activity parent, String id,NonConfigurationInstances lastNonConfigurationInstances,Configuration config, String referrer, IVoiceInteractor voiceInteractor,Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,IBinder shareableActivityToken) {attachBaseContext(context);mFragments.attachHost(null /*parent*/);mWindow = new PhoneWindow(this, window, activityConfigCallback);mWindow.setWindowControllerCallback(mWindowControllerCallback);mWindow.setCallback(this);mWindow.setOnWindowDismissedCallback(this);mWindow.getLayoutInflater().setPrivateFactory(this);if (info.softInputMode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {mWindow.setSoftInputMode(info.softInputMode);}if (info.uiOptions != 0) {mWindow.setUiOptions(info.uiOptions);}mUiThread = Thread.currentThread();mMainThread = aThread;mInstrumentation = instr;mToken = token;mAssistToken = assistToken;mShareableActivityToken = shareableActivityToken;mIdent = ident;mApplication = application;mIntent = intent;mReferrer = referrer;mComponent = intent.getComponent();mActivityInfo = info;mTitle = title;mParent = parent;mEmbeddedID = id;mLastNonConfigurationInstances = lastNonConfigurationInstances;if (voiceInteractor != null) {if (lastNonConfigurationInstances != null) {mVoiceInteractor = lastNonConfigurationInstances.voiceInteractor;} else {mVoiceInteractor = new VoiceInteractor(voiceInteractor, this, this,Looper.myLooper());}}mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);if (mParent != null) {mWindow.setContainer(mParent.getWindow());}mWindowManager = mWindow.getWindowManager();mCurrentConfig = config;mWindow.setColorMode(info.colorMode);mWindow.setPreferMinimalPostProcessing((info.flags & ActivityInfo.FLAG_PREFER_MINIMAL_POST_PROCESSING) != 0);getAutofillClientController().onActivityAttached(application);setContentCaptureOptions(application.getContentCaptureOptions());}

即它没有用**PolicyManager.makeNewWindow(this)**来创建Window的

而是

mWindow = new PhoneWindow(this, window, activityConfigCallback);

其中这里面的window对象来自

抽象类

public abstract class Window

chatGPT的回答是:

实际的 Window 创建是在 PhoneWindow 中进行的,而不是在 Activityattach() 方法中直接调用 PolicyManager.makeNewWindow(this)

final void attach(...) {...mWindow = new PhoneWindow(this, window, activityConfigCallback);...
}private void performLaunchActivity(...) {...// 创建 Activity 的实例Activity activity = instantiateActivity(cl, component);...// 创建 PhoneWindow 对象Window window = policyManager.makeNewWindow(activity);...// 调用 attach() 方法activity.attach(..., window, ...);...
}

总结起来,mWindow = new PhoneWindow(this, window, activityConfigCallback); 创建了一个具体的 PhoneWindow 对象,用于管理 Activity 的窗口。而 Window window = policyManager.makeNewWindow(activity); 创建了一个基本的 Window 对象,作为系统策略管理器与 Activity 的窗口之间的接口。

第一个Window是具体的窗口实现,用于管理 Activity 的窗口。

第二个Window是系统级别的Window对象,并不是直接与 Activity 关联的窗口对象。

并且给出的最终回复,在 Activity 的创建过程中,主要是创建了一个具体的 PhoneWindow 对象,用于表示和管理 Activity 的窗口。系统级别的 Window 对象由策略管理器创建,用于处理窗口的管理和操作。


我们创建完成PhoneWindow后,Activity实现了windowcallBack接口,可以把Activity自己设置为window的观察者。然后我们就开始初始化WindowManager,记得之前我们说过的Window没办法直接访问,我们只能通过访问WindowManager来访问Window

mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);     

我们继续看看setWindowManager的源码

2.WindowManager的初始化过程

public void setWindowManager(WindowManager wm, IBinder appToken, String appName,boolean hardwareAccelerated) {mAppToken = appToken;mAppName = appName;mHardwareAccelerated = hardwareAccelerated;if (wm == null) {//获取到应用服务的WindowManagerwm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);}mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

mAppToken,mAppName, mHardwareAccelerated这三个就是给它们赋了个值m主要还是下面的

先判断传进去的WindowManager为不为空,为空的话进行初始化

,这个初始化与上一篇博客我进行完WindowManager的布局后的初始化一样都是

WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);

然后将**((WindowManagerImpl)wm).createLocalWindowManager(this)赋给mWindowManager**

话说mWindowManager是什么,我们点击它,会发现它是一个全局变量

private WindowManager mWindowManager;

而((WindowManagerImpl)wm).createLocalWindowManager(this)是在创建一个本地的窗口管理器对象。

本地窗口管理器是一个与设备本地窗口系统交互的对象,它提供了与底层窗口系统通信的功能。通过创建本地窗口管理器,可以在应用程序中实现对窗口的创建、显示、布局、交互等操作。


PhoneWindow类型的mWindow已经通过

mWindow = new PhoneWindow();

创建好了

刚才在setWindowManager中的内部也成功创建好了WindowManager

便可以成功绑定WindowWindowManager

又因为我们之前说过WindowManager主要是由WindowManagerImpl实现的,

所以刚才那句话也就相当于WindowWindowManagerImpl进行成功的绑定

我们就可以通过调用WindowManagerImpl的那三个方法来实现Windowadd,remove,update

3.流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fVB1PIU0-1685279766886)(../../assets/流程图-导出 (3)]-1685262524947-1.png)

4.把Activity的布局文件设置给PhoneWindow

上面提到调用Activityattach方法之后,会回调ActivityonCreate方法,在其中会调用setContentView来设置布局,如下:

public void setContentView(View view, ViewGroup.LayoutParams params) {getWindow().setContentView(layoutResID);initWindowDecorActionBar();
}

Activity将setContentView具体实现交给了Window处理,这里的getWindow返回我们上面创建的PhoneWindow对象。我们继续看下去:

// 注意他有多个重载的方法,要选择参数对应的方法
public void setContentView(int layoutResID) {// 创建DecorViewif (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 {// **这里根据布局id加载布局,把Activity的布局加载到DecorView的**mContentParent**中**mLayoutInflater.inflate(layoutResID, mContentParent);}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {// 回调activity的方法,**通知Activity视图已经发生改变**cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}

首先我们先看第一个if

虽然不知道它在干什么但是我们看到了

  installDecor();

就可以明白它创建了一个DecorView

首先判断 mContentParent 是否为 null。mContentParent 是 Window 的内容视图的父级容器,如果为 null,表示 DecorView 还未创建,因此需要调用 installDecor() 来创建并安装 DecorView。

如果 mContentParent 不为 null,则进一步判断是否存在 FEATURE_CONTENT_TRANSITIONS 特性。如果不存在该特性,表示不需要进行内容转场动画,那么可以通过 mContentParent.removeAllViews() 清空已有的内容视图。

之后第二个if就是判断是否存在 FEATURE_CONTENT_TRANSITIONS 特性,如果开启了该特性,则会使用场景切换的方式来加载布局

没开启的话会直接使用 mLayoutInflater.inflate(layoutResID, mContentParent) 方法将指定布局文件加载到 DecorView 的 mContentParent 容器中,完成 Activity 的布局加载。

之后就是 回调Activity的callBack方法

4.1简单流程

总之把Activity的布局文件加载到PhoneWindow就以下几个流程

1.判断DecorView是否创建,没有创建就创建

2.将Activity的布局加载到DecorView的mContentParent

3.进行Activity的回调

所以我们就可以知道了Activity的加载布局文件为什么是setContentView了,因为Activity的加载布局文件最后加载到了Window的DecorWindow的**ContentView**中


5.总结前三步

现在回顾以下

我们刚才做的

1是创建了Window,(Window是在ActivityThread中调用performLaunchActivity中创建的Activity调用attach中创建的)

2.如何创建WindowManager,并且进行了绑定(在Window调用setWindowManager中创建的WindowManager)

3.如何将Activity的布局文件设置给PhoneWindow(1.判断DecorView是否创建

​ 2.将布局文件传给DecorView的ContentParent

​ 3.进行Activity的回调,给Activity说DecorView已经创建好了)

现在就差最后一步了,那就是把DecorView作为window添加到屏幕上。

肯定想的是那很简单啊,WindowManager已经和Window绑定了,那么我们直接WindowManager.add不就好了

而且在Activity的创建的时候我们说过:

Activity创建完成后,会把DecorView添加到Window中,并创建相应的ViewRootImpl,再把ViewRootImpl与DecorView关联起来

但是

但是这个时候由于DecorView并没有被WindowManager识别,所以这个时候的Window无法提供具体功能,因为它还无法接收外界的输入信息

6.将DecorView添加到Window中

还是在ActivityThread中

我们调用

public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,String reason) {// 调用Activity的onResume方法final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);...// 让decorView显示到屏幕上if (r.activity.mVisibleFromClient) {r.activity.makeVisible();
}

首先第一步是performResumeActivity进行了onResume方法的回调

第二步是**makeVisible()**让decorView显示到屏幕上

我们点击makeVisible()看里面的源码

void makeVisible() {if (!mWindowAdded) {ViewManager wm = getWindowManager();wm.addView(mDecor, getWindow().getAttributes());mWindowAdded = true;}mDecor.setVisibility(View.VISIBLE);
}

会发现它在这个地方调用了addView,并且将mDecor设置为可见

7.Activity创建的流程图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-X9W5bacP-1685279766887)(../../assets/流程图-导出 (4)]-1685265053644-3.png)

Dialog的Window创建

只要你把Activity的Window的创建好了,Dialog的Window创建就很容易了

Dialog(@NonNull Context context, @StyleRes int themeResId, boolean createContextThemeWrapper) {...// 获取windowManagermWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
//mWindowManager其实是Activity的WindowManager,这里的context一般是activity// 构造PhoneWindowfinal Window w = new PhoneWindow(mContext);mWindow = w;// 初始化PhoneWindoww.setCallback(this);w.setOnWindowDismissedCallback(this);w.setOnWindowSwipeDismissedCallback(() -> {if (mCancelable) {cancel();}});w.setWindowManager(mWindowManager, null, null);w.setGravity(Gravity.CENTER);mListenersHandler = new ListenersHandler(this);
}public Object getSystemService(@ServiceName @NonNull String name) {if (getBaseContext() == null) {throw new IllegalStateException("System services not available to Activities before onCreate()");}// 获取的是activity的windowManagerif (WINDOW_SERVICE.equals(name)) {return mWindowManager;} else if (SEARCH_SERVICE.equals(name)) {ensureSearchManager();return mSearchManager;}return super.getSystemService(name);
}

1.创建Window

这步和Activity的Window创建一样,就没必要讲了

2.初始化DecorView并将Dialog视图添加到DecorView中

  public void setContentView(int layoutResID) {mWindow.setContentView(layoutResID);}

3.将DecorView添加到Window并显示

public void show() {...// 回调onStart方法,获取前面初始化好的decorviewonStart();mDecor = mWindow.getDecorView();...WindowManager.LayoutParams l = mWindow.getAttributes();...// 利用windowManager来添加window    mWindowManager.addView(mDecor, l);
//这里的mWindowManager是Activity的WindowManager...mShowing = true;sendShowMessage();
}

Dialog的Window创建和Activity的Window创建过程有很多类似的地方,二者几乎没有区别

Dialog在被关闭的时候,会通过WindowManager来移除DecorView:

mWindowManager.removeViewImmediate(mDecor);

removeViewImmediate是同步方法

removeView是异步方法

普通的Dialog有一个特殊之处,那就是必须采用Activity的Context,如果采用Application的Context,那么就会报错

是没有应用token导致的,而应用token一般只有Activity拥有,所以只需要用Activity作为Context来显示对话框。

系统Window比较特殊,它可以不要token

我们只需要这么改就可以运行了

dialog.getWindow().setType(LayoutParams.TYPE_SYSTEM.ERROR);

还有

<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Toast的Window创建

Toast比Dialog稍微难一点,虽然Toast也是基于Window来实现的,但是Toast有定时取消这一功能。

所以系统采用了Handler

Toast内部有2类IPC过程:

1.Toast访问NotificationManagerService

2.NotificationManagerService回调Toast里的TN接口

Toast属于系统Window,它内部的视图由两种方式指定,一种是系统默认的样式另一种是通过setView方法来指定一个自定义View,视图都对应Toast的一个View类型的内部成员mNextView。Toast提供了show和cancel分别用于显示和隐藏Toast,show和cancel的内部是IPC过程,

Toast的show()

首先看Toast的显示过程,它调用了NMS中的enqueueToast方法,如下所示。

public void show() {if (mNextView == null) {throw new RuntimeException("setView must have been called");}INotificationManager service = getService();String pkg = mContext.getOpPackageName();TN tn = mTN;tn.mNextView = mNextView;try {service.enqueueToast(pkg, tn, mDuration);} catch (RemoteException e) {// Empty}
}

NMSenqueueToast方法的第一个参数表示当前应用的包名,第二个参数tn表示远程回调,第三个参数表示Toast的时长。

enqueueToast会先将Toast请求封装成为ToastRecord对象并将它添加到一个名为mToastQueue的ArrayList集合中

对于非系统应用来说,mToastQueue最多同时存在50个ToastRecord,这样做是为了防止DOS,如果不这样做,使用大量循环弹出Toast会导致其他应用没机会弹出Toast,那么对于其他应用的Toast请求,系统的行为就是拒绝服务

// Limit the number of toasts that any given package except the android
// package can enqueue.  Prevents DOS attacks and deals with leaks.
if (! isSystemToast) {int count = 0;final int N = mToastQueue.size();for (int i=0; i<N; i++) {final ToastRecord r = mToastQueue.get(i);if (r.pkg.equals(pkg)) {count++;if (count >= MAX_PACKAGE_NOTIFICATIONS) {Slog.e(TAG, "Package has already posted " + count+ " toasts. Not showing more. Package=" + pkg);return;}}}}
}

正常情况下,一个app的ToastRecord不会到达上限,当ToastRecord被添加到mToastQueue后,NMS会通过showNextToastLocked方法显示当前的Toast

void showNextToastLocked() {ToastRecord record = mToastQueue.get(0);while (record ! = null) {if (DBG) Slog.d(TAG, "Show pkg=" + record.pkg + " callback=" + record.callback);try {record.callback.show();scheduleTimeoutLocked(record);return;} catch (RemoteException e) {Slog.w(TAG, "Object died trying to show notification " + record.callback+ " in package " + record.pkg);// remove it from the list and let the process dieint index = mToastQueue.indexOf(record);if (index >= 0) {mToastQueue.remove(index);}keepProcessAliveLocked(record.pid);if (mToastQueue.size() > 0) {record = mToastQueue.get(0);} else {record = null;}}}}

需要注意的是Toast的显示是由ToastRecord的callback完成的,这个callback是Toast中的TN对象的远程Binder

通过callback访问TN中的方法需要跨进程完成,最终被调用的TN中的方法会运行在发起Toast请求的应用的Binder线程池

Toast显示以后,NMS还会通过scheduleTimeoutLocked方法发送一个延迟消息,消息的延迟取决于Toast的时长

private void scheduleTimeoutLocked(ToastRecord r)
{mHandler.removeCallbacksAndMessages(r);Message m = Message.obtain(mHandler, MESSAGE_TIMEOUT, r);long delay = r.duration == Toast.LENGTH_LONG ? LONG_DELAY : SHORT_DELAY;mHandler.sendMessageDelayed(m, delay);
}

LONG_DELAY为3.5s,而 SHORT_DELAY为2.5s

我最开始以为这个就决定着

Toast.makeText(MainActivity.this,"nihao",Toast.LENGTH_LONG).show()

的这个的持续时间,后来搜了一下发现:

实际上,delay 的值是用于处理 Toast 消息的显示时间间隔,而不是 Toast 消息的总显示时间。在 Android 中,LONG_DELAYSHORT_DELAY 是表示延迟时间的常量,具体的值可能会根据系统的设置而有所不同。

Toast的show()总结

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R23kHBJI-1685279766888)(../../assets/流程图-导出 (5)].png)

我对show方法的理解就是Toast把它封装到ToastRecord并把它放入ToastQueue集合中

然后NMS会进行回调ToastRecord中的callback即Toast中的TN方法

Toast的hide()

try {record.callback.hide();
} catch (RemoteException e) {Slog.w(TAG, "Object died trying to hide notification " + record.callback+ " in package " + record.pkg);// don't worry about this, we're about to remove it from// the list anyway
}

Toast的显示和隐藏的本质

可以看出来Toast的显示和隐藏过程实际上是通过Toast的TN这个类实现的,它有一个**show()方法和一个hide()**方法

分别对应Toast的显示和隐藏

由于这两个方法是被NMS跨进程调用的,因此它们都在Binder线程池中,为了将执行环境切换到Toast所在的线程,内部使用Handler

/*** schedule handleShow into the right thread*/
@Override
public void show() {if (localLOGV) Log.v(TAG, "SHOW: " + this);mHandler.post(mShow);
}/*** schedule handleHide into the right thread*/
@Override
public void hide() {if (localLOGV) Log.v(TAG, "HIDE: " + this);mHandler.post(mHide);
}

可以发现mShow和mHide是两个Runnable,内部分别调用了handleShow和handleHide,所以它们两个才是真正完成显示和隐藏的

TN的handleShow会将Toast视图加载到Window中

mWM = (WindowManager)context.getSystemService(Context.WINDOW_SERVICE);
mWM.addView(mView, mParams)

而TN的handleHide会将Toast视图从Window中移除

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/69300.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

密码验证 长度八位包含字母数字特殊字符

View Code 1 #region 密码验证2 if (tbPassword.Text "")3 {4 CommonFunction.ShowMessage(this.Page, "密码不能为空");5 return;6 }7 …

smart计算机英语作文,关于科技的英语作文(精选5篇)

关于科技的英语作文(精选5篇) 在平平淡淡的日常中&#xff0c;大家都跟作文打过交道吧&#xff0c;写作文可以锻炼我们的独处习惯&#xff0c;让自己的心静下来&#xff0c;思考自己未来的方向。一篇什么样的作文才能称之为优秀作文呢&#xff1f;下面是小编精心整理的关于科技…

华为鸿蒙的科技话题作文800字,科技的发展作文800字4篇

科技的发展作文800字4篇 科技改变生活&#xff0c;可以说没有科技的高速发展就没有今天的我们。那么以下是小编为大家整理的科技的发展作文800字&#xff0c;欢迎大家阅读&#xff01; 科技的发展作文800字(一) 随着科学技术的高度发展&#xff0c;科技是利是弊成了人们热议的话…

计算机未来的发展英语作文,关于科技发展英语作文(通用10篇)

关于科技发展英语作文(通用10篇) 在平平淡淡的学习、工作、生活中&#xff0c;大家总免不了要接触或使用作文吧&#xff0c;写作文可以锻炼我们的独处习惯&#xff0c;让自己的心静下来&#xff0c;思考自己未来的方向。那么一般作文是怎么写的呢&#xff1f;下面是小编为大家整…

计算机技术发展作文,【推荐】科技发展作文三篇

【推荐】科技发展作文三篇 在日复一日的学习、工作或生活中&#xff0c;大家都写过作文&#xff0c;肯定对各类作文都很熟悉吧&#xff0c;作文是经过人的思想考虑和语言组织&#xff0c;通过文字来表达一个主题意义的记叙方法。相信许多人会觉得作文很难写吧&#xff0c;下面是…

Android混合开发快速上手掌握

目录 一 混合开发简介 二 Android-Js互调 2.1 准备自己的html文件 2.2 WebView控件的准备设置 2.3 Android调用Js代码 2.4 Js调用Android方法和传参数 三 常用的几个方法和注意点 3.1 WebViewClient中的shouldOverrideUrlLoading拦截url 3.2 WebViewClient中的onPageS…

安卓开发快速集成即时通讯聊天,只需几行代码轻松实现

信贸通即时通讯系统&#xff0c;一款跨平台可定制的 P2P 即时通信系统&#xff0c;为电子商务网站及各行业门户网站和企事业单位提供“一站式”定制解决方案&#xff0c;打造一个稳定&#xff0c;安全&#xff0c;高效&#xff0c;可扩展的即时通信系统&#xff0c;支持在线聊天…

IM即时通讯聊天,5分钟显示一次时间。JS

想在聊天界面想做个和微信一样的时间显示 达到下图这种效果 百度了一下&#xff0c;发现都是有点不全的&#xff0c;把网上的合并了一下组成下方的js文件 记录一下 1.建议新建一个JS文件 common.js 1.第一个方法是把时间戳转成具体时间日期 /** * 对Date的扩展&#xff0c;将…

GPT-4 Copilot X震撼来袭!写代码效率10倍提升,码农遭降维打击

因公众号更改推送规则&#xff0c;请点“在看”并加“星标”第一时间获取精彩技术分享 点击关注#互联网架构师公众号&#xff0c;领取架构师全套资料 都在这里 0、2T架构师学习资料干货分 上一篇&#xff1a;2T架构师学习资料干货分享 大家好&#xff0c;我是互联网架构师&…

什么是生成器 — 一篇文章让你看懂

嗨嗨&#xff0c;我是小圆 ~ 今天来给大家讲讲什么是生成器 生成器是 Python 初级开发者最难理解的概念之一&#xff0c;虽被认为是 Python 编程中的高级技能&#xff0c;但在各种项目中可以随处见到生成器的身影&#xff0c;你得不得去理解它、使用它、甚至爱上它。 提到生成器…

怎么才能大批量生成原创文章

要大批量生成原创文章并不容易。毕竟&#xff0c;原创文章需要花费较多地时间和精力&#xff0c;才能够展现出高质量、有价值地内容。以下是一些方法可以帮助您大批量生成原创文章&#xff1a;1. 利用关键词通过使用关键词工具&#xff0c;寻找与您网站或品牌相关地长尾关键词。…

新媒体必备小技能——文章生成图片

相信公众号运营大家都不陌生了&#xff0c;与运营和自媒体相关的工作大多都会用到图文编辑&#xff0c;当然好看的排版直接影响读者的阅读体验&#xff01;在日常编辑推文以及制作宣传的过程中&#xff0c;图片是必不可少的重要组成部分&#xff01;96编辑器的一键生成图片功能…

狗屁文章生成器-批量生成原创文章自动发布网站-免费下载

狗屁文章生成器,什么是狗屁文章生成器&#xff0c;狗屁文章生成器从字面意思都能理解出来&#xff0c;就是生成的文章毫无逻辑感&#xff0c;胡乱生成&#xff0c;毫无可读性。只需要输入关键词就能实现狗屁文章生成。狗屁文章生成器。火于2020年某老板喊一员工写3000字原创检讨…

AI文章生成

文章 &#x1f9d0;一、我们在做什么&#x1f971;二、项目详情1.前端&#x1f642;&#xff08;1&#xff09;基本要求&#x1f610;&#xff08;2&#xff09;批量操作功能&#x1f641;&#xff08;3&#xff09;模式选择功能 &#x1f61f;&#xff08;4&#xff09;模型选…

文章生成器-原创文章生成器

在网络营销领域&#xff0c;优质文章是吸引新客户和保留老客户的重要工具。然而&#xff0c;生成高质量且符合SEO优化的文章并不是一件容易的事情。这就是为什么网站文章生成器如今备受欢迎的原因。而在众多的文章生成工具中&#xff0c;147GPT批量生成文章软件是一款非常出色的…

GPT关键词挖掘,自动关键词文章生成

随着互联网的发展&#xff0c;内容营销已成为企业营销策略中不可或缺的一环。有效的关键词文章生成可以帮助企业吸引更多的潜在客户&#xff0c;提高品牌曝光度和转化率&#xff0c;从而实现营销目标。 关键词文章生成是指根据特定的关键词和主题&#xff0c;使用软件工具自动生…

ChatGPT添加插件功能:开始联网集成第三方服务 渐成操作系统

雷递网 乐天 3月24日 人工智能公司OpenAI日前宣布&#xff0c;正为ChatGPT添加对插件的支持——将其与第三方服务集成或允许其访问最新信息的扩展。OpenAI称&#xff0c;正从小处着手&#xff0c;研究现实世界的使用、影响、安全和校准挑战。 “据我们的迭代部署理念&#xff0…

最高年薪近56万!猎聘重磅发布2023最新AIGC就业趋势大数据报告

©作者 | 编辑部 来源 | 新智元 猎聘大数据研究院重磅发布《AIGC就业趋势大数据报告2023》&#xff0c;招聘平均年薪已达40万&#xff0c;博士需求量同比增长超100%。 不用赘述&#xff0c;大家都知道&#xff0c;最近半年ChatGPT是有多么火爆。 随着ChatGPT的全球爆火&…

文心一言与GPT-4比对测试!

Waitlist了三个星期&#xff0c;今天下午终于拿到了百度文心一言的体验资格&#xff0c;于是立刻展开测试。 根据文心一言网页端信息显示&#xff0c;目前最新发版是4月1号的版本&#xff0c;版本号是v1.0.3&#xff0c;应该是从上个月16号发布会以后又做了两版迭代。根据文心一…

2013年各大IT公司研发类笔试题

不可以看见的部分请见http://www.iteblog.com/archives/262 一、百度(武汉地区) 第一部分&#xff1a; 1、描述数据库的简单操作。 2、描述TCP\IP四层模型&#xff0c;并简述之。 3、描述MVC的内容。 第二部分&#xff1a; 1、给出a-z0-9&#xff0c;在其中选择三个字符组…