踏破铁鞋无觅处,从AsyncTask学Android线程池

简介:

android对于主线程的响应时间限制的非常严格,稍有不慎就会遇到Application Not Responding(ANR)的弹框。用户可以轻点手指关掉你的APP。官方文档写的非常明确!同时,保持应用随时响应用户的操作也是良好用户体验的前提。

线程的开始和结束

要做到以上多线程是必不可少的。课本会告诉你什么时候开辟一个线程,但是很少说的一个很重要的问题是结束。比如,我现在在Activity里有一个工作需要创建一个线程执行,但是这个Activity在进入后台后不幸遇到系统回收资源被销毁了。但是这个线程还在漫无目的的游走,耗费资源。

如何结束?先创建一个:

mThread = Thread(Runnable {
    // do something here...
})

mThread?.start()

以上使用kotlin的lambda表达式简写了创建Runnable对象部分的代码。主旨还是创建了一个Runnable对象,并将其作为参数传入Thread

如何让一个Thread能够退出呢?这就要在Runnable身上下功夫了。首先添加一个是否停止的标识isCancelled,一旦值为true则停止线程的运行,否则继续。我们这里不讨论Thread#interrupt()这个方法,这个方法诡异的地方太多。

首先要给Runnable“添加一个属性”作为上文的是否停止的标识。直接添加时不可能的,Runnable只是一个interface,不是class。所以要实现这个借口为一个抽象类,这样就可以添加属性了。

abstract class CancelableRunnable() : Runnable {
    var isCancelled: Boolean = false
}

这里使用抽象类,是因为run()方法的实现留给使用的时候给出。

var runnable = object : CancelableRunnable() {
    override fun run() {
        if (isCancelled) {
            var msg = mHandler.obtainMessage(THREAD_CANCELLED)
            mHandler.sendMessage(msg)
            return
        }

        Thread.sleep(2000)

        if (isCancelled) {
            var msg = mHandler.obtainMessage(THREAD_CANCELLED)
            mHandler.sendMessage(msg)
            return
        }

        var msg = mHandler.obtainMessage(THREAD_FINISHED)
        mHandler.sendMessage(msg)
    }
}

Thread.sleep(2000)用来模拟一个费时的任务。开始之前检测是否取消了线程的执行,执行之后在检测。之后的检测是有的时候任务执行之后需要有持久化处理结果或者修改任务完成情况的标识之类的动作,如果已经取消了线程的执行,即使任务执行完成也不持久化结果、不修改完成情况。

最后都检测完成之后如果没有取消线程,则发出任务完成执行的消息。

发出和处理这些消息的Handler的定义:

var mHandler = object : Handler() {
    override fun handleMessage(msg: Message?) {
        when (msg?.what) {
            THREAD_CANCELLED -> {
                mResultTextView.text = "thread cancelled"
            }
            THREAD_FINISHED -> {
                mResultTextView.text = "thread finished"
            }
            else -> {
                mResultTextView.text = "what's going on"
            }
        }
    }
}

运行在UI线程的Handler检测从线程发出的消息,如果是THREAD_CANCELLED那就是线程已经取消了,如果是THREAD_FINISHED那就是线程完全运行结束。之后根据message的消息设置TextView的文本内容。

这里使用了两个按钮来启动和停止线程:

findViewById(R.id.start_button)?.setOnClickListener { v ->
    runnable.isCancelled = false
    mThread = Thread(runnable)
    mThread?.start()

    mResultTextView.text = "Thread running..."
}

findViewById(R.id.stop_button)?.setOnClickListener { v ->
    this.runnable.isCancelled = true
}

上面用到的Runnable是只做一件事的,如果是连续不断的循环很多事的话也可以使用white语句来控制是否一直执行线程的工作。一旦设置为停止线程,则停止线程任务的循环跳出Runnable#run()方法,结束线程。

完整代码放在附录中
所以,如果你在Activity里开辟了一个线程,在Activity被回收的时候结束线程就可以这么做:

override fun onDestroy() {
    super.onDestroy()
    
    this.runnable.isCancelled = true
}

这样就再也不用担心Activity挂了,线程还阴魂不散了。

AsyncTask

既然缘起AsyncTask那就肯定需要读者一起了解一下相关的概念。

比起来使用Handler+Thread+Runnable的多线程异步执行模式来说,使用AsyncTask是简单了非常的多的。

先简单了解一下AsyncTask

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask是一个抽象泛型类。三个类型ParamsProgressResult分别对应的是输入参数的类型,精度更新使用的类型,最后是返回结果的类型。其中任何一个类型如果你不需要的话,可以使用java.lang.Void代替。

继承AsyncTask给出自己的实现,最少需要实现doInBackground方法。doInBackground方法是在后台线程中运行的。如果要在任务执行之后更新UI线程的话还至少需要给出onPostExecute方法的实现,在这个方法中才可以更新UI。

上述的两个方法已经构成了一个AsyncTask使用的基本单元。在后台线程处理一些任务,并在处理完成之后更新UI。但是如果一个任务比较长,只是在最后更新UI是不够的,还需要不断的提示用户已经完成的进度是多少。这就是需要另外实现onProgressUpdate方法。并在doInBackground方法中调用publishProgress方法发出每个任务的处理进度。

这个AsyncTask总体上就是这样的了:

inner class DemoAsyncTask() : AsyncTask<String, Int, String>() {
    //        var isRunning = true
    override fun doInBackground(vararg params: String?): String? {
        Log.i(TAG, "##AsyncTask doing something...")
        
        var i = 0
        val TOTAL = 100000000
        var progress = 0
        while (i < TOTAL) {
            Log.d(TAG, "doning jobs $i is cancelled $isCancelled")
            i++

            var currentProgress = i.toFloat() / TOTAL
            if (currentProgress > progress && Math.abs(currentProgress - progress) > 0.1) {
                progress = currentProgress
                publishProgress((progress * 100).toInt())
            }
        }
        }

        Log.d(TAG, "doing jobs $i is cancelled $isCancelled")

        return "Task done"
    }

    override fun onPostExecute(result: String?) {
        this@CancalableActivity.mAsyncTextView?.text = result
    }

    override fun onProgressUpdate(vararg values: Int?) {
        mAsyncTextView?.text = "${mAsyncTextView?.text ?: "Async task..."} progress: ${values?.get(0) ?: 0}"
    }
}

到这里各位读者应该对AsyncTask已经有一个总体的认识了。后台任务在doInBackground处理,处理过程的百分比使用publishProgress方法通知,并在onProgressUpdate方法中更新UI的百分比。最后任务处理全部完成之后在onPostExecute更新UI,显示全部完成。

怎么取消一个任务的执行呢?这个机本身还上面的线程的取消基本上一样。只是AsyncTask已经提供了足够的属性和方法完成取消的工作。直接调用AsyncTask#cancel方法就可以发出取消的信号,但是是否可以取消还要看这个方法的返回值是什么。如果是true那就是可以,否则任务不可取消(但是不可取消的原因很可能是任务已经执行完了)。

调用cancel方法发出取消信号,并且可以取消的时候。isCancelled()就会返回true。同时onPostExecute这个方法就不会再被调用了。而是onCancelled(object)方法被调用。同样是在doInBackground这个方法执行完之后调用。所以,如果想要在取消任务执行后尽快的调用到onCancelled(object)的话,就需要在onInBackground的时候不断的检查isCancelled()是否返回true。如果返回的是true就跳出方法的执行。

inner class DemoAsyncTask() : AsyncTask<String, Int, String>() {
    //        var isRunning = true
    override fun doInBackground(vararg params: String?): String? {
        Log.i(TAG, "##AsyncTask doing something...")

        var i = 0
        val TOTAL = 1000000
        var progress = 0.0f
        while (i < TOTAL && !isCancelled) {
            Log.d(TAG, "doning jobs $i is cancelled $isCancelled")
            i++

            var currentProgress = i.toFloat() / TOTAL
            if (currentProgress > progress && Math.abs(currentProgress - progress) > 0.1) {
                progress = currentProgress
                publishProgress((progress * 100).toInt())
            }
        }

        Log.d(TAG, "doning jobs $i is cancelled $isCancelled")

        return "Task done"
    }

    override fun onPostExecute(result: String?) {
        this@CancalableActivity.mAsyncTextView?.text = result
    }

    override fun onProgressUpdate(vararg values: Int?) {
        mAsyncTextView?.text = "${mAsyncTextView?.text ?: "Async task..."} progress: ${values?.get(0) ?: 0}"
    }

    override fun onCancelled() {
        Log.i(TAG, "##Task cancelled")
//            isRunning = false
        this@CancalableActivity.mAsyncTextView?.text = "###Task cancelled"
    }

//        override fun onCancelled(result: String?) {
//            Log.i(TAG, "##Task cancelled")
////            isRunning = false
//            this@CancalableActivity.mAsyncTextView?.text = result ?: "Task cancelled"
//        }
}

onCancelled()是API level 3的时候加入的。onCancelled(Result result)是API level 11的时候加入的。这个在兼容低版本的时候需要注意。

但是一点需要格外注意:

AsyncTask一定要在UI线程初始化。不过在**JELLY_BEAN**以后这个问题也解决了。
总之,在UI线程初始化你的`AsyncTask`肯定是不会错的。

线程池

下面就来看看线程池的概念。顾名思义,线程池就是放线程的池子。把费时费力,或者影响响应用户操作的代码放在另外一个线程执行时常有的事。但是如果无顾忌的开辟线程,却会适得其反,严重的浪费系统资源。于是就有了线程池。线程池就是通过某些机制让线程不要创建那么多,能复用就复用,实在不行就让任务排队等一等

这个机制在线程池的构造函数里体现的非常明显:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)
  • corePoolSize 线程池里闲着也不回收的线程数量。除非allowCoreThreadTimeOut指定可以回收。
  • ** maximumPoolSize** 线程池允许的最大线程数。
  • ** keepAliveTime** 非核心线程(就是如果核心线程数量corePoolSize定义为1的话,第二个就是非核心线程)的超时时间。
  • unit keepAliveTime的时间单位,毫秒,秒等。
  • ** workQueue** 存放execute(Runnable cmd)方法提交的Runnable任务。
  • ** threadFactory**线程池用来创建新线程用的一个工厂类。
  • ** handler**线程池达到最大线程数,并且任务队列也已经满的时候会拒绝execute(Runnable cmd)方法提交任务。这个时候调用这个handler。

知道以上基本内容以后,就可以探讨线程池管理线程的机制了。概括起来有三点:

  1. 如果线程池的线程数量少于corePoolSize的时候,线程池会使用threadFactory这个线程工厂创建新的线程执行Runnable任务。
  2. 如果线程池的线程数量大于corePoolSize的时候,线程池会把Runnable任务存放在队列workQueue中。
  3. 线程池的线程数量大于corePoolSize,队列workQueue已满,而且小于maximumPoolSize的时候,线程池会创建新的线程执行Runnable任务。否则,任务被拒。

现在回到AsyncTask。被人广为诟病的AsyncTask是他的任务都是顺序执行的。一个AsyncTask的实例只能处理一个任务。但是在AsyncTask后面处理任务的是一个静态的线程池。在看这个线程池SerialExecutorexecute方法实现:

final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();

public synchronized void execute(final Runnable r) {
    mTasks.offer(new Runnable() {
        public void run() {
            try {
                r.run();
            } finally {
                scheduleNext();
            }
        }
    });
    if (mActive == null) {
        scheduleNext();
    }
}

protected synchronized void scheduleNext() {
    if ((mActive = mTasks.poll()) != null) {
        // 执行一个task
    }
}

这个线程池SerialExecutor在处理Runnable的传入参数的时候对这个任务进行了重新包装成了一个新的Runnable对象,并且将这个新的对象存入了一个叫做mTasks的队列。这个新的Runnable对象首先执行传入的任务,之后不管有无异常调用scheduleNext方法执行下一个。于是整体的就生成了一个传入的任务都顺序执行的逻辑。

这个线性执行的静态线程池SerialExecutor的实现非常简单。并不涉及到我们前文所说的那么多复杂的内容。在实现上,这个线程池只实现了线程池的最顶层接口Executor。这个接口只有一个方法就是execute(Runnable r)。另外需要强调一点:mTasks的类型ArrayDeque<T>是一个不受大小限制的队列。可以存放任意多的任务。在线程池的讨论中遇到队列就需要看看容量概念。

SerialExecutor只是进行了简单的队列排列。但是在scheduleNext方法的实现上又会用到一个复杂一些的线程池来执行任务的具体执行。这线程池叫做
THREAD_POOL_EXECUTOR。我们来具体看看其实现:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;

public static final Executor THREAD_POOL_EXECUTOR
        = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

这个线程池的实现非常具有现实价值。虽然稍后介绍的系统提供的几种线程池的实现就够用。但是难免遇到一些需要自定义线程池的情况。详细解析如下:

  • CORE_POOL_SIZE 线程池的核心线程数量为设备核心数加一。
  • ** MAXIMUM_POOL_SIZE** 线程池的最大线程数量为核心数的两倍加一。
  • ** KEEP_ALIVE** 线程池中非核心线程的超时时间为一秒。
  • ** sPoolWorkQueue ** 线程池存放任务的队列。最大个数为128个。参考上面说的线程池处理机制,会出现任务被拒的情况。排队的线程池SerialExecutor存放任务的队列是可以认为无限长的,但是THREAD_POOL_EXECUTOR的队列最多存放128个任务,加上线程池核心线程的数量,能处理的任务相对有限。出现任务被拒的情况的几率比较大。所以,往AsyncTask里直接添加Runnable对象的时候需要三思。
  • ** sThreadFactory** 线程池用来创建线程的工厂对象。ThreadFactory是一个只有一个方法Thread newThread(Runnable r);的接口。这里在实现的时候给新创建的线程添加了一个原子计数,并把这个计数作为线程名称传递给了线程的构造函数。

到这里,我们就已经很清楚AsyncTask是如何用一个极其简单的线程池SerialExecutor给任务排队的。又是如何使用一个复杂一些的线程池THREAD_POOL_EXECUTOR来处理具体的任务执行的。尤其是线程池THREAD_POOL_EXECUTOR,在我们实际应用一个自定义的线程池的时候在设定线程池核心线程数量,线程池最大线程数量的时候都依据什么?明显就是设备的CPU核心数。线程分别在不同个CPU核心中做并行的处理。核心数多可以同时处理的线程数就相对较多,相反则会比较少一些。如此设置核心线程数量就会平衡并行处理的任务数量和在处理的过程中耗费的系统资源。

为了让开发者省时省力,系统默认的提供了四种可以适应不同应用条件的线程池:

public class Executors {
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }
    
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
    
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }
}
  • ** newFixedThreadPool** 顾名思义,线程数量固定的线程池,且其数量等于参数指定值。这一类型的线程池的核心线程数量和最大线程数量是一样的。存放任务的队列的容量可以被认为无限大。一旦线程池创建的线程数量等* nThreads*参数值的时候,新增的任务将会被存放在任务队列中等待核心线程可用的时候执行。
  • ** newSingleThreadExecutor** newFixedThreadPool的一个特殊情况,当mThreads值为1的时候。
  • ** newCachedThreadPool** 这一类型的线程池中创建的线程都有60秒的超时时间,由于超时时间比较长等于是线程空闲了以后被缓存了60秒。由于核心线程数量为0,所以创建的线程都是非核心线程。也因此超时时间才管用。任务队列SynchronousQueue非常特殊,简单理解就是一个任务都存放不了。而线程池的最大线程数量又设定为Integer.MAX_VALUE,可以认为是无限大。根据线程池处理任务的机制,可以认为有新任务过来就会创建一个线程去处理这个任务,但是如果存在空闲没有超时的线程会优先使用。
  • ** newScheduledThreadPool** 生成一个ScheduledThreadPoolExecutor实例。可以通过其提供的接口方法设定延迟一定的时间执行或者隔一定的时间周期执行。

来一个例子:

import static java.util.concurrent.TimeUnit.*;
class BeeperControl {
    private final ScheduledExecutorService scheduler =
    Executors.newScheduledThreadPool(1);

    public void beepForAnHour() {
        final Runnable beeper = new Runnable() {
            public void run() { System.out.println("beep"); 
        };
        final ScheduledFuture beeperHandle =
            scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS);
        scheduler.schedule(new Runnable() {
            public void run() { beeperHandle.cancel(true); }
        }, 60 * 60, SECONDS);
    }
}}

附录

这里是上面例子中使用的全部代码。

线程的停止:

package demo.retrofit2rxjavademo.Activities

import android.os.Bundle
import android.os.Handler
import android.os.Message
import android.support.v7.app.AppCompatActivity
import android.widget.TextView
import demo.retrofit2rxjavademo.R

class CancalableActivity : AppCompatActivity() {
    lateinit var mResultTextView: TextView
    var mHandler = object : Handler() {
        override fun handleMessage(msg: Message?) {
            when (msg?.what) {
                THREAD_CANCELLED -> {
                    mResultTextView.text = "thread cancelled"
                }
                THREAD_FINISHED -> {
                    mResultTextView.text = "thread finished"
                }
                else -> {
                    mResultTextView.text = "what's going on"
                }
            }
        }
    }

    var mThread: Thread? = null

    var runnable = object : CancelableRunnable() {
        override fun run() {
            if (isCancelled) {
                var msg = mHandler.obtainMessage(THREAD_CANCELLED)
                mHandler.sendMessage(msg)
                return
            }

            Thread.sleep(2000)

            if (isCancelled) {
                var msg = mHandler.obtainMessage(THREAD_CANCELLED)
                mHandler.sendMessage(msg)
                return
            }

            var msg = mHandler.obtainMessage(THREAD_FINISHED)
            mHandler.sendMessage(msg)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_cancalable)

        mResultTextView = findViewById(R.id.run_result_text_view) as TextView

        findViewById(R.id.start_button)?.setOnClickListener { v ->
            runnable.isCancelled = false
            mThread = Thread(runnable)
            mThread?.start()

            mResultTextView.text = "Thread running..."
        }

        findViewById(R.id.stop_button)?.setOnClickListener { v ->
            this.runnable.isCancelled = true
        }
    }

    abstract class CancelableRunnable() : Runnable {
        var isCancelled: Boolean = false
    }

    companion object {
        val THREAD_FINISHED = 0
        val THREAD_CANCELLED = 1
    }
}
欢迎加群互相学习,共同进步。QQ群:iOS: 58099570 | Android: 572064792 | Nodejs:329118122 做人要厚道,转载请注明出处!























本文转自张昺华-sky博客园博客,原文链接:http://www.cnblogs.com/sunshine-anycall/p/5506154.html ,如需转载请自行联系原作者

相关文章
|
4月前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
【7月更文挑战第28天】在Android开发中,确保UI流畅性至关重要。多线程与异步编程技术可将耗时操作移至后台,避免阻塞主线程。我们通常采用`Thread`类、`Handler`与`Looper`、`AsyncTask`及`ExecutorService`等进行多线程编程。
60 2
|
4月前
|
Java Android开发
Android面试题经典之Glide取消加载以及线程池优化
Glide通过生命周期管理在`onStop`时暂停请求,`onDestroy`时取消请求,减少资源浪费。在`EngineJob`和`DecodeJob`中使用`cancel`方法标记任务并中断数据获取。当网络请求被取消时,`HttpUrlFetcher`的`cancel`方法设置标志,之后的数据获取会返回`null`,中断加载流程。Glide还使用定制的线程池,如AnimationExecutor、diskCacheExecutor、sourceExecutor和newUnlimitedSourceExecutor,其中某些禁止网络访问,并根据CPU核心数动态调整线程数。
141 2
|
1月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
49 4
|
2月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android多线程编程的重要性及其实现方法,涵盖了基本概念、常见线程类型(如主线程、工作线程)以及多种多线程实现方式(如`Thread`、`HandlerThread`、`Executors`、Kotlin协程等)。通过合理的多线程管理,可大幅提升应用性能和用户体验。
131 15
一个Android App最少有几个线程?实现多线程的方式有哪些?
|
2月前
|
Java Android开发 UED
🧠Android多线程与异步编程实战!告别卡顿,让应用响应如丝般顺滑!🧵
在Android开发中,为应对复杂应用场景和繁重计算任务,多线程与异步编程成为保证UI流畅性的关键。本文将介绍Android中的多线程基础,包括Thread、Handler、Looper、AsyncTask及ExecutorService等,并通过示例代码展示其实用性。AsyncTask适用于简单后台操作,而ExecutorService则能更好地管理复杂并发任务。合理运用这些技术,可显著提升应用性能和用户体验,避免内存泄漏和线程安全问题,确保UI更新顺畅。
97 5
|
2月前
|
Java 数据库 Android开发
一个Android App最少有几个线程?实现多线程的方式有哪些?
本文介绍了Android应用开发中的多线程编程,涵盖基本概念、常见实现方式及最佳实践。主要内容包括主线程与工作线程的作用、多线程的多种实现方法(如 `Thread`、`HandlerThread`、`Executors` 和 Kotlin 协程),以及如何避免内存泄漏和合理使用线程池。通过有效的多线程管理,可以显著提升应用性能和用户体验。
73 10
|
2月前
|
API Android开发 iOS开发
安卓与iOS开发中的线程管理对比
【9月更文挑战第12天】在移动应用的世界中,安卓和iOS平台各自拥有庞大的用户群体。开发者们在这两个平台上构建应用时,线程管理是他们必须面对的关键挑战之一。本文将深入探讨两大平台在线程管理方面的异同,通过直观的代码示例,揭示它们各自的设计理念和实现方式,帮助读者更好地理解如何在安卓与iOS开发中高效地处理多线程任务。
|
2月前
|
Android开发 开发者 Kotlin
告别AsyncTask:一招教你用Kotlin协程重构Android应用,流畅度飙升的秘密武器
【9月更文挑战第13天】随着Android应用复杂度的增加,有效管理异步任务成为关键。Kotlin协程提供了一种优雅的并发操作处理方式,使异步编程更简单直观。本文通过具体示例介绍如何使用Kotlin协程优化Android应用性能,包括网络数据加载和UI更新。首先需在`build.gradle`中添加coroutines依赖。接着,通过定义挂起函数执行网络请求,并在`ViewModel`中使用`viewModelScope`启动协程,结合`Dispatchers.Main`更新UI,避免内存泄漏。使用协程不仅简化代码,还提升了程序健壮性。
82 1
|
2月前
|
Java Android开发 开发者
安卓应用开发中的线程管理优化技巧
【9月更文挑战第10天】在安卓开发的海洋里,线程管理犹如航行的风帆,掌握好它,能让应用乘风破浪,反之则可能遭遇性能的暗礁。本文将通过浅显易懂的语言和生动的比喻,带你探索如何优雅地处理安卓中的线程问题,从基础的线程创建到高级的线程池运用,让你的应用运行更加流畅。
|
3月前
|
调度 Android开发 开发者
【颠覆传统!】Kotlin协程魔法:解锁Android应用极速体验,带你领略多线程优化的无限魅力!
【8月更文挑战第12天】多线程对现代Android应用至关重要,能显著提升性能与体验。本文探讨Kotlin中的高效多线程实践。首先,理解主线程(UI线程)的角色,避免阻塞它。Kotlin协程作为轻量级线程,简化异步编程。示例展示了如何使用`kotlinx.coroutines`库创建协程,执行后台任务而不影响UI。此外,通过协程与Retrofit结合,实现了网络数据的异步加载,并安全地更新UI。协程不仅提高代码可读性,还能确保程序高效运行,不阻塞主线程,是构建高性能Android应用的关键。
62 4