目录

1.计算一段时间内的平均值

2.优化搜索联想功能

3.优化多次重复点击

4.RxJava + Retrofit + OkHttp

5.页面轮询请求

6.接口重新请求(retry)

7.本地验证输入有效性

8.优先加载本地缓存,再读取网络数据

方案一:concat

方案二:concatEager

方案三:merge

方案四:publish + merge

9.网络请求中发现token过期后刷新token并重新发起请求

1.Token缓存(SharedPreferences)

2.判断是否失效

3.Observable

4.retryWhen

5.map

6.TokenLoader

7.场景模拟

10.屏幕旋转导致Activity重建时恢复任务

1.Android屏幕旋转处理方式

(1)禁止旋转

(2)旋转后恢复现场(官方推荐)

(3)手工处理旋转(官方不推荐)

2.RxJava实现Activity重建时恢复任务

(1)定义一套Fragment与Activity通讯的接口

(2)定义任务Fragment:WorkerFragment

(3)模拟一个旋转屏幕需要销毁重建的Activity(实现Iholder接口)

11.网络重连自动请求

1.定位模块

2.网络状态模块

3.整合上述两个触发条件,并实现网络请求逻辑

12.模拟60秒发送验证码


1.计算一段时间内的平均值

应用场景:不定期产生一些数值,需要计算固定一段时间内的平均值

(1)Observable 模拟了按随机时间发送数据值的场景。

(2)buffer(200,TimeUnit.MILLISECONDS) 把一段时间(200s)发送的数据值收集起来,生成一个List发送给Consumer,Consumer收到后计算出平均值。

Observable.create(new ObservableOnSubscribe<Integer>() {@Overridepublic void subscribe(@NonNull ObservableEmitter<Integer> e) throws Throwable {e.onNext(1);Thread.sleep(50);e.onNext(2);Thread.sleep(90);e.onNext(3);Thread.sleep(60);e.onNext(4);Thread.sleep(70);}
}).buffer(200,TimeUnit.MILLISECONDS).subscribe(new Consumer<List<Integer>>() {@Overridepublic void accept(List<Integer> integers) throws Throwable {if(integers.size() <= 0) {return;}int result = 0;for(Integer i : integers) {result += i;}Log.i("avg","avg = " + result / integers.size());}
});

2.优化搜索联想功能

搜索联想:每当搜索框中输入的key发生变化,随即返回相应的搜索结果并展示。

优化项:

(1)连续输入引发的不必要请求。用户输入abc,App会连续发起三次关键字搜索request:a,ab,abc

(2)连续request,返回乱序。(1)场景下无法保证response按照a,ab,abc顺序返回,导致实际搜索结果错误。

解决方案:

按照上述场景,实际需要请求的是abc,即输入的最后结果key!

(1)debounce操作符,当输入框发生变化时,不会立刻将事件发送给下游,而是等待500ms,如果在这段事件内,输入框没有发生变化,那么才发送该事件;反之,则在收到新的关键词后,继续等待500ms。

(2)filter操作符,只有关键词的长度大于0时才发送事件给Observer。

(3)switchMap操作符,即便当ab、abc都触发request,即使ab的结果返回了,也不会发送给Observer,从而避免了出现前面介绍的搜索词和联想结果不匹配的问题。

RxTextView.textChanges(editText)// 表示延时多少秒后执行,当你敲完字之后停下来的半秒就会执行下面语句.debounce(500, TimeUnit.MILLISECONDS)// 只有关键词的长度大于0时才发送事件给Observer.filter(new Predicate<CharSequence>() {@Overridepublic boolean test(CharSequence charSequence) throws Exception {return charSequence != null && charSequence.length() > 0;}})// 数据转换 flatMap: 当同时多个数据请求访问的时候,前面的网络数据会覆盖后面的网络数据// 数据转换 switchMap: 当同时多个网络请求访问的时候,会以最后一个发送请求为准,前面网络数据会被最后一个覆盖.switchMap(new Function<CharSequence, ObservableSource<List<String>>>() {@Overridepublic ObservableSource<List<String>> apply(@NonNull CharSequence charSequence) throws Exception {// 网络请求操作,获取我们需要的数据List<String> list = new ArrayList<String>();list.add("2017");list.add("2018");return Observable.just(list);}}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe();

3.优化多次重复点击

场景:用户多次点击,造成重复请求。

RxView.clicks(button).throttleFirst(1, TimeUnit.SECONDS).subscribe(new Observer<Object>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(Object o) {Log.i("JAVA", "onClick");}@Overridepublic void onError(Throwable e) {}@Overridepublic void onComplete() {}});

4.RxJava + Retrofit + OkHttp

参见Retrofit相关资料

5.页面轮询请求

interval操作符:固定延时请求,每隔n秒请求一次。

Observable// 每间隔10s轮询一次.interval(10, TimeUnit.SECONDS)// 防止由于Activity已经销毁,但Observable依然在发送数据导致的内存泄漏.takeWhile(new Predicate<Long>() {@Overridepublic boolean test(Long aLong) throws Exception {// 用于中断interval的发送,当Activity在 onDestory / onPause等方法 设置 needRefresh 为 false时,Observable将停止发送数据return needReFresh;}}).subscribeOn(Schedulers.computation()).observeOn(Schedulers.io()).map(new Function<Long, String>() {@Overridepublic String apply(Long aLong) throws Exception {// 发送网络请求,获取结果return null;}}).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<String>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(String string) {// update UI}@Overridepublic void onError(Throwable e) {}@Overridepublic void onComplete() {}});

扩展:也可以用repeat,repeatWhen实现。

6.接口重新请求(retry)

应用场景:

1.规定重试次数

2.规定重试条件,什么条件下需要重试,什么条件下不需要重试,重试的时间、方式等

解决方案:

retryWhen + flatMap

// 用于在UI线程结束前,清空订阅对象,防止内存溢出final CompositeDisposable mCompositeDisposable = new CompositeDisposable();
final String ERROR_CODE1 = "error_code1";
final String ERROR_CODE2 = "error_code2";
final int apiMaxRetryCount = 4;
Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(@NonNull ObservableEmitter<String> e) throws Throwable {// doNetWorkString result = doNetWork();// 模拟失败if (null == result) {e.onError(new Throwable(ERROR_CODE1));} else {// 模拟成功e.onNext(result);e.onComplete();}}
}).retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {private int retryCount = -1;private long retryWaitingTime = 0;@Overridepublic ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Throwable {// 根据Throwable类型,实现不同的重试策略(是否重试,重试延时,重试次数)return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {@Overridepublic ObservableSource<?> apply(Throwable throwable) throws Throwable {// throwable为空属于异常情况,不做重试,直接结束if (null == throwable) {return Observable.error((throwable));}switch (throwable.getMessage()) {case ERROR_CODE1:// ERROR_CODE1,延时1000s后重试retryWaitingTime = 1000;break;case ERROR_CODE2:// ERROR_CODE2,延时2000s后重试retryWaitingTime = 2000;break;default:break;}retryCount++;// 控制重试次数小于允许的最大重试次数;否则返回error,调用Observer的onError,结束本次请求return retryWaitingTime > -1 && retryCount < apiMaxRetryCount ?Observable.timer(retryWaitingTime, TimeUnit.SECONDS): Observable.error(throwable);}});}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<String>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {mCompositeDisposable.add(d);}@Overridepublic void onNext(@NonNull String s) {// 接收正常请求结果,更新UI}@Overridepublic void onError(@NonNull Throwable e) {// 处理异常情况}@Overridepublic void onComplete() {}});

7.本地验证输入有效性

模拟场景:用户名、密码登录。长度要求分别为 2-8 和 4-16。如果两者都正确,“登录”恢复可点击,否则置灰不可点击

final CompositeDisposable compositeDisposable = new CompositeDisposable();PublishSubject<String> userNamePublisher = PublishSubject.create();
PublishSubject<String> userPasswordPublisher = PublishSubject.create();
userNameEdit.addTextChangedListener(new EditTextMonitor(userNamePublisher));
passwordEdit.addTextChangedListener(new EditTextMonitor(userPasswordPublisher));
Observable.combineLatest(userNamePublisher, userPasswordPublisher, new BiFunction<String, String, Boolean>() {@Overridepublic Boolean apply(String name, String password) {if (null == name || null == password) {return false;}return name.length() >= 2 && name.length() <= 8 && password.length() >= 4 && password.length() <= 16;}
}).subscribe(new Observer<Boolean>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {compositeDisposable.add(d);}@Overridepublic void onNext(@NonNull Boolean aBoolean) {// update UI}@Overridepublic void onError(@NonNull Throwable e) { }@Overridepublic void onComplete() { }
});

首先创建两个PublishSubject,用于对用户名、密码输入框的订阅,通过重写TextWatcher,实现在afterTextChanged(Editable editable) 触发时,使用PublishSubject把输入框结果值发送出去publishSubject.onNext(editable.toString().trim());

然后通过combineLatest操作符对两个PublishSubject进行组合,当任何一个PublishSubject发送数据时,都会回调代码中的apply方法,对两个PublishSubject最后一次发送的数据进行校验。

8.优先加载本地缓存,再读取网络数据

实现逻辑:同时发起读取缓存、访问网络的请求,如果缓存的数据先回来,那么就先展示缓存的数据,而如果网络的数据先回来,那么就不再展示缓存的数据。

定义读取缓存并发送结果的Observable:

private Observable<List<String>> getFromCache(final long needTime) {return Observable.create(new ObservableOnSubscribe<List<String>>() {@Overridepublic void subscribe(@NonNull ObservableEmitter<List<String>> emitter) {try {// get data from cacheLog.d("TAG", "开始加载本地缓存");// 模拟读取缓存时长Thread.sleep(needTime);List<String> cacheData = new ArrayList<>();emitter.onNext(cacheData);emitter.onComplete();Log.d("TAG", "结束加载本地缓存");} catch (Exception e) {emitter.onError(e);}}});
}

定义读取网络数据并发送结果的Observable

private Observable<List<String>> getFromNetwork(final long needTime) {return Observable.create(new ObservableOnSubscribe<List<String>>() {@Overridepublic void subscribe(@NonNull ObservableEmitter<List<String>> emitter) {try {// get data from networkLog.d("TAG", "开始请求网络数据");// 模拟网络请求时长Thread.sleep(needTime);List<String> networkData = new ArrayList<>();emitter.onNext(networkData);emitter.onComplete();Log.d("TAG", "结束请求网络数据");} catch (Exception e) {emitter.onError(e);}}});
}

定义获取最新数据并更新UI adapter的Observer

private List<String> data = new ArrayList<>();private DisposableObserver<List<String>> getArticleObserver() {return new DisposableObserver<List<String>>() {@Overridepublic void onNext(List<String> newsResultEntities) {Log.d("TAG", "展示数据");data.clear();data.addAll(newsResultEntities);mNewsAdapter.notifyDataSetChanged();}@Overridepublic void onError(Throwable throwable) {Log.d("TAG", "加载错误, e=" + throwable);}@Overridepublic void onComplete() {Log.d("TAG", "加载完成");}};
}

方案一:concat

Observable.concat (getFromCache(500).subscribeOn(Schedulers.io()),getFromNetwork(2000).subscribeOn(Schedulers.io())).observeOn(AndroidSchedulers.mainThread()).subscribe(getArticleObserver());

假设读取缓存需要500ms,获取网路数据需要2000ms,concat操作符标准地按序先获取缓存,再读取网络数据,总共耗时2500ms。执行逻辑清晰,但两种操作无法同时发起。

Log:

开始加载本地缓存

展示数据 – cache

结束加载本地缓存

开始请求网路数据

展示数据 – network

结束请求网络数据

加载完成

方案二:concatEager

List<Observable<List<String>>> observables = new ArrayList<>();observables.add(getFromCache(500).subscribeOn(Schedulers.io()));
observables.add(getFromNetwork(2000).subscribeOn(Schedulers.io()));
Observable.concatEager(observables).observeOn(AndroidSchedulers.mainThread()).subscribe(getArticleObserver());

与concat最大的不同就是多个Observable可以同时开始发送数据,如果后一个Observable发送完成后,前一个Observable还有发送完数据,那么它会将后一个Observable的数据先缓存起来,等到前一个Observable发送完毕后,才将缓存的数据发送出去。

Log:

开始加载本地缓存

开始请求网路数据

结束加载本地缓存

展示数据 – cache

结束请求网络数据

展示数据 – network

加载完成

如果把两个时间对调一下:读取缓存时间2000ms,网络请求时间500ms,会出现“network data”等待“cache data”完成展示后才能发送给Observer,因为最终会以网络数据为准,所以中间浪费了1500ms

Log:

开始加载本地缓存

开始请求网路数据

结束请求网络数据

结束加载本地缓存

展示数据 – cache

展示数据 – network

加载完成

方案三:merge

Observable.merge (getFromCache(500).subscribeOn(Schedulers.io()),getFromNetwork(2000).subscribeOn(Schedulers.io())).observeOn(AndroidSchedulers.mainThread()).subscribe(getArticleObserver());

merge操作符合并Observable集合后会产生乱序,当 读取缓存时间 > 网络请求时间,merge符合要求;但当 读取缓存时间 < 网络请求时间,界面会先展示网络数据,之后又被缓存数据刷了一遍。

方案四:publish + merge

getFromNetwork(2000).subscribeOn(Schedulers.io()).publish(new Function<Observable<List<String>>, ObservableSource<List<String>>>() {@Overridepublic ObservableSource<List<String>> apply(Observable<List<String>> network) {return Observable.merge(network, getFromCache(500).subscribeOn(Schedulers.io()).takeUntil(network));}}).observeOn(AndroidSchedulers.mainThread()).subscribe(getArticleObserver());

Log:网络请求时间(2000ms) > 读取缓存时间(500ms)

开始加载本地缓存

开始请求网路数据

展示数据 – cache

结束加载本地缓存

结束请求网络数据

展示数据 – network

加载完成

Log:网络请求时间(500ms) < 读取缓存时间(2000ms)

开始加载本地缓存

开始请求网路数据

展示数据 – network

结束请求网络数据

加载完成

扩展问题:

如果网络请求失败,返回error,将导致后面的缓存数据也无法加载的问题,针对这个问题,把获取网络数据的Observable做一下加工,让它主动屏蔽掉network所造成的error,或者把error进行业务逻辑转换,再通过onNext传递给Observer让页面展示提示语。

上述问题通过 onErrorResumeNext实现:

return Observable.create(new ObservableOnSubscribe<List<String>>() {@Overridepublic void subscribe(@NonNull ObservableEmitter<List<String>> emitter) {try {// get data from networkLog.d("TAG", "开始请求网络数据");// 模拟网络请求时长Thread.sleep(needTime);List<String> networkData = new ArrayList<>();networkData.add("network");emitter.onNext(networkData);emitter.onComplete();Log.d("TAG", "结束请求网络数据");// 异常情况emitter.onError(new Throwable("network error"));} catch (Exception e) {emitter.onError(e);}}
}).onErrorResumeNext(new Function<Throwable, ObservableSource<? extends List<String>>>() {@Overridepublic ObservableSource<? extends List<String>> apply(Throwable throwable) throws Exception {// 返回一个永远不发送事件的Observable;从而阻止network error传递给UI Observerreturn Observable.never();}
});

原理解析:

1.takeUntil解决了只要网络源数据开始发送,缓存就不再发送数据 的需求,针对concatEager在获取网络时间 < 读取缓存时间时,网络数据要等待缓存数据加载并显示完之后才能显示的问题。

2.merge的作用满足了两个操作同时进行的需求。

3.publish

上面代码中merge和takeUntil结合使用会发生两次订阅。

publish操作符完美解决了这个问题,它接收一个Function函数,该函数返回一个Observable,该Observable是对原Observable,即获取网络数据的Observable转换之后的结果,该Observable可以被takeUntil和merge操作符所共享,从而实现只订阅一次的效果。

9.网络请求中发现token过期后刷新token并重新发起请求

应用场景:

网络请求中,发现token失效,需要刷新token并重新请求接口。

特殊考虑:

同时有多个Request因Token失效而重复触发刷新Token的情况。如果当前有Request正在刷新Token,则其他Request只需等待Token刷新请求返回后,直接重新发起请求即可。

Token相关处理流程:

1.Token缓存(SharedPreferences)

// 缓存SharedManager.getInstance().setValue(SharedManager.KEY_TOKEN_VALUE, token);// 读取SharedManager.getInstance().getValue(SharedManager.KEY_TOKEN_VALUE, "0");

2.判断是否失效

Demo中token缓存的获取token的时间戳,默认值为0;Demo中token在获取2s后被判定失效。

private boolean isExpires() {String token = getTokenCached();return System.currentTimeMillis() > Long.parseLong(token) + 2000;
}

3.Observable

判定token是否有效,无效刷新token,有效正常执行请求,index模拟request id。

private void startRequest(final int index) {Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(@NonNull ObservableEmitter<String> emitter) {if (isExpires()) {// 无效:发送Throwable给retryWhen刷新tokenemitter.onError(new Throwable("Token is invalid"));} else {// 有效:发送token给map,正常执行请求emitter.onNext(index + ":" + getTokenCached() + "的用户信息");emitter.onComplete();}}})}

4.retryWhen

接收"Token is invalid"类型的Throwable,刷新token,并触发重订阅,使用最新Token重新执行请求。

Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(@NonNull ObservableEmitter<String> emitter) {// …..}
}).retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {@Overridepublic ObservableSource<?> apply(Observable<Throwable> throwableObservable) {return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {@Overridepublic ObservableSource<?> apply(Throwable throwable) {if ("Token is invalid".equals(throwable.getMessage())) {Log.d("TAG", index + ":" + "接收Throwable:" + throwable.getMessage() + " 刷新Token");return TokenLoader.getInstance().getNetTokenLocked();} else {// 过滤其他类型的error给下游return Observable.error(throwable);}}});}
})

5.map

模拟正常执行API Request,获取结果后刷新UI

private void startRequest(final int index) {Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(@NonNull ObservableEmitter<String> emitter) {// …..}}).retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {@Overridepublic ObservableSource<?> apply(Observable<Throwable> throwableObservable) {return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {@Overridepublic ObservableSource<?> apply(Throwable throwable) {// …..}});}}).subscribeOn(Schedulers.io()).observeOn(Schedulers.io()).map(new Function<String, Object>() {@Overridepublic Object apply(String s) {// 模拟执行 API Request,获取Response解析后返回return "Response Data";}}).observeOn(AndroidSchedulers.mainThread()).subscribe(new DisposableObserver<Object>() {@Overridepublic void onNext(@NonNull Object value) {Log.d("TAG", index + ":" + "收到信息=" + value);}@Overridepublic void onError(@NonNull Throwable e) {Log.d("TAG", index + ":" + "onError=" + e);}@Overridepublic void onComplete() {Log.d("TAG", index + ":" + "onComplete");}});
}

6.TokenLoader

TokenLoader采用AtomicBoolean来标记是否有刷新Token的请求正在执行,如果有,那么直接返回一个PublishSubject,否则就先发起一次刷新token的请求,并将PublishSubject作为该请求的订阅者。

AtomicBoolean保证当同时有多个Request因Token失效,只触发一次刷新Token,其他Request直接返回PublishSubject触发重订阅,但此时第一个请求正在刷新Token(进行中),所以后面所有的Request只能等待,等待第一个Request刷新Token完成后返回,一起触发重订阅。在doOnNext/doOnError中,我们将AtomicBoolean恢复标志位,同时缓存最新的token。

PublishSubject既作为刷新Token Observable的订阅者,同时又用于触发retryWhen的重订阅。

(1)Token刷新成功,发送onNext事件,触发重订阅

(2)Token刷新失败,发送onError事件,结束整个调用链

public static class TokenLoader {// 定义刷新Token的Observableprivate Observable<String> mTokenObservable;// getNetTokenLocked()最后返回一个Observable类型,用于触发retryWhen的重订阅private PublishSubject<String> mPublishSubject;// 保证当同时有多个Request因Token失效,只触发一次刷新Token操作,其他Request直接触发重订阅private AtomicBoolean mRefreshing = new AtomicBoolean(false);private TokenLoader() {mPublishSubject = PublishSubject.create();mTokenObservable = Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> e) throws Exception {// 模拟刷新Token的请求操作Thread.sleep(1000);Log.d("TAG", "发送Token");// Token取当前时间,返回给下游缓存e.onNext(String.valueOf(System.currentTimeMillis()));}}).doOnNext(new Consumer<String>() {@Overridepublic void accept(String token) {// 完成Token本地缓存Log.d("TAG", "存储Token=" + token);SharedManager.getInstance().setValue(SharedManager.KEY_TOKEN_VALUE, token);// 重置标志位mRefreshing.set(false);}}).doOnError(new Consumer<Throwable>() {@Overridepublic void accept(Throwable throwable) {// 重置标志位mRefreshing.set(false);}}).subscribeOn(Schedulers.io());}public static TokenLoader getInstance() {return Holder.INSTANCE;}private static class Holder {private static final TokenLoader INSTANCE = new TokenLoader();}public Observable<String> getNetTokenLocked() {// 第一次把AtomicBoolean标记为true,表示当前有token正在请求if (mRefreshing.compareAndSet(false, true)) {Log.d("TAG", "没有请求,发起一次新的Token请求");startTokenRequest();} else {Log.d("TAG", "已经有请求,直接返回等待");}return mPublishSubject;}private void startTokenRequest() {mTokenObservable.subscribe(mPublishSubject);}
}

7.场景模拟

模拟多个Request同时因token失效而触发刷新token的情况,在发起一个请求500ms之后,立刻发起另一个请求,当第二个请求决定是否要重订阅时,第一个请求正在进行刷新token的操作,这时候第二个请求只是等待而已。等第一个请求返回后,两个请求一起重订阅。

startRequest(0);try {Thread.sleep(500);
} catch (InterruptedException e) {e.printStackTrace();
}
startRequest(1);

Log:

// 第一个Request 0 发现Token失效,触发刷新Token操作

0:接收Throwable:Token is invalid 刷新Token

没有请求,发起一次新的Token请求

// 刷新Token时,第二个Request 1 发起请求,同时发现Token失效,试图触发刷新Token操作,这时候判断当前已经有一个请求在刷新Token,所以返回等待

1:接收Throwable:Token is invalid 刷新Token

已经有请求,直接返回等待

// 第一个Request 0 获取Token,并完成缓存

发送Token

存储Token=1589525718464

// 第一个Request 0 刷新Token完成,返回和Request1一起触发重订阅,两个Request重新发起

1:收到信息=Response Data

1:onComplete

0:收到信息=Response Data

0:onComplete

10.屏幕旋转导致Activity重建时恢复任务

1.Android屏幕旋转处理方式

针对Android屏幕旋转导致Activity销毁重建问题,处理方式有三种:

(1)禁止旋转

<activityandroid:name=".MyActivity"android:screenOrientation="portrait"android:label="@string/app_name" />

(2)旋转后恢复现场(官方推荐)

使用持久化 / 恢复现场的逻辑解决。

在onPause()里将用户数据保存到Preference, 如果Activity重建需要耗费大量资源或需要访问网络导致时间很长,可以实现onRetainNonConfigurationInstance()方法将所需数据先保存到一个对象里。

@Override
public Object onRetainCustomNonConfigurationInstance() {final MyDataObject data = cacheMyDataObject();return data;
}

在onCreate()取回

@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.main);final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance();if (data == null) {//表示不是由于Configuration改变触发的onCreate()data = loadDataFromNet();}//... 
}

(3)手工处理旋转(官方不推荐)

通过配置AndroidManifest.xml,指定Activity在屏幕旋转时不做重建。

<activityandroid:name=".MyActivity"android:configChanges="orientation|keyboardHidden"android:label="@string/app_name" />

上述设置可以让Activity在屏幕旋转时不做重建,而是触发onConfigurationChanged()方法,在这里可以获取到当前的屏幕方向以便做必要的更新。旋转后Activity当前的信息也不会丢失,任务不会被终止。

2.RxJava实现Activity重建时恢复任务

场景:

没有在AndroidManifest.xml中对android:configChanges进行特殊的声明。Activity因屏幕旋转重建时,有些和Activity生命周期没有绑定关系的任务,可以放在具有setRetainInstance属性设置的Fragment中。

Fragment. setRetainInstance方法:

Fragment设置该标志位,在上述场景下,Fragment实例会被保留,并在Activity销毁过程中,只调用Fragment的onDetach方法,而不会调用onDestroy方法;在Activity重建时,会调用Fragment的onAttach,onActivityCreated方法,而不会调用onCreate方法。

(1)定义一套Fragment与Activity通讯的接口

由Activity实现接口,Fragment持有接口句柄,用于Fragment向Activity传递一个ConnectableObservable,Fragment通过ConnectableObservable向Activity发送数据(比如任务执行进度)

public interface IHolder {void onWorkerPrepared(ConnectableObservable<String> worker);
}

(2)定义任务Fragment:WorkerFragment

定义成员变量

// 持有View的接口引用,调用接口方法把ConnectableObservable传递给View
private IHolder mHolder;
// 用于取消订阅
private Disposable mDisposable;
// 用于向Activity发送数据
private ConnectableObservable<String> mWorker;

onCreate

(1)设置标志位setRetainInstance(true);

(2)模拟一个独立(和view生命周期无关)任务,开始执行并同步执行进度给view。

// 设置标志位,当Activity重建时,本Fragment保留实例
setRetainInstance(true);
if (mWorker == null) {mWorker = Observable.create(new ObservableOnSubscribe<String>() {@Overridepublic void subscribe(ObservableEmitter<String> observableEmitter) throws Exception {for (int i = 0; i < 10; i++) {String message = "任务进度=" + i * 10 + "%";try {// 模拟任务执行Thread.sleep(1000);if (observableEmitter.isDisposed()) {break;}} catch (InterruptedException error) {if (!observableEmitter.isDisposed()) {observableEmitter.onError(error);}}observableEmitter.onNext(message);}observableEmitter.onComplete();}}).subscribeOn(Schedulers.io()).publish();mDisposable = mWorker.connect();
}

onAttach

对于设置setRetainInstance(true); 的Fragment,在Activity重建时,会调用Fragment的onAttach,onActivityCreated方法,而不会调用onCreate方法。onAttach中,将Activity的上下文转换成接口(IHolder)类型。

@Override
public void onAttach(Context context) {super.onAttach(context);if (context instanceof IHolder) {mHolder = (IHolder) context;}
}

onResume

把负责执行任务的ConnectableObservable通过接口传递给Activity

@Override
public void onResume() {super.onResume();if (mHolder != null) {mHolder.onWorkerPrepared(mWorker);}
}

onDestroy

销毁订阅

@Override
public void onDestroy() {super.onDestroy();mDisposable.dispose();
}

(3)模拟一个旋转屏幕需要销毁重建的Activity(实现Iholder接口)

onCreate

调用startWorker()开始执行任务。

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.layout);startWorker();mCompositeDisposable = new CompositeDisposable();
}

startWorker

考虑两个场景:(1)启动App,Activtiy被创建,开始执行任务;(2)屏幕旋转,Activity被销毁后重建,继续执行任务。注意(2)情况下不是重新执行任务,而是应该继续执行当前任务。

场景实现:如果存在Fragment实例(旋转屏幕,Activity重建情况下),不需要重新添加;否则创建并添加一个新的Fragment。

private void startWorker() {WorkerFragment worker = getWorkerFragment();if (worker == null) {addWorkerFragment();} else {Log.d(TAG, "WorkerFragment has attach");}
}
private void addWorkerFragment() {WorkerFragment workerFragment = new WorkerFragment();FragmentManager manager = getSupportFragmentManager();FragmentTransaction transaction = manager.beginTransaction();transaction.add(workerFragment, WorkerFragment.TAG);transaction.commit();
}

IHolder接口实现

当Activity销毁重建时,会调用已经存在的Fragment实例的onAttach方法,接着调用onResume方法,onAttach中,Fragment实例获取了重建的Activity IHolder类型的句柄,onResume方法中,Fragment实例调用了onWorkerPrepared方法,把执行任务的Observable同步给重建的Activity。

在Activity中,为ConnectableObservable订阅了观察者,用于接收进度信息并显示在页面上。

@Override
public void onWorkerPrepared(ConnectableObservable<String> worker) {worker.observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<String>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {mCompositeDisposable.add(d);}@Overridepublic void onNext(@NonNull String message) {mTvResult.setText(message);}@Overridepublic void onError(@NonNull Throwable e) {onWorkerFinished();mTvResult.setText("任务错误");}@Overridepublic void onComplete() {onWorkerFinished();mTvResult.setText("任务完成");}});
}

finishWorker

任务结束,移除Fragment。

private void onWorkerFinished() {removeWorkerFragment();
}
private void removeWorkerFragment() {WorkerFragment workerFragment = getWorkerFragment();if (workerFragment != null) {FragmentManager manager = getSupportFragmentManager();FragmentTransaction transaction = manager.beginTransaction();transaction.remove(workerFragment);transaction.commit();}
}

onDestroy

记得解除订阅,防止内存泄漏。

@Override
protected void onDestroy() {super.onDestroy();mCompositeDisposable.clear();
}

11.网络重连自动请求

应用场景:模拟天气预报请求场景。

1.定位模块

起后台线程,每隔一段时间获取一次定位(城市代码)的结果,并发送结果通知。

(1)interval + takeWhile:每隔5s有条件地循环执行一次

(2)distinctUntilChanged:过滤连续的重复数据,即连续两次定位(城市代码)信息一样,则过滤掉重复信息,避免后面接口的重复请求。

(3)doOnNext:当位置(城市代码)变更,向下通知之前,先做本地缓存

// 每隔5s获取一次位置信息,直到停止天气服务(stopService = true)
return Observable.interval(5000, TimeUnit.MILLISECONDS).takeWhile(new Predicate<Long>() {@Overridepublic boolean test(Long aLong) throws Throwable {return !stopService;}}).subscribeOn(Schedulers.computation()).observeOn(Schedulers.io()).map(new Function<Long, Long>() {@Overridepublic Long apply(Long aLong) {Log.d("TAG", "重新定位");// 模拟获取位置信息过程 ...Log.d("TAG", "定位到城市信息=" + cityCode);return cityCode;}})// 过滤掉连续相同的数据,如果连续两次定位位置相同,不重复请求.distinctUntilChanged()// 发送位置信息通知之前,把位置信息缓存到本地.doOnNext(new Consumer<Long>() {@Overridepublic void accept(Long s) {saveCacheCity(s);}}).subscribeOn(Schedulers.io());

2.网络状态模块

监听网络状态变化,当断网后重连接时,发送通知给下游,以便重新请求网络数据。

(1)PublishSubject:注册网络状态监听器,当接收器接收到网络状态变更事件,由PublishSubject发送当前网路状态(是否可用)给下游。

(2)filter:PublishSubject发送数据前,对数据加过滤,只发送网络已连接事件(isConnect == true),同时只有当前已经获取到位置信息时才发送。

(3)map:将网络状态变化事件,最终转换成获取的本地缓存的城市代码,并发送给下游,请下游组织向服务器请求天气数据。

// 用于发送网络状态变更事件
private PublishSubject<Boolean> mNetStatusPublish = new PublishSubject<>();
// 网络状态Observable由网络状态变更事件触发,最后返回缓存的位置信息,下游将用位置信息重新请求天气数据
private Observable<Long> getNetStatusObservable() {return mNetStatusPublish.filter(new Predicate<Boolean>() {@Overridepublic boolean test(Boolean isConnect) {// 只发送网络已连接事件(isConnect == true),同时只有当前已经获取到位置信息时才发送return isConnect && getCacheCity() > 0;}}).map(new Function<Boolean, Long>() {@Overridepublic Long apply(Boolean aBoolean) {// 当网络重连成功后,发送缓存的位置信息给下游return getCacheCity();}).subscribeOn(Schedulers.io());
}
// 注册网络状态变更广播接收器,当发生变更时,判断是否重连成功,并把结果通知给下游
private void registerBroadcast() {BroadcastReceiver mReceiver = new BroadcastReceiver() {@Overridepublic void onReceive(Context context, Intent intent) {if (mNetStatusPublish != null) {mNetStatusPublish.onNext(isNetworkConnected(context));}}};IntentFilter filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION);registerReceiver(mReceiver, filter);
}

3.整合上述两个触发条件,并实现网络请求逻辑

(1)定义API接口

public interface IWeatherApi {Observable<WeatherEntity> doRequestWeather(long cityId);
}

(2)网络请求

merge操作符:整合两个数据源

retryWhen操作符:出错重试

Observable.merge(getLocationObservable(), getNetStatusObservable()).flatMap(new Function<Long, ObservableSource<WeatherEntity>>() {@Overridepublic ObservableSource<WeatherEntity> apply(Long cityCode) throws Exception {Log.d("TAG", "尝试请求天气信息=" + cityCode);return doRequestWeather(cityCode).subscribeOn(Schedulers.io());}}).subscribeOn(Schedulers.io()).retryWhen(new Function<Observable<Throwable>, ObservableSource<?>>() {@Overridepublic ObservableSource<?> apply(Observable<Throwable> throwableObservable) throws Exception {return throwableObservable.flatMap(new Function<Throwable, ObservableSource<?>>() {@Overridepublic ObservableSource<?> apply(Throwable throwable) throws Exception {Log.d("TAG", "请求天气信息过程中发生错误,进行重订阅");return Observable.just(0);}});}}).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer());

12.模拟60秒发送验证码

public void onCodeClick() {Observable.intervalRange(0, 60, 0, 1, TimeUnit.SECONDS).map(new Function<Long, Long>() {@Overridepublic Long apply(@NonNull Long aLong) throws Exception {return 60 - aLong; // 设置60秒,由于是倒计时,需要将倒计时的数字反过来}}).observeOn(AndroidSchedulers.mainThread()).doOnSubscribe(new Consumer<Disposable>() {@Overridepublic void accept(@NonNull Disposable disposable) throws Exception {button.setEnabled(false);button.setTextColor(Color.GRAY);}}).subscribeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Long>() {@Overridepublic void onSubscribe(Disposable d) {}@Overridepublic void onNext(Long aLong) {button.setText(aLong + "秒后重发");}@Overridepublic void onError(Throwable e) {}@Overridepublic void onComplete() {button.setEnabled(true);button.setTextColor(Color.RED);button.setText("发送验证码");}});
}

 

 

参考链接:https://www.jianshu.com/p/c935d0860186

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

相关文章

  1. openstack理论,面试常用

    openstack组件 nova 用于在计算级别管理虚拟机,并在计算或管理程序级别执行其他计算任务 neutron 为虚拟机,计算和控制节点管理网络功能 keystone 为所云用户和opensatck云服务提供身份认证服务 Horizon 用于提供图形用户界面 cinder 用于提供块存储功能,通常来说来共同为控…...

    2024/4/16 21:14:59
  2. Docker weave 安装-容器跨主机访问-简洁的步骤

    简介 weave是docker容器跨主机访问的其中一种方案,weave的优势是简单使用、报文自动加密、可穿透防火墙。 官方地址:https://www.weave.works/oss/net/ 本文安装环境以centos7为例,假设有两个主机,ip分别是192.168.0.10和192.168.0.11 开始安装 192.168.0.10 主机安装 wget…...

    2024/4/15 7:21:41
  3. 干货 汽车金融如何进行贷后管理?

    在车贷的风险控制中,很多人比较熟悉贷前审核,认为把控好贷前审核即是风险可控,其实不然,从车贷风险管理整个生命周期来看,优质的车贷金融风控不仅需要做好贷前审核,贷后管理也是相当重要的。 贷后管理基本分5个部分: 1、档案管理 2、保险管理 3、客服管理 4、解除抵押 5…...

    2024/4/16 21:13:41
  4. bc: Tendermint: 共识概览

    fix pic of https://blog.csdn.net/simple_the_best/article/details/77198837 content: Tendermint 是一个易于理解,大部分操作为异步的 BFT 共识协议。下图是一个简单的状态机,它展示了协议遵循的规则:协议中的参与者叫着 “验证人”(validator)。他们轮流对交易区块进行…...

    2024/4/15 7:21:38
  5. 为什么要前后端分离?有什么优缺点?

    一、前戏前后端分离已成为互联网项目开发的业界标准使用方式,通过nginx+tomcat的方式(也可以中间加一个nodejs)有效的进行解耦,并且前后端分离会为以后的大型分布式架构、弹性计算架构、微服务架构、多端化服务(多种客户端,例如:浏览器,车载终端,安卓,IOS等等)打下坚…...

    2024/4/24 7:00:48
  6. LoRa有应用场景吗?

    不同的技术特征会带来不同的应用场景,LoRa最大的价值点在于,易部署与自主性,总体来说,LoRa更适合企业用户对自主性、快速性要求高,对连续覆盖、深度覆盖要求高的场景,如园区、工厂、厂矿、农场、物流集散地、综合体、人居社区等环境。“能够生存下来的物种,并不是那些最…...

    2024/4/24 7:00:44
  7. 【整理之路二】百度地图的路径规划和调用本机地图导航

    推荐看完之后注意一下最后的东西 一、细说百度地图的路径规划 路径规划主要有这么几种 1.公交路径规划 1.1 市内公交规划(暂时不在这里说) 1.2 跨市/省公交规划// 导入头文件#import <BaiduMapAPI_Search/BMKSearchComponent.h>#import <BaiduMapAPI_Map/BMKPolylin…...

    2024/4/24 7:00:43
  8. 自定义一个回到顶部Vue指令

    CSS样式: <style>* {margin: 0;padding: 0;}html, body, #app {height: 100%;}#app {display: flex;flex-direction: column;overflow: hidden;}section {flex: 1;overflow-y: auto;}li {height: 28px;line-height: 28px;border-bottom: 1px solid #000000;}header, foo…...

    2024/4/24 7:00:39
  9. 积分兑换商城运营时需注意的6大细节,你清楚吗?

    随着互联网的快速发展,积分兑换商城也变得越来越常见,很多企业也都开始逐渐意识到积分兑换商城在客户运营方面的重要性,但是知易行难,很多企业由于疏于管理、商品单一、售后不及时等问题,让本该是盘活客户的积分,逐渐成为“留也不是不留也不是”的烫手山芋,究其根本,还…...

    2024/4/24 7:00:38
  10. windows10环境下mysql8的安装与配置

    1.下载mysql8 下载地址: https://dev.mysql.com/downloads/mysql/8.0.html2.解压及配置 将下载的zip文件解压到想要安装的目录下编辑my.ini文件如下: [mysql] # 设置mysql客户端默认字符集 default-character-set=utf8 [mysqld] #设置3306端口 port = 3306 # 设置mysql的安装…...

    2024/4/24 7:00:37
  11. 斯坦福四足机器人运动学逆解(笔记4/作业4)

    一.运动学正/逆解概念 1.运动学正解:已知舵机/电机转角,求足端坐标。 2.运动学逆解:已知足端坐标,求舵机/电机转角。 二.足端轨迹规划 摆线方程: { x=r*(t-sint) y=r*(1-cost) [其中r为圆的半径,t是圆的半径所经过的弧度(滚动角),当t由0变到2π时,动点就画出了摆线的一支…...

    2024/4/24 7:00:42
  12. vuepress入门详解(四)vuepress 基本配置

    vuepress 基本配置 ::: tip 本页所列的选项仅对默认主题生效。如果你在使用一个自定义主题,选项可能会有不同。 ::: 首页配置 首页配置文件是doc目录下的README.md文件,以下是一个如何使用的例子:--- home: true heroImage: /hero.png heroText: Hero 标题 tagline: Hero 副…...

    2024/4/24 7:00:41
  13. Vue中把对象中的数据给了某个变量,改变一个对象的值,另一个对象也变化的解决方案

    在vue中的data数据中建立这两个对象 var salesOreder = new Vue({delimiters: [${, }], [想知道这里的作用解释请看这里](https://blog.csdn.net/qq_46124502/article/details/106326034)data(){return {objData1:{name:"zky"},objData2:""}}})此时把objD…...

    2024/4/24 7:00:40
  14. 039 老师分糖果

    例:幼儿园老师将糖果分成若干等份,让学生按任意次序领取,第1个领取的,得到1份加上剩余糖果的1/10;第2个领取的,得到2份加上剩余糖果的1/10;第3个领取的,得到3份加上剩余糖果的1/10;……依此类推。问共有多少个学生?老师共将糖果分成了多少等份? 分析:设一开始等分成…...

    2024/4/24 7:00:33
  15. Vue-11-生命周期

    一、知识点部分生命周期钩子 = 生命周期函数 = 生命周期事件 1.生命周期函数分为: 1.1创建期间: 1.1.1 beforeCreat 1.1.2 created 1.1.3 beforeMonute 1.1.4 monuted 1.2运行期间: 1.2.1beforeUpdate 1.2.2updated 1.3销毁期间: 1.31 beforeDestory 1.32 destoryed created阶…...

    2024/4/24 7:00:35
  16. 利用GISTIC2.0整合队列CNV拷贝数变异分析结果

    今天我们学习一个拷贝数变异的整合软件——GISTIC2。注意,这和软件本身并不做CNV calling,而是主要用于检测一组样品中显着扩增或缺失的基因组区域(明白一点说就是你需要提供一批样本中的每个样本的CNV检测结果,这个软件经过呼啦呼啦显著性计算会告诉你这一批样本中显著扩增…...

    2024/5/2 14:25:20
  17. element+vue+echarts 实现视图转换和图例大小自适应

    element+vue+echarts 实现视图转换和图例大小自适应本教程适合新手,大佬勿喷.第一次写博客,有不足之处,希望大家指出.示例图html由于要是视图大小自适应页面并随页面大小变化,所以一定要在页面上方写入样式 <head><title>通知列表</title><#include "…...

    2024/5/2 6:00:14
  18. 稀疏数组(压缩算法)详解

    稀疏数组(压缩算法)详解 稀疏数组逻辑框架 (该数组共有6行7列,有8个有效值)1.随机创建个二维数组 //1.创建一个二维数组11*11 int[][] array1 = new int[11][11];array1[1][2]=1; //设置第一行第二列的值为1array1[2][3]=5; //设置第二行第三列的值为52.输出原始的…...

    2024/4/16 21:14:59
  19. qml退出应用

    qml退出应用 Qt.quit() Qt.exit()...

    2024/4/16 21:15:05
  20. XCOPY艾高贝ab1史上最走心webpack4.0中级教程——配置之外你应该知道的事//华为云社区首发

    《webpack4.0各个击破系列》适合不满足于只会配置webpack但一时间又看不懂源码的中级读者。我没法保证这个系列是最好的,但至少能保证每一篇博文都跟那些Ctrl+C和Ctrl+V的博文有本质的区别,不信你读读看。一.webpack是什么中文版官方网址:www.webpackjs.comwebpack是前端最火…...

    2024/4/19 20:38:29

最新文章

  1. 计算机网络期末试题

    第一章 概述 一. 单选题&#xff08;共13题&#xff0c;36.4分&#xff09; 1. (单选题) 因特网起源于( &#xff09;网络。 A. ARPANETB. EthernetC. CATVD. CERNET 我的答案: A:ARPANET;正确答案: A:ARPANET; 2.8分 2. (单选题)人们把( &#xff09;年作为因特网的诞…...

    2024/5/4 10:18:39
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/3/20 10:50:27
  3. 持续交付工具Argo CD的部署使用

    Background CI/CD&#xff08;Continuous Integration/Continuous Deployment&#xff09;是一种软件开发流程&#xff0c;旨在通过自动化和持续集成的方式提高软件交付的效率和质量。它包括持续集成&#xff08;CI&#xff09;和持续部署&#xff08;CD&#xff09;两个主要阶…...

    2024/5/4 2:53:47
  4. 面试经典算法系列之双指针1 -- 合并两个有序数组

    面试经典算法题1 – 合并两个有序数组 LeetCode.88 公众号&#xff1a;阿Q技术站 问题描述 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 到 nums1 中&#…...

    2024/5/3 9:00:01
  5. CSS使用JS变量

    1. CSS变量 CSS 变量&#xff08;也称为自定义属性&#xff09;允许我们在 CSS 中定义可重复使用的值&#xff0c;并将其应用于不同的选择器。为了创建一个 CSS 变量&#xff0c;我们需要使用 -- 前缀&#xff0c;然后可以像常规属性一样使用它。 :root {--primary-color: bl…...

    2024/5/3 17:12:15
  6. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/1 17:30:59
  7. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/2 16:16:39
  8. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/4/29 2:29:43
  9. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/3 23:10:03
  10. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/4/27 17:58:04
  11. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/4/27 14:22:49
  12. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/4/28 1:28:33
  13. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/4/30 9:43:09
  14. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/4/27 17:59:30
  15. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/2 15:04:34
  16. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/4/28 1:34:08
  17. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/4/26 19:03:37
  18. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/4/29 20:46:55
  19. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/4/30 22:21:04
  20. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/1 4:32:01
  21. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/4 2:59:34
  22. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/4/28 5:48:52
  23. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/4/30 9:42:22
  24. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/2 9:07:46
  25. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/4/30 9:42:49
  26. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  27. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  28. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  29. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  30. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  31. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  32. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  33. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  34. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  35. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  36. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  37. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  38. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  39. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  40. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  41. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  42. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  43. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  44. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  45. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57