IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 只需一篇文章“吃透”Android多线程技术,移动端跨平台开发技术 -> 正文阅读

[移动开发]只需一篇文章“吃透”Android多线程技术,移动端跨平台开发技术

@Override
public void run() {
//do something
}
});
}

综上所述,HandlerThread最屌的地方就在于,只要你还有它的句柄,你可以随时拿到在该线程下创建的Looper对象,用于生成一个Handler。之后post的所有runnable都可以在该HandlerThread下运行。然而。。

在实际的开发中,我们好像很难找到这么一个需求,要在指定的一个线程下执行某些任务。注意了是指定的一个,不是一些(线程池)。唯一比Thread厉害的地方恐怕就是可以取消未执行的任务,减少内存泄漏的情况了吧。不过个人观点是线程池好像也可以做到。所以并没有察觉?HandlerThread有任何的优势。而且其实实现也很简单,我们可以随时手写一个简陋版的HandlerThread.

public static class DemoThread extends Thread{
private LinkedBlockingQueue queue = new LinkedBlockingQueue<>();

@Override
public void run() {
super.run();
while(true){
if(!queue.isEmpty()){
Runnable runnable;
synchronized (this){
runnable = queue.poll();
}
if(runnable!= null) {
runnable.run();
}
}
}
}

public synchronized void post(Runnable runnable){
queue.add(runnable);
}

public synchronized void clearAllMessage(){
queue.clear();
}

public synchronized void clearOneMessage(Runnable runnable){
for(Runnable runnable1 : queue){
if(runnable == runnable1){
queue.remove(runnable);
}
}
}
}

public void testDemoThread(){
DemoThread thread = new DemoThread();
thread.start();
//发一个消息
Runnable r = new Runnable() {
@Override
public void run() {
}
};
thread.post?;
//不想执行了。。。。删掉
thread.clearOneMessage?;
}

看分分钟完成HandlerThread能做到的一切。。。。是不是很简单。

3.直接使用AsyncTask.execute()

AsyncTask.execute(new Runnable() {
@Override
public void run() {
}
});

个人认为AsyncTask的设计暴露了这个 接口方法 谷歌做的非常不恰当。它这样允许开发者直接使用AsyncTask本身的线程池,我们可以看看源代码做验证

@MainThread
public static void execute(Runnable runnable) {
sDefaultExecutor.execute(runnable);
}

果不其然,execute直接访问了executor。

这样的问题在于,这样使用完全丧失了AsyncTask本身的意图。个人的观点是,AsyncTask提供了一个后台任务切换到主线程的通道,就像RxJava的subscribeOn/observeOn一样,同时提供cancel方法,可以取消掉切换回主线程执行的代码,从而防止内存泄漏。

AsyncTask asyncTask = new AsyncTask() {
@Override
protected Object doInBackground(Object[] objects) {
return null;
}

@Override
protected void onPostExecute(Object o) {
//1.提供了后台线程切换回主线程的方法
super.onPostExecute(o);
}
};

//2.可以随时取消
asyncTask.cancel(true);

But!如果直接使用execute方法的话,我们完全没有利用到AsyncTask本身设计的初衷下的优势,和直接自己创建一个线程池没有任何区别,还存在内存泄漏的

《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》

【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享

风险。这样的用法,肯定不能称之为best practice.

4. 以为RxJava的unsubscribe能包治百病

这个误区标题起的有点模糊,这个没办法,因为例子有点点复杂。让我来慢慢解释。

image

我们以一个实际的app例子开始,让我们看看youtube的app退订频道功能:

用户点击退订按钮之后,app发出api call,告诉后台我们停止订阅该频道,同时把UI更新为progress bar,当api call结束,在api的回调里面我们更新UI控件显示已退订UI。我们写一个示例代码看看:完美!

但是万一用户在点击退订按钮,但是api call还没发出去之前就退出了app呢?

public class YoutubePlayerActivity extends Activity {
private Subscription subscription;
public void setUnSubscribeListner(){
unsubscribeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
subscription = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber<? super Void> subscriber) {
try {
//在这里我们做取消订阅的API, http
API api = new API();
api.unSubscribe();
}
catch (Exception e){
subscriber.onError(e);
}
subscriber.onNext(null);
subscriber.onCompleted();
}
})

.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1() {
@Override
public void call(Void aVoid) {
//API call成功!,在这里更新订阅button的ui
unsubscribeButton.toggleSubscriptionStatus();
}
});
}
});
}
@Override
protected void onDestroy() {
super.onDestroy();
//onDestroy 里面对RxJava stream进行unsubscribe,防止内存泄漏
subscription.unsubscribe();
}
}

看似好像没啥问题,没有内存泄漏,可以后台线程和主线程直接灵活切换,更新UI不会crash。而且我们使用了Schedulers.io()调度器,看似也没有浪费线程资源。

BUT!!!!!!

我们先仔细想想一个问题。我们在点击button之后,我们的Observable

API api = new API();
api.unSubscribe();

会立刻执行么?

答案是NO。因为我们的Observable是subscribeOn io线程池。如果该线程池现在非常拥挤,这段代码,这个Observable是不会立刻执行的。该段代码会华丽丽的躺在线程池的队列中,安安静静的等待轮到自己执行。

那么如果用户点击按钮,同时退出app,我们unubscribe了这个RxJava 的observable 我们就存在一个不会执行api call的风险。也就是用户点击退订按钮,退出app,返回app的时候,会发现,咦,怎么明明点了退订,竟然还是订阅状态?

这就回到了一个本质问题,来自灵魂的拷问。是不是所有异步调用,都需要和Activity或者fragment的生命周期绑定?

答案同样是NO,在很多应用场景下,当用户做出一个行为的时候,我们必须坚定不移的执行该行为背后的一切操作,至于异步操作完成之后的UI更新,则视当前Activity或者fragment的生命周期决定。也就是异步操作和生命周期无关,UI更新和生命周期有关。简单点说,很多情况下,写操作不能取消,读操作可以。

很多情况下,比如支付,订阅等等这种用户场景,需要涉及到异步操作的都是会有以上的问题。在这些场景下,我们需要遵循以下流程。
用户做出一个行为的时候,我们必须坚定不移的执行该行为背后的一切操作,至于异步操作完成之后的UI更新,则视当前Activity或者fragment的生命周期决定。也就是异步操作和生命周期无关,UI更新和生命周期有关。简单点说,很多情况下,写操作不能取消,读操作可以。

很多情况下,比如支付,订阅等等这种用户场景,需要涉及到异步操作的都是会有以上的问题。在这些场景下,我们需要遵循以下流程。

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-11-30 15:43:53  更:2021-11-30 15:45:00 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 5:54:25-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码