ThreadLocal:你不知道的优化技巧,Android开发者都在用

引言

Android开发中,多线程是一个常见的话题。为了有效地处理多线程的并发问题,Android提供了一些工具和机制。其中,ThreadLocal是一个强大的工具,它可以使得每个线程都拥有自己独立的变量副本,从而避免了线程安全问题。

本文将深入探讨Android中的ThreadLocal原理及其使用技巧, 帮助你更好的理解和使用ThreadLocal

ThreadLocal的原理

java 复制代码
public class Thread implements Runnable {

    /* ThreadLocal values pertaining to this thread. This map is maintained
     * by the ThreadLocal class. */
    ThreadLocal.ThreadLocalMap threadLocals = null;
}

ThreadLocal的原理是基于每个线程都有一个独立的ThreadLocalMap对象。ThreadLocalMap对象是一个Map,它的键是ThreadLocal对象,值是ThreadLocal对象保存的值。

ini 复制代码
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        map.set(this, value);
    } else {
        createMap(t, value);
    }
}

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

当我们调用ThreadLocalset()方法时,会将值存储到当前线程的ThreadLocalMap对象中。当我们调用ThreadLocalget()方法时,会从当前线程的ThreadLocalMap对象中获取值。

ThreadLocal的使用

使用ThreadLocal非常简单,首先需要创建一个ThreadLocal对象,然后通过setget方法来设置和获取线程的局部变量。以下是一个简单的例子:

kotlin 复制代码
val threadLocal = ThreadLocal<String>()

fun setThreadName(name: String) {
    threadLocal.set(name)
}

fun getThreadName(): String {
    return threadLocal.get() ?: "DefaultThreadName"
}

Android开发中,ThreadLocal的使用场景非常多,比如:

  • Activity中存储Fragment的状态
  • Handler中存储消息的上下文
  • RecyclerView中存储滚动位置

实际应用场景

kotlin 复制代码
// 在 Activity 中存储 Fragment 的状态
class MyActivity : AppCompatActivity() {

    private val mFragmentState = ThreadLocal<FragmentState>()

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

        // 获取 Fragment 的状态
        val fragmentState = mFragmentState.get()
        if (fragmentState == null) {
            // 初始化 Fragment 的状态
            fragmentState = FragmentState()
        }

        // 设置 Fragment 的状态
        mFragmentState.set(fragmentState)

        // 创建 Fragment
        val fragment = MyFragment()
        fragment.arguments = fragmentState.toBundle()
        supportFragmentManager.beginTransaction().add(R.id.container, fragment).commit()
    }

}

class FragmentState {

    var name: String? = null
    var age: Int? = null

    fun toBundle(): Bundle {
        val bundle = Bundle()
        bundle.putString("name", name)
        bundle.putInt("age", age)
        return bundle
    }

}

这段代码在Activity中使用ThreadLocal来存储Fragment的状态。当Activity第一次启动时,会初始化Fragment的状态。当Activity重新启动时,会从ThreadLocal中获取Fragment的状态,并将其传递给Fragment

注意事项

  • 内存泄漏风险:

ThreadLocal变量的生命周期与线程的生命周期是一致的。这意味着,如果一个线程一直不结束,那么它所持有的ThreadLocal变量也不会被释放。这可能会导致内存泄漏。

为了避免内存泄漏,我们应该在不再需要ThreadLocal变量时,显式地将其移除。

kotlin 复制代码
threadLocal.remove()
  • 不适合全局变量: ThreadLocal适用于需要在线程间传递的局部变量,但不适合作为全局变量的替代品。

优化技巧

  • 合理使用默认值: 在获取ThreadLocal值时,可以通过提供默认值来避免返回null,确保代码的健壮性。
kotlin 复制代码
fun getThreadName(): String {
    return threadLocal.get() ?: "DefaultThreadName"
}
  • 懒加载初始化: 避免在声明ThreadLocal时就初始化,可以使用initialValue方法进行懒加载,提高性能。
kotlin 复制代码
val threadLocal = object : ThreadLocal<String>() {
    override fun initialValue(): String {
        return "DefaultValue"
    }
}
  • 尽量避免在ThreadLocal中保存大对象

结论

在本文中,我们介绍了ThreadLocal的原理和使用技巧,希望这些知识能够帮助你更好地理解和使用它。

推荐

android_startup: 提供一种在应用启动时能够更加简单、高效的方式来初始化组件,优化启动速度。不仅支持Jetpack App Startup的全部功能,还提供额外的同步与异步等待、线程控制与多进程支持等功能。

AwesomeGithub: 基于Github的客户端,纯练习项目,支持组件化开发,支持账户密码与认证登陆。使用Kotlin语言进行开发,项目架构是基于JetPack&DataBinding的MVVM;项目中使用了Arouter、Retrofit、Coroutine、Glide、Dagger与Hilt等流行开源技术。

flutter_github: 基于Flutter的跨平台版本Github客户端,与AwesomeGithub相对应。

android-api-analysis: 结合详细的Demo来全面解析Android相关的知识点, 帮助读者能够更快的掌握与理解所阐述的要点。

daily_algorithm: 每日一算法,由浅入深,欢迎加入一起共勉。

相关推荐
读心悦10 分钟前
CSS 溢出内容处理、可见性控制与盒类型设置深度解析
前端·css
Minyy1118 分钟前
Vue3指令(二)--v-text、v-html数据渲染,计算属性
前端·javascript·vue.js·前端框架·vue·html
个人开发-胡涂涂21 分钟前
ECMAScript标准:JavaScript的核心
前端·javascript·ecmascript
androidwork23 分钟前
Android 中使用通知(Kotlin 版)
android·kotlin
GISer_Jing26 分钟前
React底层架构深度解析:从虚拟DOM到Fiber的演进之路
前端·react.js·架构
斯密码赛我是美女34 分钟前
ssti刷刷刷
java·服务器·前端
Mryan20051 小时前
Angular | 利用 `ChangeDetectorRef` 解决 Angular 动态显示输入框的聚焦问题
前端·javascript·angular.js
郭尘帅6661 小时前
Vue3中实现轮播图
开发语言·前端·javascript
众乐乐_20081 小时前
Java 后端给前端传Long值,精度丢失的问题与解决
java·前端·状态模式
Digitally1 小时前
如何从 Android 设备打印短信(5 种方法)
android