Java并发编程-Android的UI框架为什么是单线程的

Java并发编程-Android的UI框架为什么是单线程的? 众所周知,Android 会在ViewRootImpl中调用checkThread方法检测是否是在 UI 线程中更新 阅读详情

前言

众所周知,Android 会在 ViewRootImpl 中调用 checkThread 方法检测是否是在 UI 线程中更新 UI

// ViewRootImpl.java

final Thread mThread;

public ViewRootImpl(Context context, Display display) {
    mThread = Thread.currentThread();
}

void checkThread() {
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
            "Only the original thread that created a view hierarchy can touch its views.");
    }
}

为什么 Android 只能在 UI 线程中更新 UI,不能在子线程中更新 UI1?Android 为什么不使用多线程更新 UI呢?

GUI 框架为什么是单线程的2

早期的 GUI 应用程序都是单线程的,并且 GUI 事件在 ”主事件循环“ 中进行处理。当前的 GUI 框架则使用了一种略有不同的模型:在该模型中创建一个专门事件分发线程(Event Dispatch Thread,EDT)来处理 GUI 事件

单线程的 GUI 框架并不仅限于在 Java 中,在 Qt、NexiStep、MacOS Cocoa、X Windows 以及其他环境中的 GUI 框架都是单线程的。许多人曾经尝试过编写多线程的 GUI 框架,但最终都由于静态条件和死锁导致的稳定性问题而又重新回到单线程的事件队列模型:采用一个专门的线程从队列中抽取事件,并将它们转发到应用程序定义的事件处理器。

在多线程的 GUI 框架中更容易发生死锁问题,其部分原因在于,在输入事件的处理过程中与 GUI 组件的面向对象模型之间会存在错误的交互。用户引发的动作将通过一种类似于 “气泡上升” 的方式从操作系统传递给应用程序:操作系统首先检测到一次鼠标点击,然后通过工具包将其转化为 “鼠标点击” 事件,该事件最终被转换为一个更高层事件(例如 “鼠标左键被按下” 事件)转发给应用程序的监听器。另一方面,应用程序引发的动作又会以 “气泡下沉” 的方式从应用程序返回给操作系统。例如,在应用程序中引发修改某个组件背景色的请求,该请求将被转发给某个特定的组件,并最终转发给操作系统进行绘制。因此,一方面这组操作将以完全相反的顺序来访问相同的 GUI 对象;另一方面又要确保每个对象都是线程安全的,从而导致不一致的锁定顺序,并引发死锁。

另一个在多线程 GUI 框架中导致死锁的原因就是 “模型 — 视图 — 控制 (MVC)” 这种设计模式的广泛应用。通过将用户的交互分解到模型、视图和控制等模块中,能极大的简化 GUI 应用程序的实现,但这却也进一步增加了出现不一致锁定顺序的风险。

单线程的 GUI 框架通过线程封闭机制来实现线程安全性。所有 GUI 对象,包括可视化组件和数据模型等,都只能在事件线程中访问。当然,这只是将确保线程安全性的一部分工作交给应用程序的开发人员来负责,他们必须确认这些对象被正确的封闭在事件线程中。

从以上文档中不难发现,早期前辈们尝试过多线程的 GUI 框架,最终都以 “失败” 告终而又回归到单线程的事件队列模型

原因大致总结为:在多线程中操作 GUI 对象,会有线程安全问题

线程安全三大恶

何为线程安全性3

要对线程安全性给出一个确切的定义是非常复杂的。定义越正式,就越复杂,不仅很难提供有实际意义的指导建议,而且也很难从直观上去理解。因此,下面给出了一些非正式的描述,看上去令人困惑。在互联网上可以搜到许多 “定义”,例如:

  • 可以在多个线程中调用,并且在线程之间不会出现错误的交互。
  • 可以同时被多个线程调用,而调用者无需执行额外的动作。

看看这些定义,难怪我们会对线程安全性感到困惑。它们听起来非常像 “如果某个类可以在多个线程中安全的使用,那么它就是一个线程安全的类”。对于这种说法,虽然没有太多的争议,但同样不会带来太多的帮助。我们如何区分线程安全的类以及非线程安全的类?进一步说,“安全” 的含义是什么?

在线程安全性的定义中,最核心的概念就是正确性。如果对线程安全性的定义是模糊的,那么就是因为缺乏对正确性的清晰定义。

正确性的含义是:某个类的行为与其规范完全一致。在良好的规范中通常会定义各种不变性条件(Invariant)来约束对象的状态,以及定义各种后验条件(Postcondition)来描述对象操作的结果。由于我们通常不会编写详细的规范,那么如何知道这些类是否正确呢?我们无法知道,但这并不妨碍我们在确信 “类的代码能工作” 后使用它们。这种 “代码可信性” 非常接近于我们对正确性的理解,因此我们可以将单线程的正确性近似定义为 “所见即所知(we know it when we see it)”。在对 “正确性” 给出一个较为清晰的定义后,就可以定义线程安全性:当多个线程访问某个类时,这个类始终都能表现出正确的行为,那么就称这个类是线程安全的。

由于单线程程序也可以看成是一个多线程程序,如果某个类在单线程环境中都不是正确的,那么它肯定不会是线程安全的。

大恶 - 可见性

可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉。4

在上个世纪的单核时代,所有的线程都在唯一的一颗 CPU 上执行,CPU 缓存与内存之间的就像两口子,你的就是我的,我的就是你的,不分彼此😂

因为只有一颗 CPU,也只有一个 CPU 缓存,所以一个线程花了大洋,对另一个线程来说,它一定能看到还剩多少大洋。例如在下面的图中,内存中一共有100大洋,如果线程A花了20大洋,线程B想再挥霍时,只能挥霍80大洋


一个线程对共享变量的修改,另外一个线程可以立刻看到,称为可见性

在21世纪的多核时代,每颗 CPU 都有自己的缓存,这时 CPU 缓存与内存之间的事就不好掰扯了,就像古代的皇帝(内存)有后宫佳丽三千(CPU),皇帝跟所有佳丽们说咱国库充足:白银100两,佳丽们都知道了国库有100两白银,皇后想花80两买面膜(操作 CPU-1 缓存),贵妃想花70两买BB霜(操作 CPU-2 缓存),过段时间皇帝一看(CPU 缓存同步到内存),国库还有30两白银。例如在下面的图中,内存有100两白银,线程A花了80两,然后同步到内存,皇帝看了国库还有20两,线程B花了70两,然后同步到内存,国库变成30两了?啧啧,这国库白银越花越多😂


这里想说明线程A对共享变量的操作对于线程B来说是不可见的

接下来用一段代码来看一下可见性问题,下面代码4中说明了当多个线程在没有同步的情况下共享数据时出现的错误。在代码中,主线程和读线程都将访问共享变量 ready 和 number。主线程启动读线程,然后将 number 设为 45,并将 ready 设为 true。读线程一直循环直到发现 ready 的值变为 true,然后输出 number 的值。虽然程序看起来会输出 45,但事实上很可能输出 0,或者根本无法终止。这是因为在代码中没有使用足够的同步机制,因此无法保证主线程写入的 ready 值和 number 值对于读线程来说是可见的。

public class VisibilityTest {

    private static boolean ready;
    private static int number;

    private static class ReaderThread extends Thread {
        @Override
        public void run() {
            while (!ready) {
                Thread.yield();
            }
            System.out.println("number = " + number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        ready = true;
        number = 45;
    }
}

二恶 - 原子性

与可见性类似,原子性也是一种复杂的属性,因为原子性中的错误也是会违背我们的直觉。

原子性:是指一个或多个指令(操作)在 CPU 执行过程中不被中断、不可分割的特性,这里强调在 CPU 指令(操作)的执行过程中,表示原子性是在 CPU 指令(操作)层面而不是语言层面

下面介绍两种常见的原子性错误形式

读取 - 修改 - 写入

接下来用一段代码看一下 “读取 - 修改 - 写入” 问题,下面代码由 Kotlin 编写,在 reduceCount 方法中循环 100000 次执行 count-- 操作,两个线程执行 reduceCount 方法,可以先想下程序运行后输出的 count 是多少?

fun main() {

    val thread1 = thread {
        reduceCount()
    }

    val thread2 = thread {
        reduceCount()
    }

    thread1.join()
    thread2.join()

    println("count = $count")
}

// 20万
var count: Long = 200000L

private fun reduceCount() {
    // 循环10万次
    for (i in 1..100000) {
        count--
    }
}

直观上感觉 count 应该是 0,但是程序输出的 count 是位于 0 至 100000 之间的随机数,这是为什么呢?

虽然递减操作 count-- 是一种紧凑的语法,使其看上去只是一个操作,但这个操作并非原子的,因而它并不会作为一个不可分割的操作来执行。

实际上,它包含了三个独立的操作:

  • 读取 count 的值
  • 将值加 1
  • 然后将第二步计算结果写入 count

对于上面的三个操作来说,count 的初始值为 200000,那么在某些情况下,两个线程读到的值都为 200000,接着执行递减操作,并且都将 count 的值设为 199999。这显然不是我们期望的结果。这种由于不恰当的执行时序而出现不正确的结果是一种非常重要的情况,这种情况称为:竞态条件 (Race Condition)

先检查后执行

先检查后执行是最常见的竞态条件类型,即通过一个可能失效的观测结果来决定下一步的动作。先检查后执行问题中常见的情况就是下面形式的代码:

// check
if (condition) {
    // action
}

下面通过延迟初始化看一下 “先检查后执行” 问题,延迟初始化的目的是将对象的初始化操作推迟到实际使用时才进行,同时要确保只被初始化一次3。

public class LazyInitRace {
    
    private LazyInitRace instance = null;
    
    public LazyInitRace getInstance() {
        // 先检查instace是否已经被初始化,如果已经初始化则返回现有的实例
        if (instance == null) {
            // 否则将创建一个新的实例,返回一个实例引用
            instance = new LazyInitRace();
        }
        
        return instance;
    }
    
    private LazyInitRace() {
    }
}

在 LazyInitRace 中包含了一个竞态条件,它可能会破坏这个类的正确性。假定线程 A 和线程 B 同时执行 getInstance。线程 A 看到 instance 为空,因而创建一个新的 LazyInitRace 实例。线程 B 同样需要判断 instance 是否为空。此时的 instance 是否为空,要取决于不可预测的时序,包括线程的调度方式,以及线程 A 需要花多长时间来初始化 LazyInitRace 并设置 instance。如果当线程 B 检查时,instance 为空,那么在两次调用 getInstance 时可能会得到不同的结果,即使 getInstance 通常被认为是返回相同的实例。3

例如在下图中,线程A 执行完 “检查” 阶段后做线程调度(切换),线程 A 和线程 B 按图中序列执行,最终发现两个线程都创建了一个新的 LazyInitRace 实例,但这不是期望的结果。

三恶 - 有序性

在可见性章节中的示例代码中有一种情况:number 很可能输出 0,因为读线程可能看到了写入 ready 的值,但却没有看到之后写入 number 的值,这种现象被称为 “重排序 (Reordering)”。

在没有同步的情况下,编译器、处理器以及运行时等都有可能对操作的执行顺序进行一些意想不到的调整。在缺乏足够同步的多线程程序中,要想对内存操作的执行顺序进行判断,几乎无法得到正确的结论。

public class VisibilityTest {

    private static boolean ready;
    private static int number;

    private static class ReaderThread extends Thread {
        @Override
        public void run() {
            while (!ready) {
                Thread.yield();
            }
            System.out.println("number = " + number);
        }
    }

    public static void main(String[] args) {
        new ReaderThread().start();
        ready = true;
        number = 45;
    }
}

有序性指的是程序按照代码的先后顺序执行

下面再来个代码示例5来说明有序性问题,在程序 PossibleReordering 中说明了5,在没有正确同步的情况下,即使要推断最简单的并发程序的行为也很困难。很容易想象 PossibleReordering 是如何输出 (1, 0) 或 (0,1) 或 (1,1) 的:线程 A 可以在线程 B 开始之前就执行完成,线程 B 也可以在线程 A 开始之前执行完成,或者二者的操作交替进行。但奇怪的是,PossibleReordering 还可以输出 (0,0) 。由于每个线程中的各个操作之间不存在数据流依赖性,因此这些操作可以乱序执行。(即使这些操作按照顺序执行,但在将缓存刷新到主内存的不同时序中也可能出现这种情况,从线程 B 的角度看,线程 A 中的赋值操作可能以相反的次序执行。)

public class PossibleReordering {
    private static int x = 0;
    private static int y = 0;
    
    private static int a = 0;
    private static int b = 0;

    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            a = 1;
            x = b;
        });
        
        Thread thread2 = new Thread(() -> {
            b = 1;
            y = a;
        });
        
        thread1.start();
        thread2.start();
        
        thread1.join();
        thread2.join();

        System.out.println("( " + x + ", " + y + " )");
    }
}

下图给出了一种可能由重排序导致的交替执行方式,在这种情况中会输出 (0,0) 。

总结

本文从多线程并发编程中的线程安全角度解读了 Android UI 框架为什么是单线程的,猜想 Android UI 框架设计人员也是汲取了前人多线程中线程安全问题,遂采取单线程的封闭机制来实现线程安全性。

说明与参考文献


  1. 在子线程中也能更新UI,这里不抬杠 ↩︎

  2. 摘自《Java并发编程实战-第九章》 ↩︎

  3. 摘自《Java并发编程实战-第二章》 ↩︎ ↩︎ ↩︎

  4. 摘自《Java并发编程实战-第三章》 ↩︎ ↩︎

  5. 摘自《Java并发编程实战-第十六章》 ↩︎ ↩︎

Android Studio安卓学习笔记(三)Android用户界面的设计布局与组件(一)用户界面布局设计(1)... 当我们创建了一个安卓项目后,我们会发现真正建立一个完善的安卓项目并不是想象的那么容易。其实和设计GUI可视化界面一样,开发安卓也需要考虑很多方面,主要考虑的还是界面布局和需要的组件。 一:Android用户界面布局管理 Android系统按照MVC(模型(model)-视图(view)-控制器(controller))设计模式将应用程序的界面设计与功能控制设计分离,类似于... 阅读详情

相关推荐

使用UI Automator Viewer工具来分析和查看Android用户界面

通过使用UI Automator Viewer,您可以捕捉应用程序的界面截图,并查看元素的属性以及它们在布局中的层次结构。通过使用UI Automator Viewer,您可以捕捉应用程序的界面截图,并查看元素的属性以及它们在布局中的层次结构。您可以在左侧的"Device Screenshots"窗格中查看屏幕截图,而右侧的"UI Automator Dump"窗格则显示了界面布局的分层结构。捕捉界面截图:在UI Automator Viewer界面中,点击工具栏上的相机图标,捕捉当前设备屏幕的截图。

WwxActionscript的博客 1544

一篇特别好的描述“为什么几乎所有的框架都只能在专门的UI线程中处理UI事件而不能使用多线程处理”的博客

多线程GUI工具包是一个失败的梦

qq_28107929的博客 205

面试题:Android 为什么设计只有主线程更新UI

选择方案的选择 单线程更新UI 多线程更新UI 从问题本身考虑就两个方案不是单线程就是多线程。 下面是个人根据具体情况分析,如果有什么不对的地方,欢迎指正。 从开发效率来看,开发一个 单线程UI 库,会显得非常容易,而且,每个控件的运行效率肯定会比多线程的效率高,比如我们单线程可以使用HashMap,多线程就需要使用JUC 框架下的类库了,这个效率肯定比 HashMap低很多,这样就很好理解。编写一个多线程的UI库,很可能每个控件,都会加锁,控件本身效率就低了,但是这样还不够 ,后面会解释。 还有一

1 1582

UI控件为什么不能使用多线程

1.MFChttp://blog.youkuaiyun.com/xsckernel/article/details/51436572http://blog.youkuaiyun.com/xbmoxia/article/details/16981243PS:在MFC主线程里创建子线并传入this指针,在子线程操作控件不是线程安全的,避免这么做。正确做法是向子线程传入m_hWnd句柄参数,自建消息,使用Postmessage

koln频道 949

Android语言基础教程(40)Android用户界面设计中的控制UI界面之使用XML和Java代码混合控制UI界面:别让UI拖后腿!Android布局“混搭”秘籍:XML稳如老狗,Java浪到飞起

想摆脱Android界面开发中的选择困难症吗?这份指南揭秘XML与Java代码混合控制UI的实战技巧。通过“装修房子”的生动类比,你将掌握如何用XML打好布局地基,再用Java实现动态交互,完整示例带你打造一个会“说话”的按钮组合。256字带你解锁界面开发新姿势!

jxf_jxfcsdn的博客 1022

Android WLAN STA/AP 并发

Android 9 引入了可让设备同时在 STA 和 AP 模式下运行的功能。对于支持双频并发 (DBS) 的设备,此功能让一些新功能得以实现,例如在用户想要启用热点 (softAP) 时不会中断 STA WLAN。

华南烹鱼宴 1247

有多线程的时候不可以直接用ui

android在其他线程执行任务之后再在主线程中执行ui方法,给xml中控件赋值。会出现错误。 此时应该用handler方法。

dawn的专栏 398

从AMCL到SLAM Toolbox:在Nav2中实现高精度纯定位的实践指南

本文详细介绍了从AMCL迁移到SLAM Toolbox纯定位模式的实践指南,帮助开发者在Nav2中实现高精度定位。通过对比AMCL与SLAM Toolbox的性能差异,提供环境配置、参数调优、Nav2集成等关键步骤,显著提升机器人在动态环境中的定位精度和重定位效率。

weixin_29292535的博客 148

Java 并发系列(一):多线程三大特性

概述 多线程三大特性:原子性、可见性、有序性。 1. 原子性 原子性是指:多个操作作为一个整体,不能被分割与中断,也不能被其他线程干扰。如果被中断与干扰,则会出现数据异常、逻辑异常。 多个操作合并的整体,我们称之为复合操作。一个复合操作,往往存在前后依赖关系,后一个操作依赖上一个操作的结果。如果上一个操作结果被其他线程干扰,对于当前线程看来整个复合操作的结果便不符合预期。同理线程也不能在复合操作中间被中断,中断必须发生在进入复合操作之前或者等到复合操作结束之后。 保证原子性就是在多线程环境下,保证

TroyLemon的专栏 1720

Ubuntu18.04 SoftRoCE安装教程

目标:创建两台Ubuntu18.04虚拟机,安装SoftRoCE环境,一台作为Server,一台作为Client,模拟RDMA通信环境,熟悉RDMA的通信方式。

GGGYL111的博客 1548

HALCON卡尺模型实战:5分钟搞定工件尺寸测量(附完整代码)

本文详细介绍了HALCON卡尺模型在工件尺寸测量中的实战应用,通过5分钟快速搭建测量系统的完整指南,包括基础配置、高级参数调优和完整代码示例。卡尺模型能自动识别边缘并计算精确尺寸,显著提升工业视觉检测的效率和一致性,特别适用于实时测量场景。

weixin_42570296的博客 462

线程安全笔记

线程安全的笔记

1.01^365 224

Python遥感技术实战:NDWI与MNDWI水体提取算法对比与优化

本文详细对比了NDWI与MNDWI两种水体提取算法在Python遥感技术中的应用。通过实战代码演示,分析了二者在自然与城镇场景下的表现差异,并重点介绍了阈值优化与后处理技巧,旨在帮助读者掌握高效、精准的水体提取方法,提升遥感数据分析能力。

f6g7h8i9j的博客 498

安卓Android 架构模式及UI布局设计

界面设计与程序逻辑分离:有利于并行安卓中的架构模式 **优势:** 1. 设计可扩展的应用程序,方便进行未来的更改和更新。 2. 实现模块化设计,提高代码质量,便于测试和维护。开发,后期修改界面时不需修改程序逻辑代码。设计模式MVC、MVP、MVVM

m0_74154295的博客 3918

Steam游戏服务器攻防全景解读——如何构建游戏级抗DDoS防御体系?

当检测到TCP/UDP混合攻击时,应触发预设的熔断策略:优先保障游戏大厅服务的UDP 27015端口,临时限制匹配系统的TCP连接数。针对Steam游戏服务器特有的UDP 4380端口,需配置协议合规性检查规则:限制数据包长度不超过128字节,强制校验请求来源IP的真实性。Steam游戏服务器的防护需要平衡安全性与玩家体验,通过协议深度解析、智能流量调度和服务端加固的三维防御体系,可构建游戏行业专属的抗DDoS护城河。答:采用弹性防护方案,基础防护带宽保持50Gbps,遭遇攻击时自动扩展至T级清洗能力。

2501_91486804的博客 1453

XGBoost的简单安装及入门使用

XGBoost安装及简单入门 XGBoost支持多种操作系统,如Windows, Linux, MacOS等,并支持多种语言版本,如Python, R, Scale, Java等。XGBoost的安装方式一般有两种,一种是直接通过pip安装(适合用于Python),另外一种是通过源码编译安装 1、通过pip安装 通过pip安装Python包既简单又方便,只需执行如下的命令: pip install -i https://pypi.tuna.tsinghua.edu.cn/simple xgboost 使用

qq_41513230的博客 3万+

Android 为什么设计只有主线程更新UI?

通过分析总结几个点。1.一般UI还是要保证同一时刻只有一个线程在更新,所以效率不会更高。2.多线程更新UI实现上会复杂一些,Java的内部人员发布过文章也说过这个几乎不可实现。3.从响应速度角度分析,单线程可以设计出更好的响应速度的api4.单线程更新,也是一个被证明效果非常好的方案。从过个角度分析 Android 为什么设计只有主线程更新UI 都是最好的选择。【Android开发核心知识点笔记】【Android思维脑图(技能树)】

2401_84544461的博客 1109

转录组数据提取技巧汇总

数据整理是数据分析的前提,而数据的正确提取则是数据整理的前提。

zfyyzhys的博客 1784

【Android基础UI学习之用户界面设计基础】

android前端UI相关的用户界面设计基础知识

weixin_48495360的博客 893

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

各种Android ui 开源框架

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。下面分享的腾讯、头条、阿里、美团、字节跳动等公司2019-2021年的高频面试题全套解析,博主还把这些技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,下面只是以图片的形式给大家展示一部分。知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。

2401_84149635的博客 1402

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

Android 需要学习的UI框架

Android需要了解学习的UI框架等 UI Awesome-MaterialDesign- MaterialDesignCenter改名为Awesome-MaterialDesign,优化了布局,新增了不少库。 awesome-android-ui- ui库,非常多的ui及特效。 ChipsLibrary- 在Android EditText中实现打Tag功能。 bitmapMesh- 拉窗帘效果。 ObservableScrollView- 在滚动的视图观测滚动事件的Android...

u010259833的博客 1628

最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版

Part-E1-SDIO-Specification-Ver4.00-Final-120220.pdf:最新的sdio 4.0 spec 协议标准 带书签 高清完整PDF版,包括SDIO 接口的外设,SDIO 接口官方协议等。

android 界面组件库,Android用户界面设计:框架布局

框架布局是将控件组织在Android程序的用户界面中最简单的布局类型之一。理解布局对于良好的Android程序设计来说是非常重要的。在这个教程里,你将学到所以关于框架布局的知识,它们主要用来在屏幕上组织特别的或重叠的视图控件。使用得当的话,很多有趣的Android程序用户界面都可以基于框架布局来设计。什么是框架布局框架布局是Android开发者组织视图控件最简单和最有效的布局之一。它们使用得比其它...

weixin_31487917的博客 385

Android用户界面设计

Android用户界面设计 用户界面设计是Android应用开发的一项重要内容。在进行开发的过程中,我们需要了解UI元素如何呈现给用户,也就是如何控制UI界面。andorid提供了4种控制UI的方法。 一、控制UI布局 使用xml布局文件来控制UI 使用JAVA代码来控制UI 使用xml布局与JAVA代码来控制UI 使用自定义View来控制UI 具体的东西我们需要简单讲解:1.如何使用xml布局

人生在世 1万+

安卓Android和Java语言的异同、关系和区别

安卓Android和Java语言的异同、关系和区别,安卓Android是一种以Linux为基础的开放源码操作系统,主要使用于便携设备。2011年第一季度,安卓在全球的市场份额首次超过塞班系统,跃居全球第一。 2012年2月数据,安卓占据全球智能手机操作系统市场52.5%的份额,中国市场占有率为68.4%。自google收购安卓之后,安卓开发者更是风生水起。安卓开发前景看好,安卓手机市场大卖,安卓应

编程小丫 3万+
上一篇: Kotlin-KCP的应用-第二篇
下一篇: Kotlin-KCP的应用-修改SDK版本号
guodongAndroid
博客等级 码龄10年 16粉丝 19原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值