重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
Android中有个我们熟悉又陌生的对象Context(上下文),当我们启动Activity的时候需要上下文,当我们使用dialog的时候我们需要上下文,但是上下文对象到底是个什么东西呢?
10年的大观网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。成都全网营销的优势是能够根据用户设备显示端的尺寸不同,自动调整大观建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。创新互联从事“大观网站设计”,“大观网站推广”以来,每个客户项目都认真落实执行。
在Android api当中是这样描述context对象的。
"Interface to global information about an application environment. This is an abstract class whose implementation is provided by the Android system. It allows access to application-specific resources and classes, as well as up-calls for application-level operations such as launching activities, broadcasting and receiving intents, etc."
“是一个用于实现关于应用环境的整体信息的一个接口。这是一个由安卓系统提供的抽象类并且系统有对他进行实现。它允许访问到应用特殊的资源和类,同时它也可以实现到应用级别的操作,例如:Activity的启动,广播的实现和接受intent等。”
一、Context的主要实现和继续理解
知道了context的大概描述,我们可以再继续理解Context这个神秘的对象了,首先,作为基类,肯定有其它类去实现它,主要实现了context类的类是Activity,Service,Application。他们三个类虽然都是Context的子类,但是具体的继承关系却有些不大一样:
Activity的继承关系:
Service和Application的继承关系:
可以看出我们的Context其实就是我们熟知的Activity,Service,Application。
在这3个类中,Activity的context对象和Application的context对象最容易弄混淆。
二、Context中的主要方法
知道了Context的大概描述和他的一些继承关系,我们对Context这个类有了一个大致的了解。现在可以看看在context中的一些方法,来加深对context的一个理解,有很多我们使用过的方法其实都是从Context这个类中实现而来。
我们从Android api中查看Context类,这里出现了一个非常熟悉的方法:startActivity,可以看到其实Activity中的StartActivity方法是重写了Context中的方法。
abstract void startActivity ( Intent intent)
Same as startActivity(Intent, Bundle) with no options specified.
abstract void startActivity ( Intent intent, Bundle options)
Launch a new activity.
同时context还可以访问到资源文件,获得资源文件中的信息。
abstract Resources getResources ()
Return a Resources instance for your application's package.
abstract SharedPreferences getSharedPreferences ( String name, int mode)
Retrieve and hold the contents of the preferences file 'name', returning a SharedPreferences through which you can retrieve and modify its values.
final String getString (int resId)
Return a localized string from the application's package's default string table.
final String getString (int resId, Object... formatArgs)
Return a localized formatted string from the application's package's default string table, substituting the format arguments as defined in Formatter and format(String, Object...) .
同时context不但可以开启一个activity,同时还可以开启或者关闭一个Service。
abstract ComponentName startService ( Intent service)
Request that a given application service be started.
abstract boolean stopService ( Intent service)
Request that a given application service be stopped.
访问Android Api 或者查看源码可以看到,Context中还有很多访问资源文件和程序之间互相通信的方法。
可以看出context其实就是一个应用之中的手脚,可以通过他来拿取资源文件中的资源,还可以通过他来处理Activity和Service中的一些操作,这个类就是整个程序的枢纽,负责管理整个程序的通畅运行。
我们可以通过分析一个Toast通知的源码去分析context的去向和使用,来了解context到底做了些神马操作:
public static Toast makeText(Context context, CharSequence text, int duration) {
Toast result = new Toast(context);
LayoutInflater inflate = (LayoutInflater)
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
TextView tv = (TextView)v.findViewById(com.android.internal.R.id.message);
tv.setText(text);
result.mNextView = v;
result.mDuration = duration;
return result;
}
可以看到makeText方法接受的context被用于
context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflate.inflate(com.android.internal.R.layout.transient_notification, null);
这是用于获取XML中定义的View的方法,可以看到通过外部传入的Context,在这里获得了一个View布局用于显示Toast。
public Toast(Context context) {
mContext = context;
mTN = new TN();
mTN.mY = context.getResources().getDimensionPixelSize(
com.android.internal.R.dimen.toast_y_offset);
mTN.mGravity = context.getResources().getInteger(
com.android.internal.R.integer.config_toastDefaultGravity);
}
这一行中可以看出在context又被用来获取资源文件,可以看出Toast的显示和布局都是通过context去调用系统写好的资源文件来进行实现的。
三、Activity context和Application context的区别
Activity的context和Application的context的区别在于生命周期的区别,Activity的context是依附在着Activity的生命周期的,而Application的Context的生命周期是依附在整个应用之上的。
UI 优化系列专题,来聊一聊 Android 渲染相关知识,主要涉及 UI 渲染背景知识 、 如何优化 UI 渲染 两部分内容。
《 View 绘制流程之 setContentView() 到底做了什么? 》
《 View 绘制流程之 DecorView 添加至窗口的过程 》
《 深入 Activity 三部曲(3)View 绘制流程 》
《 Android 之 LayoutInflater 全面解析 》
《 关于渲染,你需要了解什么? 》
《 Android 之 Choreographer 详细分析 》
《 Android 之如何优化 UI 渲染(上) 》
《 Android 之如何优化 UI 渲染(下) 》
优化是无止境的,Google 在 2012 年的 I/O 大会上宣布了 Project Butter 黄油计划,并且在 Android 4.1 中正式开启了这个机制。
Project Butter 主要包含两个组成部分:一个是 VSYNC,另一个是 Triple Buffering。
也正是从这时候开始,作为严重影响 Android 口碑的 UI 流畅性问题便得到了有效解决。今天我们就来聊一聊这两个组成部分的工作原理,不过在理解它们之前,需要先看下所涉及的另外两个概念:
表示屏幕在一秒内刷新画面的次数, 刷新率取决于硬件的固定参数,单位 HZ(Hz)。例如常见的 60 Hz,即每秒钟刷新 60 次。
逐行扫描
显示器并不是一次性将画面显示到屏幕上,而是从左到右边,从上到下逐行扫描显示,不过这一过程快到人眼无法察觉到变化,以 60 Hz 刷新率的屏幕为例,即 1000 / 60 ≈ 16ms。
表示 GPU 在一秒内绘制操作的帧数。例如电影采用 24 fps、Android 系统采用 60 fps,即一秒钟绘制 30 / 60 帧画面。更多内容参考《 Why 60 fps 》。
现在,刷新频率和帧速率需要一起合作,才能使图形内容呈现在屏幕上,GPU 会获取图形数据进行绘制, 然后硬件负责把图像内容呈现到屏幕上,这一过程在应用程序的生命周期内一遍又一遍的发生。
不幸的是,刷新频率和帧率并不总是能够保持相对同步,如果你的帧速率实际比刷新率快,例如帧速率是 120 fps,显示器的刷新频率为 60 Hz。此时将会发生一些视觉上的问题。
由于显示器提取画面是从左到右,从上到下逐行扫描提取图像显示,这一过程需要 16ms(1000 / 60),当 GPU 利用一块内存区域写入帧数据时,从顶部开始新一帧覆盖前一帧,并立刻输出一行内容。当屏幕刷新时,它并不知道图像缓冲区的状态,因此它从 GPU 抓取的帧并不是完整的数据。也就是它有一半的前一帧和一半的当前帧,这种情况被称之为 屏幕撕裂 。
解决该问题的方案是双缓冲,即 GPU 和显示器都有各自的工作缓冲区。GPU 始终将完成的一帧绘制数据写入到 Back Buffer,而显示器使用 Frame Buffer。当屏幕刷新时,Frame Buffer 并不会发生变化。Back Buffer 根据屏幕的刷新将数据 copy 到 Frame Buffer,这便是 VSYNC 的用武之地。
在 Android 4.1 之前,Android 使用双缓冲机制。怎么理解呢?一般来说,同一个 View Hierarchy 内的 View 都会共用一个 Window,也就是共用一个 Surface。
而每个 Surface 都会有一个 BufferQueue 缓存队列,但是这个队列会由 SurfaceFlinger 管理,通过匿名共享内存与 App 应用层交互。
整个流程如下:
Android 一直使用 VSYNC 来阻止屏幕撕裂,对于 Android 4.0,CPU 可能会因为在忙其他的事情,导致没来得及处理 UI 绘制。所以从 4.1 开始 VSYNC 则更进一步,VSYNC 脉冲现在用于开始下一帧的所有处理。
VSYNC 类似于时钟中断,每收到 VSYNC 中断,CPU 会立即准备 Buffer 数据,由于大部分显示设备刷新频率都是 60Hz(一秒刷新 60 次),也就是说一帧数据的准备工作都要在 16ms(1000/60≈16)内完成。
这样应用总是在 VSYNC 边界上开始绘制,而 SurfaceFlinger 总是在 VSYNC 边界上进行合成。这样便可以消除卡顿,并提升图形的视觉表现。
如果理解了双缓冲机制的原理,那就非常容易理解什么是三缓冲区了。如果只有两个 Graphic Buffer 缓存区 A 和 B,如果 CPU/GPU 绘制过程较长,超过了一个 VSYNC 信号周期,因为缓冲区 B 中的数据还没有准备完成,所以只能继续展示 A 缓冲区的内容,这样缓冲区 A 和 B 都分别被显示设备和 GPU 占用,CPU 则无法准备下一帧的数据。
如果再提供一个缓冲区,CPU、GPU 和显示设备都能使用各自的缓冲区工作,互不影响。简单来说,三缓冲机制就是在双缓冲机制基础上增加了一个 Graphic Buffer 缓冲区,这样可以最大限度利用空闲时间,带来的坏处是多使用了一个 Graphic Buffer 所占用的内存。
从图中可以看出,缓冲区 B 花费的时间太长,并且正在使用 A 来显示当前帧。不过这次,系统不是在重复的缓冲区中浪费时间,而是创建一个 C 缓冲区,并开始处理下一帧。三重缓冲降低了 jank 的进一步加剧。
三缓冲并不总是存在,通常情况下仅运行一个双缓冲区,但是当发生延迟等情况时,第三个缓冲区就会出现,以便降低延迟的加剧。对于 VSYNC 信号和 Triple Buffering 更详细的介绍,可以参考《 Project Butter - How it works and What it added? 》。
Android 渲染框架非常庞大,而且演进的非常快。感兴趣的朋友可以进一步阅读下面的参考资料。
本文主要记录使用单例模式的几种形式,并分析各自的优缺点。使用单例模式可以避免重复创建对象,以此来节省开销,首先了解一下单例模式的四大原则:
常用的单例模式有:饿汉模式、懒汉模式、双重锁懒汉模式、静态内部类模式、枚举模式,我们来逐个解释这些模式的区别。
关于 volatile 修饰符,又是一个内容,需要理解:
参考(有例子,比较好理解): ,
静态内部类单例模式的优点:
那么有人会问了,如果有多个线程同时访问 getInstance() 方法,会多次初始化类,然后创建多个对象吗?答案是不会的,这我们需要了解一下类的加载机制:
虚拟机会保证一个类的clinit()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的clinit()方法,其他线程都需要阻塞等待,直到活动线程执行clinit()方法完毕。
所以如果一个类的clinit()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但线程唤醒之后不会再次进入clinit()方法。因为在同一个加载器下,一个类只会初始化一次。)
所以静态内部类单例模式不仅能保证线程的安全性、实例的唯一性、还延迟了单例的实例化。
但是静态内部类单例模式也有一个 缺点 ,就是无法传递参数。因为它是通过静态内部类的形式去创建单例的,所以外部就无法传递参数进去。
枚举单例模式占用的内存是静态变量的两倍,所以一般都不使用enum来实现单例。
单例有饿汉模式、懒汉模式、双重锁懒汉模式、静态内部类模式、枚举模式这几种形式。
饿汉模式在初始化类时就创建了对象,容易造成资源浪费;懒汉模式在多线程环境下有风险;枚举模式占用内存过高。这三种模式都有明显的弊端,所以一般不去采用。
双重锁懒汉模式使用了 volatile 修饰符,在性能上会差一点点;静态内部类模式无法传递参数。但是这两种方式都能保证实例的唯一性,线程的安全性,也不会造成资源的浪费。所以我们在使用单例模式时,可以在这两种方式中酌情选择。
参考文章:
架构究竟是什么?如何更好的理解架构?
我们知道一个APP通常是由class组成,而这些class之间如何组合,相互之间又如何产生作用,就是影响这个APP的关键点。
细分的话我们可以将其分为类、接口、任务流。
我们在进行架构设计的时候,通常具有一定的目的性,用一句话来概括就是: 架构设计的真正目的是为了解决软件系统的复杂度带来的问题, 所谓高性能、高可用、高扩展。
我们将其大致可以分为:易扩展、易维护、可定制、可伸缩
现在我们在进行设计的时候,一般都会有要求 高内聚、低耦合 ,以此来保证APP的高质量
为了方便大家理解,这边举个栗子:
低内聚,高耦合:
高内聚,低耦合:
大家觉得谁更好维护?更容易调整?出错了更容易排查?
我们在架构设计中最本质的目的就是管理复杂度,你听过的各种思想、原则、方法大多都是为了控制复杂度而设计出来的。
像依赖注入就是项目组件解耦中非常重要的一个手段,Dagger2 和 Hilt 都是在 Android 中最主要的依赖注入框架。
依赖注入其实并不是一个很神秘的概念,往往在不经意间我们就使用了依赖注入。依赖注入应用了IOC控制反转的原理,简单来说就是在类的外部构造依赖项,使用构造器或者 setter 注入。
使用依赖注入可以为我们带来什么好处呢?
我们都知道Dagger是一个早期的依赖注入库,但确实不好用,需要配置很多东西。虽然它能很好帮我们解耦各个模块之间的强关联性,提高项目的健壮性。但其却以羞涩难懂、难用而闻名,吓退了很多的开发者。
Hilt是 Dagger2 的二次封装, Hilt 本质上是对 Dagger 进行场景化 。是一个功能强大且用法简单的依赖注入框架,同时也可以说是近期 Jetpack 家族中最重要的一名新成员。但Hilt涉及的知识点也是相当繁多,即使它将 Dagger2 的用法进行了大幅的简化,如果你之前对于依赖注入完全没有了解,直接上手 Hilt 还是会有不少的困难。
在这里问大家几个问题,看看能不能回答上来:
说了这么多,那么我们如何学习Hilt,将IOC技术融入进我们的架构设计中呢?
为了帮助大家站在高级工程师的角度,深度理解IOC技术在移动端的实战应用,同时掌握移动端流行IOC框架Hilt与Dagger2的实战应用与实现原理。
在这里分享一份由大佬亲自收录整理的 学习PDF+架构视频+面试文档+源码笔记 , 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料
这些都是我现在闲暇时还会反复翻阅的精品资料。里面对近几年的大厂面试高频知识点都有详细的讲解。相信可以有效地帮助大家掌握知识、理解原理,帮助大家在未来取得一份不错的答卷。
当然,你也可以拿去查漏补缺,提升自身的竞争力。
真心希望可以帮助到大家,Android路漫漫,共勉!
如果你有需要的话,只需 私信我【进阶】即可获取