安卓应用跳转回流的统一和复用

8a552f4643c4cb075b47aa5ca8c3222d.jpeg

b2475376326c8f521a9d4eb607c7b122.gif

本文字数:6799

预计阅读时间:35分钟

作为一个功能复杂的应用,无法避免地需要支持众多路径的回流,比如从Launcher、从Push通知、从端外H5、从合作第三方App以及从系统资源分享组件等。

我们知道,不同的回流路径会通过App的不同入口,带着不同的参数打开应用。而应用需要根据不同的回流路径,及其参数要求,跳转到目标页面,并完成完成相应的操作。在跳转到目标页面时,回流过程往往会被启动页、登入页、新手引导、升级、主页等条件检测和页面中断,导致无法顺利地完成目标页面的跳转和相应的操作。

整个回流过程如果不统一设计,代码会因为涉及的回流入口多,回流操作多,回流中断多,以及业务需求地不断增加和变更,变得复杂且高耦合。

所以,接下来我们就从端外回流过程面临的4个问题,来一步步进行方案设计:

  1. 如何解决所有回流入口传入的数据,放到同一个处理入口,方便后续回流的统一管理?

  2. 如何约定一套回流协议,让所有回流入口的相似需求,可以复用页面跳转和控制实现?

  3. 如何支持回流过程中的条件检测,以及检测中断后的回流恢复?

  4. 如何融合回流的复用设计和中断恢复设计?

01

回流入口统一

常见业务回流场景

为了更好地理解应用的端外回流的具体业务场景,我用社交应用的回流场景为例说明一下:

52b547a57416a29c10b3cc6d53f7e3d6.jpeg

从图中我们可以大致了解到几个业务常见的回流流程:

  1. 用户从桌面Launcher打开应用,经过Splash页,最后进入应用MainPage主页;

  2. 用户通过端外H5页,下载安装,并打开App。通过启动页后,先登入,再进行个人信息设置、喜好设置、推荐好友、应用MainPage页,最后进入用户主页,或H5活动页等;

  3. 用户通过点击系统通知栏的Push通知,打开应用并跳转到Push指定页,如,用户主页、H5活动页、Feed详情页等;

  4. 用户浏览器中的端外H5分享页,直接进入到应用内的用户主页,Feed详情页等;

  5. 用户从第三方App,通过分享ShareSDK,分享Url和图片到应用中,进入到Feed发布页;

  6. 用户从系统分享组件中,分享URL网页到Feed发布页。

统一回流处理入口

上面举例的这么多回流入口,能不能都统一到一个入口去处理呢?这是我们统一回流方案首先要解决的问题。

除了系统分享组件回流入口外,其它回流入口,只要和各合作方协商约定好,都是可以通过 sohuhy://xxx 形式的URL Scheme 请求,跳转到App里的。这样,我们可以将所有回流入口都通过这种形式统一到一个Acitivity去处理,比如我们把这个唯一入口命名为ActionActivity,那我们的入口应该像这段代码显示的这样去处理:

<activity android:name=".ActionActivity"><intent-filter> <action android:name="android.intent.action.SEND" /><category android:name="android.intent.category.DEFAULT" /><data android:mimeType="text/plain" /></intent-filter><intent-filter><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.BROWSABLE" /><dataandroid:host="*"android:scheme="http" /><dataandroid:host="*"android:scheme="https" /></intent-filter><intent-filter><action android:name="android.intent.action.VIEW" /><category android:name="android.intent.category.BROWSABLE" /><category android:name="android.intent.category.DEFAULT" /><data android:scheme="sohuhy" /></intent-filter>
</activity>

你可以看到,通过ActionActivity,我们能接受系统分享组件入口过来的分享,也可以接受给浏览器发送的URL网页分享,还能支持所有通过sohuhy这个Scheme启动的隐式调用。

我们的Push通知,端外H5,集成ShareSDK的三方应用,都可以通过改用Scheme隐式调用的方式回流到应用里。按照上面的代码配置后,ActionActivity就可以统一处理所有回流入口的数据了,统一处理应该像这段代码显示的这样去处理:

public class ActionActivity extends BaseActivity {private void dispatchIntent() {mIntent = getIntent();//获取入口数据if (Intent.ACTION_SEND.equals(mIntent.getAction()))  {matchShare();//如果数据来自Send入口,则进入分享处理逻辑} else {String scheme = mIntent.getData().getScheme();//如果数据来自View入口,则区分scheme进行处理if (scheme != null && scheme.startsWith("http"))  {matchShare();//如果数据来自http网页分享,则进入分享处理逻辑} else {matchProtocol();//如果数据来自sohuhy内部定义回流,则进入内部处理逻辑}   }}
}

02

回流复用

统一回流入口后,我们就可以对回流进行统一管理了。回流下一个需要解决的问题就是:不同的回流入口有很多非常相似的页面跳转和操作需求,如果各自实现会导致大量代码冗余,耦合高,容易互相影响。所以我们继续来考虑如何设计回流的复用。

回流处理的相似性

为了更好理解不同回流入口中相似页面跳转和操作场景,我用社交应用中内容分享的场景举例说明:

01e2f732dd2ca2a0804234be126ede87.jpeg

用户通过系统分享控件和浏览器入口,最终都需要把网页分享到应用中。而通过ShareSDK进行分享的第三方应用,也需要把网页、图片分享到应用中。那这三个分享场景的回流非常相似,我们就应该要考虑复用设计了。

还有在活动场景回流中,我们通过Push通知、端外H5页面、端内广告、端内运营私信,都有进入到特定活动页、用户主页、Feed详情页等的需求。

那么这些相似的页面跳转以及跳转后的操作逻辑,应该如何考虑复用呢?

我们可以在方法级别复用,但更好的方法是在协议级别复用,通过约定一套回流协议,在回流的入口处,就开始解耦入口,并完全复用各种跳转和控制逻辑。

在统一回流处理入口这个阶段,我们已经把大多数回流入口,统一成通过 sohuhy://xxx 形式的URL Scheme 请求,跳转到App里,所以,我们可以约定所有回流协议头为"sohuhy",然后将其它系统回流入口的数据,也转换为回流协议的格式,再由回流协议模块进行统一处理。

回流协议定义

而关于回流协议定义,我们遵守了标准的URL格式规范,格式是这样的:

scheme://host/path?param1=1&param2=2

使用熟悉的协议规范,可以减少团队学习和使用成本。URL格式作为回流协议,我们需要做一些内部约定,比如这张表格中列出的:

550d02b26c47ffafb26c43ee65c79f6b.jpeg

有了协议规范,我们就可以根据业务需求,定义功能path和params了。我们以唤起应用、打开H5页和分享举例,就可以设计这样的协议:

91485cdd2062d4062fab0cf8ec16c1ac.jpeg

你可以看到, *type=1代表的是,在type=1时,这个字段是必选字段,而type!=1时,这个字段是不需要的。你也能够看到,功能可以通过path信息进行很好的区分,mainPage,browser,shareToNative的命名也能很好表达回流的功能。

唤起客户端,属于最基本的回流功能,只需要知道scheme和host,不需要path。对于简单的页面跳转,如跳转到主页,只需要定义path,不需要定义任何参数。

对于其它复杂的回流功能,就需要根据业务需求,定义相应的参数键、参数类型、参数是否必选,以及参数枚举值等。

比如说内容分享shareToNative协议,分享支持的类型有两种,这两种类型需要的相应参数是不同的。所有我们定义参数type的枚举值:1-网页,2-图片,同时通过*type=1的方式,约定网页标题和URL是网页类型的必选字段。

对于通过系统Send控件分享过来的网页和图片,及浏览器分享过来的网页,我们可以在回流入口ActionActivity的matchShare()方法里,将参数映射到shareToNative协议,然后在通过shareToNative协议的实现模块对内容分享进行统一处理。

回流协议实现

协议定义好了,接下来我们一起来看回流协议如何实现。首先我们的目标是低耦合设计,不同协议的处理是完全隔离的,采用Dispatch派发模式来实现设计需求。

首先我们定义协议和处理协议的分发配置类ProtocolConfig:

public class BaseProtocolDispatcher implements Serializable {//所有协议处理类的基类,public void execute() {}
}public class ProtocolConfig { // 所有协议处理类的基类,public static final String SCHEME = "sohuhy";//协议schemepublic static final String HOST = "w.sohu.com";//协议host//协议pathpublic static final String ACTION_MAINPAGE = "mainPage";public static final String ACTION_BROWSER = "browser";public static final String ACTION_SHARE_TO_NATIVE = "shareToNative";......private static final Map<String, Class> ACTION_MAP_NEW = new HashMap<>();static {//协议-协议派发器 Map初始化ACTION_MAP_NEW.put(ACTION_MAINPAGE, OpenMainPageDispatcher
.class);ACTION_MAP_NEW.put(ACTION_BROWSER, OpenBrowserDispatcher.class);ACTION_MAP_NEW.put(ACTION_SHARE_TO_NATIVE, ShareFeedDispatcher
.class);......}public static Map.Entry<String, Class> getMatchResult(String url)         { //根据协议url 获取 协议派发器......Uri uri = Uri.parse(url);String scheme = uri.getScheme();//根据协议url 解释出schemeif (!SCHEME.equals(scheme)) {return null;}String host = uri.getHost();//根据协议url 解释出hostif (!HOST.equals(host)) {return null;}List<String> pathList = uri.getPathSegments();if (pathList != null && pathList.size() == 1) {path = pathList.get(0);//根据协议url 解释出path, 只支持一级path}     ......for (Map.Entry<String, Class> entry : ACTION_MAP_NEW.entrySet()) {try {String key = entry.getKey();if (path.equals(key)) return entry;//遍历Map,返回对应派发器} catch (Exception e) {e.printStackTrace();continue;}}return null;
}

所有回流协议处理类继承自BaseProtocolDispatcher,可以通过excute方法处理回流。每个回流协议都会被分配给一个相应的Dispatcher处理,比如shareToNative协议会被分派给ShareFeedDispatcher类进行处理。通过静态变量ACTION_MAP_NEW保证协议配置独一份,通过getMatchResult可以根据协议url获取到相应的协议处理类。

接下来我们来实现协议到处理类的转换过程:

public class ProtocolExecutor {//回流协议分发给协议处理类public static boolean execute(String protocolUrl) {try {final Map.Entry<String, Class> result = ProtocolConfig.getMatchResult(protocolUrl);//获取匹配的协议处理类if (result != null) {final Class clazz = result.getValue();ProtocolBaseDispatcher dispatcher = null;try {dispatcher = (ProtocolBaseDispatcher) clazz.newInstance();//通过反射得到协议处理对象} catch (Exception e) {}Uri uri = Uri.parse(protocolUrl);fillFields(dispatcher, clazz, uri);//通过反射将协议参数填充到对应变量中dispatcher.execute();//执行协议处理类return true;} else {//对于不识别协议path,说明是新增的回流协议,做升级提示//对于空协议path,应用已经被系统唤起,不需要额外处理,直接退出return true;}} catch (Exception e) {e.printStackTrace();}return false;}
}// OpenBrowser协议对应的处理实现类
public class OpenBrowserDispatcher extends BaseProtocolDispatcher {public String url;//会通过反射将协议参数赋值到实例对象中public int fullScreen;//会通过反射将协议参数赋值到实例对象中@Overridepublic void execute() {//协议处理实现方法super.execute(url);//通过WebView打开端外H5页面ActivityModel.toNewWebViewActivity(mContext, url, fullScreen);}
}//ActionActivity统一入口中调用协议处理模块
public class ActionActivity extends BaseActivity {private void matchProtocol()() {mProtocolUrl = mIntent.getData();//获取协议串ProtocolExecutor.excute(mProtocolUrl.toString());//协议处理}
}

协议派发中,我们主要处理了这几个事:

  1. 通过ProtocolConfig查找对应的协议处理类,并通过反射实例化对象;

  2. 通过反射,将协议中参数值赋值到协议处理对象对应的变量中;

  3. 调用处理类的处理流程,处理类根据协议要求执行相应操作,如跳转到新的H5活动页,进行内容分享、打开用户主页进行关注等。

通过将协议protocolUrl,映射到处理类及其成员变量,就很好地实现了回流协议的分发,达到了我们低耦合高内聚的协议隔离,和回流复用的设计目标。

03

回流中段恢复

回流的条件检测场景

在应用的回流过程中往往需要进行一些条件检测,比如,需要强制登入,需要强制升级等场景。为了让你更好地理解回流条件检测具体是个什么样的业务,我用社交应用的回流中断场景举例说明。

8313b905663b6c8bdf29b8ef3775199c.jpeg

你可以看到,端外回流进入到App的用户主页,需要经过登入、用户信息设置、兴趣选择、推荐用户、升级检测这5个条件检测。条件检测通过后会直接进入下一个条件检测,否则会中断检测进入相应条件设置界面。

回流的中断恢复设计

我们观察上面的回流条件检测场景,可以发现这种流线式的条件检测,特别像一个节点链。我们可以采用责任链的模式进行处理。也就是说每个节点负责一个条件的检测,只有上一个条件检测通过后才会继续下一个条件检测。

我们用Checker来表示一个检测点,可以按如下代码进行处理:

public class BaseChecker implements Parcelable {//检测节点基础类//执行回流条件检测,子类需要重写此类protected void dispatch() {}//Checker检测执行入口public void execute(){dispatch();}
}public class UpgradeChecker extends BaseChecker{//升级检测节点@Overrideprotected void dispatch() {if (UpGradeManager.getInstance().shouldShowUpdate()) {//如果用户需要强制升级,则跳转到升级页面UpGradeManager.getInstance().update(mContext, info.mData);} else {//否则,则进入最后一个检测节点BaseCheckersuper.dispatch();}}
}public class RecommendUserChecker extends UpgradeChecker{//推荐检测节点@Overrideprotected void dispatch() {int guidPage = SPUtil.getInstance().getInt(GuideStep.getGuideKey(), GuideStep.GUIDE_UNDEFINE);if (guidPage > GuideStep.GUIDE_CLOUDTAG) {//如果用户已经完成推荐关注,则进入下一个检测节点UpgradeCheckersuper.dispatch();} else {// 否则,跳转到推荐关注页ActivityModel.toRecommendUserActivity(params);}}
}public class CloudTagChecker extends RecommendChecker{//兴趣标签检测节点@Overrideprotected void dispatch() {int guidPage = SPUtil.getInstance().getInt(GuideStep.getGuideKey(), GuideStep.GUIDE_UNDEFINE);if (guidPage > GuideStep.GUIDE_CLOUDTAG) {//如果用户已经完成标签选择,则进入下一个检测节点RecommendCheckersuper.dispatch();} else {// 否则,跳转到标签选择页ActivityModel.toCloudTagActivity(params);}}
}public class NewUserGuideChecker extends CloudTagChecker{//新用户资料检测节点@Overrideprotected void dispatch() {int guidPage = SPUtil.getInstance().getInt(GuideStep.getGuideKey(), GuideStep.GUIDE_UNDEFINE);if (guidPage > GuideStep.GUIDE_NEWUSERGUIDE) {//如果用户已经完成资料设置,则进入下一个检测节点CloudTagCheckersuper.dispatch();} else {//否则,跳转到资料设置页ActivityModel.toNewUserGuideActivity(params);}}
}public class LoginChecker extends NewUserGuideChecker {//登入检测节点@Overrideprotected void dispatch() {if (UserModel.getInstance().isLogin()) {//如果用户已经登入,则进入下一个检测节点NewUserGuideCheckersuper.dispatch();} else {// 否则,跳转到登入界面ActivityModel.toLoginActivity(params);}}
}public class SplashChecker extends LoginChecker{//启动页检测节点@Overrideprotected void dispatch() {if (!HyApp.getIsAppShow()) {//如果应用没有显示,先展示启动页ActivityModel.toSplashActivity(params);}else{//否则,继续下一个检测节点LoginChecker的条件检测super.dispatch();}}
}public class ActionActivity extends BaseActivity{//回流入口@Overrideprotected void matchProtocol() {//启动第一个检测点SplashCheckernew SplashChecker().execute();}
}

这个方案通过继承的方式实现了条件检测链。如果条件符合,就通过super.dispatch()进入下一个条件检测;如果条件不符合,就跳转到对应页面,引导用户操作。

在条件检测链中某节点条件检测不满足时,会跳转到别的页面引导用户操作,这就会导致回流中断。当用户操作完,节点条件检测通过后,应该要恢复原来的条件检测流程。

这种情况,涉及到界面之间的跳转和参数传递,考虑到Activity不是应用内部实例化的,我们可以通过加一层特殊功能Activity基类来进行条件检测中断点的保存,和回流协议的保存和恢复。

我们以SplashChecker、LoginChecker为例,中断点的保存和恢复,可以像这段代码显示的这样去处理:

public class CheckerActivity extends BaseActivity{//条件检测界面基类private BaseChecker restoreChecker; //待回复检测点@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//存储待回复检测点restoreChecker = (BaseChecker)getIntent().getParcelableExtra(BaseChecker.KEY_CHECKER);}protected void restoreChecker() {//恢复下一个检测点if(restoreChecker != null) {restoreChecker.execute();return true;} return false;    }
}public class SplashActivity extends CheckerActivity{private void toNextActivity() {//Splash展示完成后,优先恢复检测点if(restoreChecker()){finish();} else {//没有需要恢复的检测点,直接进入到应用主页ActivityModel.toMainPageActivity(params);}}
}public class SplashChecker extends LoginChecker{@Overrideprotected void dispatch() {if (!HyApp.getIsAppShow()) {//开屏页导致流程中断时,把登入检测点传给Splash页面params.putParcelableExtra(BaseChecker.KEY_CHECKER, new LoginChecker.setUri(mProtocolUri));ActivityModel.toSplashActivity(params);}else{super.dispatch();}}public class LoginActivity extends CheckerActivity{private void loginSuccess() {//登入成功后,优先恢复下一个检测点if(restoreChecker()){finish();} else {//没有需要恢复的检测点,直接进入到应用主页ActivityModel.toMainPageActivity(params);}}
}public class LoginChecker extends NewUserGuideChecker {@Overrideprotected void dispatch() {if (UserModel.getInstance().isLogin()) {super.dispatch();} else {//登入导致流程中断时,把新用户资料检测点传给Login页面params.putParcelableExtra(BaseChecker.KEY_CHECKER, this)ActivityModel.toLoginActivity(params);}}
}

这里当Checker出现检测不通过中断检测流程时,我们会通过intent的方式,把当前检测点Checker传给中断页面。中断页面则通过父类CheckerActivity,自动存储检测点Checker的实例。

当中断页面完成业务流程后,会先调用restoreChecker看是否需要恢复条件检测链。如果需要,则直接销毁界面,后续流程交给Checker处理。否则,中断页面继续根据业务需求进行跳转。

回流中断恢复设计和复用设计的融合

回流时的条件检测都通过后,我们需要继续进行回流的目标页跳转和相应操作,这就需要我们把回流中断恢复设计和复用设计很好地融合起来。

我们可以利用中断检测的基类BaseChecker来保存回流协议,以及恢复协议执行,如下代码所示:

public class BaseChecker implements Parcelable {//检测节点基础类//用于Intent传递Checker的key定义public static final String KEY_CHECKER = "key_checker";//检测完后,需要恢复执行的回流协议protected Uri mProtocolUri;public Dispatcher setUri(Uri protocolUri) {//保存需要恢复执行的回流协议mProtocolUri = protocolUri;return this;}//BaseChecker作为最后一个节点, 用来负责恢复执行回流协议protected void dispatch() {String protocolUrl = mProtocolUri.toString();ProtocolExecutor.excute(protocolUrl);//衔接上回流复用设计}
}public class ActionActivity extends BaseActivity{//回流入口@Overrideprotected void matchProtocol() {//启动第一个检测点SplashChecker,并传递后续要恢复的回流协议mProtocolUri用于后续恢复new SplashChecker().setUri(mProtocolUri).execute();}
}

首先在ActionActivity回流入口处理中,把mProtocolUri传递给条件检测Checker,然后执行execute启动检测链。如果所有检测条件都检测通过,会调到BaseChecker中的dispatch方法,它则通过ProtocolExecutor.excute 恢复回流协议的后续流程。

完成条件检测链和中断回流恢复的设计,我们页就完成回流的整体方案设计。

04

总结

最后我们一起来总结一下整个回流方案的设计。

307ecb053caa036b59560952e36fad36.jpeg

图中灰色底块表示端外回流入口,绿色底块和线,表示有继承关系。整个设计的6个关键信息如下:

  1. 整个方案通过ActionActivity对端外回流入口的进行统一处理;

  2. 通过定义一套合理的跳转协议,为不同回流入口相似功能的复用,建立一个基石;

  3. 采用协议解释器ProtocolExecutor,和Dispatch派发模式实现不同协议的解耦处理;

  4. 当需要条件检测时,通过继承方式的责任链模式,串联众多的条件检测;

  5. 使用CheckerActivity作为各中断界面的基类,处理下一检测节点的保存与恢复;

  6. 检测链检测通过后,通过检测基类BaseChecker对回流协议进行存储和恢复,最后通过协议解释器ProtocolExecutor对回流协议进行分发和逻辑处理。

这个方案基本解决了回流过程中入口和处理分散、逻辑耦合高、冗余代码多的问题,它把整个回流阶段进行三段划分,不同阶段采用恰当的模式对逻辑进行解耦。

后续,我们又把Shortcut、DeepLink等跳转方式也融入到这一套回流方案中,让Shortcut和DeepLink也能快速拥有现有回流协议支持的所有功能。

除了端外回流,我们的端内H5,私信,消息,活动入口也都在复用这套回流方案,来实现H5和服务端对客户端页面跳转和操作的控制。端内跳转不需要考虑应用启动过程的条件检测Checker,可以直接调用协议解释器ProtocolExecutor。这减少了多端交互混乱繁杂的开发工作,也让回流方案发挥出更大的价值。

而且如果你的项目已经有很多旧业务的回流入口和处理逻辑,仍然可以考虑对它们进行兼容。通过将旧回流入口统一转发到这套新回流入口中,将不同的协议先转换为新回流协议,就达到了复用一套处理逻辑。

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

C3.【C++ Cont】名字空间、注释和变量

目录 1.回顾 2.名字空间(也称命名空间) 介绍 代码示例 3.注释 4.练习 B2003 输出第二个整数 方法1 方法2 1.回顾 在C1.【C Cont】准备中提到了名字空间(namespace)语句 using namespace std; 2.名字空间(也称命名空间) 介绍 1.处在在同一个空间内的,若有重名则会名…

常见自动化测试框架分层架构

作为一名专业的测试人员&#xff0c;搭建一个高级的自动化测试框架需要考虑多个因素。以下是一些步骤和指导&#xff0c;帮助你构建一个强大且灵活的自动化测试框架&#xff1a; 1. 理解框架的概念&#xff1a; - 首先&#xff0c;我们需要明确什么是“框架”。在自动化测试中…

103 - Lecture 2 Table and Data Part 1

SQL - Tables and Data Part 1 Relational Database Management System(RDBMS) 关系型数据库管理系统&#xff08;RDBMS&#xff09;是基于关系模型的数据库系统&#xff0c;它支持多种关系操作。关系模型是一种数据存储和检索的模型&#xff0c;它使用表格来组织数据&#x…

NestJS vs Fastify:Node.js框架的性能对决

在Node.js的世界中&#xff0c;框架的选择对于应用的性能和可维护性有着至关重要的影响。NestJS和Fastify是两个备受瞩目的框架&#xff0c;它们各自以其独特的优势在开发者社区中赢得了声誉。本文将深入探讨这两个框架的性能特点&#xff0c;并分析它们在不同场景下的适用性。…

【NOIP普及组】明明的随机数

【NOIP普及组】明明的随机数 C语言实现C实现Java实现Python实现 &#x1f490;The Begin&#x1f490;点点关注&#xff0c;收藏不迷路&#x1f490; 明明想在学校中请一些同学一起做一项问卷调查&#xff0c;为了实验的客观性&#xff0c;他先用计算机生成了N个1到1000之间的随…

python中t是什么意思

python中t是什么意思&#xff1f; python中t指的是“\r”&#xff1a;回车符&#xff0c;返回到这一行的开头&#xff0c;return的意思。 其他相关&#xff1a; \n&#xff1a;换行符&#xff0c;到下一行的同一位置&#xff0c;纵坐标相同&#xff0c;new line的意思。 \t…

OracleJDK与OpenJDK的区别(附带win11下多版本jdk安装)

OracleJDK与OpenJDK的区别&#xff08;附带win11下多版本jdk安装&#xff09; 在Java开发领域&#xff0c;OracleJDK与OpenJDK是两个常被提及的名词&#xff0c;它们都是Java开发工具包&#xff08;JDK&#xff09;的实现&#xff0c;但各自具有不同的特点和优势。在早期的jav…

代码随想录算法训练营第三十一天 | 56.合并区间 738.单调递增的数字 968.监控二叉树

LeetCode 56.合并区间&#xff1a; 文章链接 题目链接&#xff1a;56.合并区间 思路&#xff1a; ① 合并所有重叠的区间&#xff0c;合并后的区间数组不重叠&#xff0c;因此下面两种多区间重叠&#xff0c;其中的区间都要进行合并 ② 合并区间&#xff1a;因为情况2也算作…

[ComfyUI]FaceAging:太好玩啦!FaceAging终于装好了!负50到正100岁随心调整!超强又难装的节点安装教程来了! Comfyui教程

大家好&#xff01;今天我要向大家介绍一个超级有趣的话题——[ComfyUI]FaceAging&#xff01;这个工具能够让你轻松实现人脸年龄的调整&#xff0c;从负50岁到正100岁&#xff0c;让你的创作更加有趣和独特。 想象一下&#xff0c;你有一个强大的AI助手&#xff0c;它能够根据…

蓝桥杯真题——乐乐的序列和(C语言)

问题描述 乐乐在玩一个游戏&#xff0c;她有一排宝石&#xff0c;每个宝石上都刻有一个整数值。她的目标是从中挑选出一些宝石&#xff0c;使得选出的宝石数量为偶数&#xff0c;且这些宝石上的数字总和最大。如果不选任何宝石&#xff08;即选出宝石数量为 00&#xff0c;也是…

猫用宠物空气净化器哪个牌子好?求噪音小的宠物空气净化器推荐!

最近真是烦躁到了顶峰&#xff01;猫咪换毛季太折磨人了&#xff0c;白天上班累的要死&#xff0c;晚上回家还要和猫毛斗争。每天回家都是一场豪赌&#xff0c;需要花费的清理时间取决于家里的猫毛散落程度。有时候忙起来完全不想管&#xff0c;回到家只想躺着。 但最近身体出…

redis7学习笔记

文章目录 1. 简介1.1 功能介绍1.1.1 分布式缓存1.1.2 内存存储和持久化(RDBAOF)1.1.3 高可用架构搭配1.1.4 缓存穿透、击穿、雪崩1.1.5 分布式锁1.1.6 队列 1.2 数据类型StringListHashSetZSetGEOHyperLogLogBitmapBitfieldStream 2. 命令2.1 通用命令copydeldumpexistsexpire …

32位汇编——通用寄存器

通用寄存器 什么是寄存器呢&#xff1f; 计算机在三个地方可以存储数据&#xff0c;第一个是把数据存到CPU中&#xff0c;第二个把数据存到内存中&#xff0c;第三个把数据存到硬盘上。 那这个所谓的寄存器&#xff0c;就是CPU中用来存储数据的地方。那这个寄存器有多大呢&a…

1.1 OpenCV准备工作

介绍了如何在Windows系统中配置Python和Anaconda环境&#xff0c;并安装OpenCV库。首先从Python官网下载并安装Python&#xff0c;然后配置环境变量。接着安装Anaconda&#xff0c;并通过Anaconda Navigator或Prompt管理包。最后&#xff0c;在Anaconda Prompt中使用pip命令安装…

在gitlab,把新分支替换成master分支

1、备份master分支&#xff0c;可以打tag 2、删除master分支 正常情况下&#xff0c;master分支不允许删除&#xff0c;需要做两个操作才能删除 a、变更项目默认分支为非master分支&#xff0c;可以先随便选择 b、取消master为非保护分支 操作了上述两步&#xff0c;就可以删…

【专题】产业全球化视角下中国企业出海人才趋势洞察报告汇总PDF洞察(附原数据表)

原文链接&#xff1a;https://tecdat.cn/?p38107 在当今全球化进程不断加速的时代背景下&#xff0c;出海业务已成为众多企业拓展市场、实现可持续发展的重要战略方向。随着世界经济的紧密联系&#xff0c;不同国家和地区的市场机遇与挑战并存。文末202份出海行业研究报告最新…

uniapp vue3 使用echarts-gl 绘画3d图表

我自己翻遍了网上&#xff0c;以及插件市场&#xff0c;其实并没有uniapp 上使用echarts-gl的样例&#xff0c;大多数都是使用插件市场的echarts的插件 开始自己尝试直接用echartsgl 没有成功&#xff0c;后来尝试使用threejs 但是也遇到一些问题&#xff0c;最后我看官网的时…

世窗健康亮相第三届中国营养师发展大会——AI赋能营养健康 共建人类健康共同体

近日,为贯彻落实《“健康中国2030”规划纲要》,加强营养健康人才队伍建设,推动中国营养健康产业迈向高质量发展。由中国营养师发展大会组委会主办,全国各地营养师协会等多家机构共同发起的第三届中国营养师发展大会在石家庄市成功举办。作为深耕数字健康领域多年的综合服务运营…

基于 GADF+Swin-CNN-GAM 的高创新轴承故障诊断模型

往期精彩内容&#xff1a; Python-凯斯西储大学&#xff08;CWRU&#xff09;轴承数据解读与分类处理 Pytorch-LSTM轴承故障一维信号分类(一)-CSDN博客 Pytorch-CNN轴承故障一维信号分类(二)-CSDN博客 Pytorch-Transformer轴承故障一维信号分类(三)-CSDN博客 三十多个开源…

ubuntu20.04安装ros与rosdep

目录 前置配置 配置apt清华源 配置ros软件源 添加ros安装源&#xff08;中科大软件源&#xff09; 设置秘钥 更新源 ros安装 安装ros 初始化 rosdep 更新 rosdep 设置环境变量 安装 rosinstall 安装验证 启动海龟仿真器 操控海龟仿真器 rosdep安装更新 安装 使用…