如何应对 Android 面试官 -> 玩转 JetPack LiveData

RK3572-Android16-记录一次OTA升级失败解决方案 这里记录一次解决RK3576 Android16 OTA 用Recovery 升级失败的一次解决案例,将部分经验作为总结。本次OTA升级耗时了几天,最终是RK给出的解决方案,再加上经验来解决了其它部分问题新平台,高一点的Android版本,可能本身有bug,多请教平台来,不要蒙着干,干不出来的。 阅读详情

前言


在这里插入图片描述

本章开始 LiveData 的讲解;

LiveData 是一种可观察的数据存储器类,与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件如(Activity、Fragment、Service)的生命周期。

这种感知能力确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者;

它既是观察者又是被观察者,充当着两个角色;

在这里插入图片描述

这样的一个状态流转,典型的数据驱动更新 UI;

基础使用


基础用法

我们先来声明一个 LiveData 变量

object MyLiveData { // 单例
    // 懒加载
    val info1: MutableLiveData<String> by lazy { MutableLiveData() }
}

这样,MyLiveData 中就持有了一个 LiveData 对象;

接下来声明观察者

class MainActivity : AppCompatActivity() {

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

        val textView : TextView = findViewById(R.id.tv_textview)

        // 观察者
        MyLiveData.info1.observe(this, {
            textView.text = it // 更新UI
        })
   }
}

可以使用 lambda 的方式,也可以使用传统的完整写法:

MyLiveData.info1.observe(this, object: Observer<String> {
    override fun onChanged(t: String?) {
        textView.text = t // 更新UI
    }
})

接下来,我们来触发改变;

MyLiveData.info1.value = "default" // setValue 主线程

也可以在子线程触发改变

thread {
  Thread.sleep(3000)
  MyLiveData.info1.postValue("三秒钟后,修改了哦")  // postValue 子线程
}

这就是典型的数据驱动UI,通过 postValue 或者 setValue 修改数据,通过 observer 回调驱动 UI 更新;

LiveData 是可以平替 EventBus 的,也就是说,我们在 A 界面触发改变,B 界面是可以监听到数据变化并更新UI;

前台更新UI

我们接下来看一个比较特殊的例子,可以验证只有当我们界面可见的时候才会更新UI这个点

声明 LiveData

object MyLiveData { // 单例
    // 这里为 data1 的MutableLiveData 懒加载初始化(懒加载:用到时才加载)
    val data1 : MutableLiveData<String> by lazy { MutableLiveData() }  
}

我们来声明一个 Service

class MyService : Service() {

    override fun onBind(intent: Intent): IBinder ? = null

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        thread {
            for (x in 1..10000) {
                Log.d("server", "服务器给推你推送消息啦(叮咚声响),消息内容是:${x}")
                MyLiveData.data1.postValue("服务器给推你推送消息啦,消息内容是:${x}")
                Thread.sleep(2000) // 2秒钟推一次
            }
        }
        return super.onStartCommand(intent, flags, startId)
    }
}

接着我们在 MainActivity 启动这个 Service,以及接收 LiveData 发送的消息

class MainActivity2 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)

        // 启动服务
        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            startService(Intent(this, MyService::class.java))
            Toast.makeText(MainActivity2@this, "推送服务器启动成功", Toast.LENGTH_SHORT).show()
        }

        // 观察者 只有在界面可见的情况下,才能做事情
        MyLiveData.data1.observe(this, {
            Log.d("server", "界面可见,说明用户在查看微信列表界面啦,更新消息列表UI界面:${it}")
            Toast.makeText(this, "更新消息列表UI界面成功:${it}", Toast.LENGTH_SHORT).show()
        })
    }
}

使用 Log 和 Toast 是为了方便查看,运行之后,当界面退后台的时候 我们能收到 LiveData 发送过来的消息,但是并不会更新UI;

数据更新的时候,如果不在前台,那么就不更新,而是等 Observer 到了前台才更新,这是利用了 Lifecycle 的特性来实现的,即性能高,又避免程序崩溃。这是 Rxjava 不能很方便做到的,它能做到,但不是很方便。这就是为什么我们用 RxJava 还能再最后一步转成 LiveData 的格式;

数据粘性

所谓数据粘性,就是我先修改数据,后订阅,但是我依然可以收到变化后的数据;

class MainActivity3 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main3)

        val button = findViewById<Button>(R.id.button)
        button.setOnClickListener {
            // 跳转之前 修改数据
            MyLiveData.value1.value = "我就是我,不一样的烟火1"
            startActivity(Intent(this, MainActivity4::class.java))
        }
    }
}

然后我们定义 MainActivity4

class MainActivity4 : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main4)

        // 我后观察数据,居然能够收到前面修改的数据,这就是数据粘性
        MyLiveData.value1.observe(this, {
            Toast.makeText(this, "观察者数据变化:$it", Toast.LENGTH_SHORT).show()
        })
    }
}

原理篇


分为订阅和发送,我们先来看下订阅 observe

订阅

我们进入这个 observe 方法看下

@MainThread
public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {
    ...
    // 三行核心代码
    // 让传递进来的 observer 具备感知生命周期的能力
    LifecycleBoundObserver wrapper = new LifecycleBoundObserver(owner, observer);
    ObserverWrapper existing = mObservers.putIfAbsent(observer, wrapper);
    // 将 LifecycleOwner 和 LifecycleObserver 关联起来
    owner.getLifecycle().addObserver(wrapper);
    ...
}

Observer 可以看到就是一个纯接口,是无法感知生命周期变化的,它并没有实现 LifecycleObserver 接口;

public interface Observer<T> {
    /**
     * Called when the data is changed.
     * @param t  The new data
     */
    void onChanged(T t);
}

而是借助 LifecycleBoundObserver 来让 observer 有了感知生命周期变化的能力;我们进入看一下

class LifecycleBoundObserver extends ObserverWrapper implements LifecycleEventObserver {
    @NonNull
    final LifecycleOwner mOwner;

    LifecycleBoundObserver(@NonNull LifecycleOwner owner, Observer<? super T> observer) {
        super(observer);
        mOwner = owner;
    }

    // 核心逻辑1,判断是否是存活状态 onStart、onResume 则是存活状态 mActive = true
    @Override
    boolean shouldBeActive() {
        return mOwner.getLifecycle().getCurrentState().isAtLeast(STARTED);
    }

    // 核心逻辑2,
    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
            @NonNull Lifecycle.Event event) {
        if (mOwner.getLifecycle().getCurrentState() == DESTROYED) {
            removeObserver(mObserver);
            return;
        }
        activeStateChanged(shouldBeActive());
    }

    @Override
    boolean isAttachedTo(LifecycleOwner owner) {
        return mOwner == owner;
    }

    @Override
    void detachObserver() {
        mOwner.getLifecycle().removeObserver(this);
    }
}

核心逻辑1,判断是否是存活状态 onStart、onResume 则是存活状态 mActive = true

核心逻辑2,我们在上一章的 Lifecycle 中,生命周期分发的时候,6 个状态都会调用到这个 onStateChanged 方法;

我们接着看下 activeStateChanged 这个方法

void activeStateChanged(boolean newActive) {
    if (newActive == mActive) {
        return;
    }
    mActive = newActive;
    boolean wasInactive = LiveData.this.mActiveCount == 0;
    LiveData.this.mActiveCount += mActive ? 1 : -1;
    if (wasInactive && mActive) {
        // 
        onActive();
    }
    if (LiveData.this.mActiveCount == 0 && !mActive) {
        // 
        onInactive();
    }
    if (mActive) {
        dispatchingValue(this);
    }
}

onActive() 和 onInactive() 是两个扩展接口,可以告诉外界存活状态和不存活状态,外界根据这两个方法进行扩展即可;

我们接着来看下 dispatchingValue 方法

void dispatchingValue(@Nullable ObserverWrapper initiator) {
    if (mDispatchingValue) {
        mDispatchInvalidated = true;
        return;
    }
    mDispatchingValue = true;
    do {
        mDispatchInvalidated = false;
        if (initiator != null) {
            // 核心逻辑1
            considerNotify(initiator);
            initiator = null;
        } else {
            for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
                    mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
                // 核心逻辑2    
                considerNotify(iterator.next().getValue());
                if (mDispatchInvalidated) {
                    break;
                }
            }
        }
    } while (mDispatchInvalidated);
    mDispatchingValue = false;
}

核心逻辑1 被 if(initiator != null) 包裹,可以看到 initiator 是外部传入,也就是 dispatchingValue(this) 说明这个 initator 一定不为 null 而我们一直在看的是 observe 订阅流程

所以:核心逻辑1 是订阅流程,核心逻辑2 是发送流程(setValue、postValue)

我们接着看下这个 considerNotify 方法;

private void considerNotify(ObserverWrapper observer) {
    if (!observer.mActive) {
        return;
    }
    if (!observer.shouldBeActive()) {
        // 核心逻辑1
        observer.activeStateChanged(false);
        return;
    }
    // 核心逻辑2
    if (observer.mLastVersion >= mVersion) {
        return;
    }
    observer.mLastVersion = mVersion;
    // 核心逻辑3
    observer.mObserver.onChanged((T) mData);
}

核心逻辑1 是判断界面如果突然可见了,就重新调用 activeStateChanged 再走一遍流程;

核心逻辑2 是决定我们的 LiveData 到底是不是粘性的;

mLastVersion 是观察者的版本,它是 -1;

mVersion 是被观察者的版本,它是 0,因为我们只要一调用 setValue 或者 postValue 它的版本就会 ++;

因为不相等,所以才能执行到核心逻辑3,将数据分发给观察者;

发送

我们进入 setValue 看下:

@MainThread
protected void setValue(T value) {
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

可以看到,mVersion 先执行 ++,才执行 dispatchingValue 方法;这个 dispatchingValue 方法就是我们前面刚分析过的 dispatchingValue 方法,只不过这次我们进入的是 else 的逻辑,因为传入的是 null,要执行 for 循环逻辑

for (Iterator<Map.Entry<Observer<? super T>, ObserverWrapper>> iterator =
        mObservers.iteratorWithAdditions(); iterator.hasNext(); ) {
    considerNotify(iterator.next().getValue());
    if (mDispatchInvalidated) {
        break;
    }
}

considerNotify 就是我们前面分析的 considerNotify 方法;

所以这也就能解释通粘性事件的原因了;

我们在 MainActivity3 中,通过 MyLiveData.value1.value = “我就是我,不一样的烟火1” 将 mVersion 通过 mVersion++ 修改为 0;

当我们打开 MainActivity4 的时候,通过 MyLiveData.value1.observe 触发订阅之后,由于 mVersion = 0,mLastVersion = -1,触发了粘性数据的 onChanged 方法,导致在 MainActivity4 中收到了消息;

粘性解决

public class LiveDataBusX {
    // 存放订阅者
    private Map<String, BusMutableLiveData<Object>> bus;
    private static LiveDataBusX liveDataBus = new LiveDataBusX();

    private LiveDataBusX() {
        bus = new HashMap<>();
    }
    
    public static LiveDataBusX getInstance(){
        return liveDataBus;
    }

    // 注册订阅者
    public synchronized <T> BusMutableLiveData<T> with(String key, Class<T> type){
        if(!bus.containsKey(key)){
            bus.put(key, new BusMutableLiveData<Object>());
        }
        return (BusMutableLiveData)bus.get(key);
    }

    public static class BusMutableLiveData<T> extends MutableLiveData{
        
        @Override
        public void observe(@NonNull LifecycleOwner owner, @NonNull Observer observer) {
            super.observe(owner, observer);
            hook(observer);
        }

        private void hook(Observer<? super T> observer) {
            try{
                // 1.得到 mLastVersion
                // 获取到 LivData 的类中的 mObservers 对象
                Class<LiveData> liveDataClass = LiveData.class;
                Field mObserversField= liveDataClass.getDeclaredField("mObservers");
                mObserversField.setAccessible(true);
                // 获取到这个成员变量的对象
                Object mObserversObject = mObserversField.get(this);
                // 得到 map 对象的 class 对象
                Class<?> mObserversClass = mObserversObject.getClass();
                // 获取到 mObservers对象的 get 方法
                Method get= mObserversClass.getDeclaredMethod("get", Object.class);
                get.setAccessible(true);
                // 执行get方法
                Object invokeEntry = get.invoke(mObserversObject, observer);
                // 取到 entry 中的 value
                Object observerWraper = null;
                if(invokeEntry!=null && invokeEntry instanceof Map.Entry){
                    observerWraper = ((Map.Entry)invokeEntry).getValue();
                }
                if(observerWraper == null){
                    throw new NullPointerException("observerWraper is null");
                }
                // 得到 observerWraperr 的类对象
                Class<?> supperClass = observerWraper.getClass().getSuperclass();
                Field mLastVersion = supperClass.getDeclaredField("mLastVersion");
                mLastVersion.setAccessible(true);
                // 得到 mVersion
                Field mVersion= liveDataClass.getDeclaredField("mVersion");
                mVersion.setAccessible(true);
                // mLastVersion = mVersion
                Object mVersionValue = mVersion.get(this);
                mLastVersion.set(observerWraper, mVersionValue);

            } catch(Exception e) {

            }
        }
    }
}

好了,LiveData 比较简单,今天就讲到这里吧~

欢迎三连

来到来了,点个关注点个赞吧,你的支持是我最大的动力

自举电容的工作原理-3句话总结 1、一道面试题 照例,先抛出来一道面试题:“Buck电路的SW引脚通常会放置一颗小电容,这颗电容有什么用?”。这个问题问得比较细,非常考验被面试者的硬件基本功。 2、似懂非懂的答案 有小伙伴可能不太清楚这颗电容的作用,回答不上来。也有小伙伴脱口而出:自举电容,用来做升压的。但后面好像说不出来具体逻辑。 没具体了解过,答不上来,也没啥可惜的。但是了解过一点,但又似懂非懂的这种,给出的答案只是轻轻点水或者千篇一律,那无疑是错过了一个加分项。 3、异步Buck的自举环路组成 既然是针对Buck电路讲自举电容,前面 阅读详情

相关推荐

GNSS/INS松组合导航实战:如何用卡尔曼滤波提升定位精度(附MATLAB代码)

本文深入探讨了GNSS/INS松组合导航的工程实践,详细阐述了如何利用卡尔曼滤波算法融合卫星与惯性数据以提升定位精度。内容涵盖从仿真环境搭建、误差状态卡尔曼滤波器(ESKF)设计,到状态与观测方程实现、噪声参数调试及常见问题排查的全过程,并提供了可直接运行的MATLAB代码,为开发高精度、鲁棒的组合导航系统提供实战指南。

weixin_29057163的博客 161

如何应对 Android 面试官 -> 玩转 Jetpack ViewModel

本文介绍了Android开发中ViewModel的基础使用和原理。ViewModel作为数据容器,可以在配置变化时保持数据稳定,减轻Activity负担,并在多个组件间共享数据。文章通过一个拨号键盘Demo展示了ViewModel结合LiveData、DataBinding和Lifecycle的使用方式:继承ViewModel类存储数据,通过ViewModelProvider获取实例,在布局文件中绑定数据,实现数据持久化和界面更新。Demo还演示了ViewModel如何保存拨号状态,并通过系统API实现拨号

IT_Android的博客 1107

Ubuntu本地部署CodeX编程助手:CLI配置全指南

本地AI编程助手是当前开发者关注的核心技术趋势,指在不依赖云端API、保障代码隐私前提下,于本地环境运行具备代码理解、多语言补全与知识库检索能力的智能辅助工具。其技术原理涉及模型加载、API网关编排、向量数据库集成及系统级资源调度,关键价值在于可控性、低延迟与合规性。典型应用场景包括企业内网DevOps提效、边缘设备嵌入式开发支持,以及敏感代码场景下的离线编程增强。本文聚焦Ubuntu系统下基于CLI的手动配置路径,深入解析Ollama服务注册、Node.js版本锁定、API Key环境隔离及Qwen2.5

weixin_26804345的博客 592

如何应对 Android 面试官 -> 玩转 Jetpack Room

Room: Android官方ORM框架简化SQLite操作 摘要:Room是Android Jetpack中的官方ORM框架,相比传统SQLiteOpenHelper具有编译期SQL检查、减少模板代码、API友好等优势。使用Room涉及三个核心组件:Entity(数据表实体)、DAO(数据访问对象)和Database(数据库入口)。通过注解方式声明Entity类定义表结构,使用@Dao接口定义CRUD操作,@Database抽象类整合管理。Room结合APT技术自动生成实现类,极大简化了SQLite操作流

IT_Android的博客 1120

RK3568笔记二:部署手写数字识别模型

部署手写数字识别模型,使用手写数字识别(mnist)数据集训练了一个 LENET 的五层经典网络模型。Lenet是我们的深度学习入门的必学模型,是深度学习领域中的经典卷积神经网络(CNN)架构之一。过程分为:训练,导出ONNX,转化RKNN,测试。

1169

39、低功耗处理器性能评估与Cortex - M23/M33低功耗特性解析

本文深入解析了Cortex-M23和Cortex-M33处理器的低功耗特性及其性能评估方法。介绍了ULPMark、CoreMark、IoTMark-BLE和SecureMark-TLS等基准测试在能效评估中的应用,并详细阐述了处理器的睡眠模式、系统控制寄存器(SCR)配置、WFI/WFE指令、退出时睡眠、挂起时发送事件(SEVONPEND)以及唤醒中断控制器(WIC)等关键低功耗机制。结合状态保留技术,展示了如何通过芯片级设计大幅降低泄漏电流,实现高效能低功耗的物联网与嵌入式系统设计。

Pepper的专栏 129

Android 14+ 模拟器抓包实录:把 Burp CA 装成系统 CA,让 targetSdk≥34 的 App 乖乖解密

本文记录了在 Android 14+ 环境下,通过将 Burp CA 安装为系统信任锚,成功抓取 targetSdk=35 App 的 HTTPS 明文流量的完整实操过程。针对“浏览器可用、App 不可用”的典型现象,精准识别出根本原因是系统级证书信任策略变更(用户安装的 CA 被屏蔽),而非 SSL Pinning。仅通过 adb 命令将 CA 推送至系统证书存储并重启生效,无需 Frida、Objection 或修改 APK,实现零侵入解密。全文命令均基于 Windows + MuMu 模拟器环境实测通

JIANqiao19931029的博客 341

使用Transformer实现自动调制识别(RML2018)

image_size = 1024 # resize重置输入大小patch_size = 16 # 从输入图片中将被提取的块大小# 分成 num_patches 个块num_patches = (image_size // patch_size) #(72/6)**2 横着裁剪),],return x})

QAQIknow的博客 1321

具身智能 2026:启元人形机器人开发者生态的“安卓时刻”来了吗

一个贯穿全文的问题:人形机器人开发者生态,是否正从「封闭垂直」走向「开源平台化」?本文以开源底座的公开数据为标尺,盘点 SDK、URDF、仿真与技能分发的开放程度,并落回启元机器人的开源路径。所有关键数据均标注来源,文末附社区与学堂链接。

在北风吹起的时候加入我们的队伍 在秋雨到来的时候成为彼此的温暖 743

YOLO模型轻量化实战:剪枝、量化与知识蒸馏三大技术落地详解

这篇文章结合我自己的实战经验,从原理、操作、效果、避坑四个方面,给大家讲清楚了YOLO模型轻量化的三大核心技术:剪枝、量化、知识蒸馏。先明确应用场景,再选择合适的技术组合,不要盲目追求极致压缩,要在“大小、速度、精度”之间找平衡。实战才是最好的学习方式——大家可以先拿自己的数据集试试剪枝和量化,感受一下效果,有问题可以在评论区留言。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 592

PyTorch深度学习实战(36)——Pix2Pix详解与实现

Pix2Pix 是基于生成对抗网络的图像转换框架,能够将输入图像转换为与之对应的输出图像,能够广泛用于图像到图像转换的任务,如风格转换、图像修复、语义标签到图像的转换等。Pix2Pix 的核心思想是通过对抗训练将输入图像和目标输出图像进行配对,使生成网络可以学习到输入图像到输出图像的映射关系。在本节中,将学习使用 Pix2Pix 根据给定轮廓生成图像。

盼小辉丶的博客 1万+

android-ad-cleaner:android免 Root 批量关闭系统开屏与营销推送

橘子,计算机视觉方向工程师,深耕 AIGC 与多媒体处理领域,专注技术实用化落地。本文为原创内容,转载请注明来源与作者。优快云 首发 | 版权所有 © 2026。

下载资源或查看教程前,请务必阅读置顶的《免责声明》 496

高速PCB设计中的一些常用规则

目前,高速PCB产品设计中,高速接口的应用日益普及,对于复杂的设计而言,高速走线就如同“蜘蛛网”一样密布在PCB各处,因此,在进行PCB检查及评审时,必要的高速PCB设计经验法则及规则是工程师开展工作必不可少的知识储备。

一只豌豆象的博客 3413

Android 14 SELinux策略深度解析

init 的全局公开约束(如 neverallow init execute_no_trans,放 public 才能约束所有分区的规则)这就是"哪些可见"的强制机制。alps_s0(MTK 工程)的 device/mediatek/sepolicy/basic/ 提供。所有分区(system/system_ext/product/vendor)都可引用。init的公开约束、跨分区共用的 type/attribute/neverallow。——vendor 的 .cil 只能引用 plat 的。

新时代农民工 102

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Android 开发入门实践(二):Kotlin 空安全,从可空类型到安全访问

延续 NewsApp 项目中的新闻标题练习,记录 Kotlin 安全调用。

Easyhood的博客 276

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

Android 模块化收口:能力组合框架、手写 LifecycleOwner 与热修复共存

二十多个模块业务高度相似却各有差异,核心矛盾在于“统一与灵活”的平衡。通过角色接口组合(如IProjectAware、IVoiceAware)实现能力按需注入,新增功能零侵入;用模板方法锁定生命周期流程,确保顺序一致;结合@CallSuper与工厂方法,既约束关键逻辑,又保留子类自由。最终实现:骨架统一、填空自由,管得住业务,又不侵入业务。

mmsx990的博客 395

霍尼韦尔SoftMaster使用说明

霍尼韦尔SoftMaster使用说明

Android Message源码阅读

本文梳理了Android Handler机制中Message的核心设计:链表结构、消息复用池(sPool)及回收机制,重点解析了异步消息与同步屏障(SyncBarrier)的协同机制。当主线程需优先处理UI绘制等高优先级任务时,系统通过postSyncBarrier插入屏障,使Looper跳过普通同步消息,优先执行标记为异步的UI渲染任务,确保帧率流畅。该机制是保障Android界面响应性的关键技术之一。

u012116089的博客 182

Android中Gradle网络下载超时、更改存放位置(解决方案)

在国内开发Android应用,需要使用到一些外网才能正常访问的环境依赖。除了挂梯子,这里再提供几个常见问题的配置方案,可以已定程度减少下载超时问题。但是要注意,不同的开发环境、网络环境、时间段,出现的问题都是不可控的,遇到配置问题,建议保持耐心,多做尝试。

indeedes的博客 122

安卓部署Linux性能实测:一颗移动处理器,能否胜任日常 Linux 开发?

本文通过在一加Ace5上部署NimoteCode的ARM64 Linux环境,测试了其在CoreMark、SQLite编译及并行构建中的表现。结果显示,尽管单核性能约2.34GHz,多核扩展比达6.15×,但优化编译耗时仍高达35秒,反映移动处理器虽具备一定开发能力,却受限于文件系统开销与运行环境(如PRoot)。实际开发中,非优化构建效率较高,而并行编译可显著提速。结论:安卓手机可作为轻量级开发辅助工具,但无法替代桌面环境,真正价值在于便携性与即时响应,而非全功能替代。

一个不知名码农的专栏 282

Android 开发入门实践(九):把新闻数据与页面状态交给 ViewModel

本文记录第110—117课学习成果:将页面标题、新闻列表及选中编号逐步迁移至NewsViewModel,通过ViewModelProvider获取实例并读取数据。页面调用ViewModel方法处理选择与返回,实现状态管理解耦。过程中纠正了对Activity重建机制的误解,并厘清mutableStateOf的可观察性与ViewModel对象生命周期的区别,深化了对Compose状态更新机制的理解。

Easyhood的博客 203

CTF 命令执行题学习笔记:终端特殊字符 $IFS 与 %09 的用法(HelloCTF RCE-labs)

空格被过滤时:用%09(TAB)或$IFS代替空格关键词被过滤时:用通配符?或字符类[f]拼出被禁的词,让 WAF 在原始字符串里看不到它,shell 在执行时再展开这类技巧在后续的命令执行题里会反复用到,建议亲手在靶机上敲一遍感受一下。

YY888888_的博客 538

Android 17 全栈性能分析关键字与源码手册

FrameTimeline 找异常帧,ATrace 找阶段,thread_state 找等待类型,ftrace 找内核原因,Simpleperf 找 CPU 热点,驱动源码找最终触发条件;任何根因结论都必须回到同一对象、同一时间窗口和同一条依赖链。

weixin_43228946的博客 362

云手机私有化部署完整方案:基于ARM服务器搭建私有安卓云集群

ARM 服务器是私有化安卓云集群最优硬件底座,原生架构匹配 Android,高密度、低损耗,非常适合企业内网场景。整套私有化方案的核心,从来不是 “跑起来安卓”,而是隔离可控、可审计、可运维、可扩容。两条路线各有取舍:开源自建灵活、门槛高;商用私有化交付开箱即用、安全能力完备,是当前企业生产环境主流方案。六边云手机私有化方案,补齐了自建方案在驱动适配、管控平台、审计合规、信创适配上的短板,适合对数据主权、内网隔离有强需求的政企、自研 App、垂直行业客户。

a598652的博客 308

Android 顶点上传:Heap 到 Direct 的零拷贝,省的是 JNI 次数

一帧数十万次putFloat的性能瓶颈源于频繁跨JNI边界,每次开销远超传输字节数。通过将写入与上传解耦:用堆内ByteBuffer高速写入,最后一次性put(writeBuf)批量传至堆外DirectByteBuffer,将数百万次边界调用压缩为一次,实现近10倍性能提升。关键在于复用堆外缓冲区、统一端序,并结合“更新-绘制”分离,仅在数据变更时上传顶点,平移缩放时零开销重绘,彻底释放CPU瓶颈。

mmsx990的博客 474

Android 系统启动与调试中的日志抓取方式全解析

│ 用户操作/系统事件 ││ │▼ ▼│ (驱动/硬件/内存管理) │ │ (App/SystemServer) ││ │ │ ││ │▼ ▼│ dmesg 命令 │ │ logd daemon ││ cat /proc/kmsg │ │ (分类存储到分区) ││ │▼ ▼│ 串口 UART │ │ logcat 命令 ││ (硬件直连,最早输出) │ │ adb logcat ││ │▼│ (汇总所有日志+系统状态) │工具最佳用途关键优势主要局限dmesg。

zhaoshuzhaoshu的专栏 256

Android 开发入门实践(十):用 Kotlin 协程完成模拟加载、取消与异常处理

本文记录第118—125课对NewsApp的协程改造:从viewModelScope.launch启动协程,逐步加入delay模拟等待、suspend函数封装、withContext(Dispatchers.IO)切换线程并观察上下文恢复机制。通过日志验证withContext完成后恢复原执行上下文(非自动回主线程),纠正了初始理解偏差。引入Job管理任务,实现加载取消与重复点击防护,结合异常处理与状态管理,完成可观察、可中断的异步流程。

Easyhood的博客 344
上一篇: 如何应对 Android 面试官 -> 玩转 JetPack Lifecycle
下一篇: 如何应对 Android 面试官 -> 玩转 JetPack ViewBinding
老A说
博客等级 码龄14年 1181粉丝 85原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值