只需一篇文章“吃透”Android多线程技术,安卓内存优化工具

2021/12/13 7:19:21

本文主要是介绍只需一篇文章“吃透”Android多线程技术,安卓内存优化工具,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

以上的做法是非常不可取的,缺点非常的多,想必大部分朋友面试的时候都会遇到这种问题,分析一下为啥不可以。浪费线程资源是第一,最重要的是我们无法控制该线程的执行,因此可能会造成不必要的内存泄漏。在Activity或者Fragment这种有生命周期的控件里面直接执行这段代码,相信大部分人都知道会可能有内存泄漏。但是就算在其他的设计模式,比如MVP,同样也可能会遇到这个问题。

//runnable->presenter->view
public class Presenter {
//持有view引用
private IView view;
public Presenter(IView v){
this.view = v;
}
public void doSomething(String[] args){
new Thread(new Runnable() {
@Override
pub

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

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

lic void run() {
/**
** 持有presenter引用
**/
//do something
}
}).start();
}
public static interface IView{}
}

比如图中的一段代码(我标记了引用方向),通常MVP里面的View都是一个接口,但是接口的实现可能是Activity。那么在代码中就可能存在内存泄漏了。Thread的runnable是匿名内部类,持有presenter的引用,presenter持有view的引用。这里的引用链就会造成内存泄漏了。关键是,就算你持有线程的句柄,也无法把这个引用关系给解除。

所以优秀的设计模式也阻止不了内存泄漏。。。。。

2. 频繁使用HandlerThread

虽然HandlerThread是安卓framework的亲儿子,但是在实际的开发过程中却很少能有他的适用之处。HandlerThread继承于Thread类,所以每次开启一个HandlerThread就和开启一个普通Thread一样,很浪费资源。我们可以通过使用HandlerThread的例子来分析他最大的作用是什么。

static HandlerThread thread = new HandlerThread(“test”);
static {
thread.start();
}

public void testHandlerThread(){
Handler handler = new Handler(thread.getLooper());
handler.post(new Runnable() {
@Override
public void run() {
//do something
}
});
//如果不需要了就remove handler’s message
handler.removeCallbacksAndMessages(null);
}

public void test(){
//如果我还想利用HandlerThread,但是已经丢失了handler的句柄,那么我们利用handler thread再构建一个handler
Handler handler = new Handler(thread.getLooper());
handler.post(new Runnable() {
@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本身设计的初衷下的优势,和直接自己创建一个线程池没有任何区别,还存在内存泄漏的风险。这样的用法,肯定不能称之为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 {
ener() {
@Override
public void onClick(View v) {
subscription = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber<? super Void> subscriber) {
try {



这篇关于只需一篇文章“吃透”Android多线程技术,安卓内存优化工具的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程