每日更新每日学习面试笔记,来自https://github.com/Moosphan/Android-Daily-Interview/
解决方案:将 Handler 以静态内部类的形式声明,然后通过弱引用的方式让 Handler 持有外部类 Activity 的引用,这样就可以避免内存泄漏问题了:
1.自定义的静态handler
2.可以加一个弱引用
3.还有一个主意的就是当你activity被销毁的时候如果还有消息没有发出去 就remove掉吧
4.removecallbacksandmessages去清除Message和Runnable 加null 写在生命周的ondestroy()就行
private WeakHandler weakHandler;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
weakHandler = new WeakHandler(this);
}
static class WeakHandler extends Handler {
private final WeakReference mActivity;
WeakHandler(MainActivity activity) {
mActivity = new WeakReference<>(activity); // 弱引用
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
MainActivity activity = mActivity.get();
switch (msg.what) {
case 1:
if (activity != null) {
activity.btnDemoOne.setText("ceshi");
}
break;
default:
break;
}
}
}
@Override
protected void onDestroy() {
super.onDestroy();
weakHandler.removeCallbacksAndMessages(null);
}
通常,Fragment 与 Activity 通信存在三种情形:
在Android中我们可以通过以下几种方式优雅地实现Activity和fragment之间的通信:
顺便说一下内存泄漏和内存溢出的区别
public class Singleton{
private volatile static Singleton mSingleton;
private Singleton(){
}
public static Singleton getInstance(){
if(mSingleton == null){
synchronized(Singleton.class){
if(mSingleton == null)
mSingleton = new Singleton();
}
}
return mSingleton;
}
}
6、Android 补间动画和属性动画的区别?
补间动画
补间动画,主要是向View对象设置动画效果,包括AlphaAnimation 、RotateAnimation 、ScaleAnimation 、TranslateAnimation 这4种效果,对应的xml标签分别是alpha、rotate、scale、translate。通过为动画设置初始和终止对应的值,根据插值器和duration计算动画过程中间相应的值实现平滑运动,即设置初始和终止状态,插值器来计算填补初始状态到终止状态间的动画
属性动画
属性动画可以对任何对象的属性做动画而不仅仅是View,甚至可以没有对象。除了作用对象进行扩展外,属性动画的效果也加强了,不仅能实现View动画的4中效果,还能实现其它多种效果,这些效果都是通过ValuAnimator或ObjectAnimator、AnimatorSet等来实现的。
7、ANR出现的场景及解决方案?
在Android中,应用的响应性被活动管理器(Activity Manager)和窗口管理器(Window Manager)这两个系统服务所监视。当用户触发了输入事件(如键盘输入,点击按钮等),如果应用5秒内没有响应用户的输入事件,那么,Android会认为该应用无响应,便弹出ANR对话框。而弹出ANR异常,也主要是为了提升用户体验。
解决方案是对于耗时的操作,比如访问网络、访问数据库等操作,需要开辟子线程,在子线程处理耗时的操作,主线程主要实现UI的操作
8、谈谈 Handler 机制和原理?
首先在UI线程我们创建了一个Handler实例对象,无论是匿名内部类还是自定义类生成的Handler实例对象,我们都需要对handleMessage方法进行重写,在handleMessage方法中我们可以通过参数msg来写接受消息过后UIi线程的逻辑处理,接着我们创建子线程,在子线程中需要更新UI的时候,新建一个Message对象,并且将消息的数据记录在这个消息对象Message的内部,比如arg1,arg2,obj等,然后通过前面的Handler实例对象调用sendMessge方法把这个Message实例对象发送出去,之后这个消息会被存放于MessageQueue中等待被处理,此时MessageQueue的管家Looper正在不停的把MessageQueue存在的消息取出来,通过回调dispatchMessage方法将消息传递给Handler的handleMessage方法,最终前面提到的消息会被Looper从MessageQueue中取出来传递给handleMessage方法。
问题扩展
A. messageQueue.next 是阻塞式的取消息, 如果有 delay 会调用 nativeWake;
那么问题来了, 线程挂起了, 是挂起的 UI线程吗? 答案是 YES, 为什么我没有察觉呢?
还有就是 nativeWake 和 nativePollOnce 的实现原理;
B. looper.loop 既然是 while-true 为什么不会卡死?
C. MessageQueue 是队列吗? 他是什么数据结构呢?
D. handler 的postDelay, 时间准吗? 答案是不准, 为什么呢?
E. handler 的 postDelay 的时间是 system.currentTime 吗? 答案是 NO, 你知道是什么吗?
F. 子线程run方法使用 handler 要先 looper.prepare(); 再 handler.post; 再 looper.loop();
那么问题来了, looper.loop(); 之后 在 handler.post 消息, 还能收到吗? 答案是 NO, 为什么?
G. handler 这么做到的 一个线程对应一个 looper, 答案是threadLocal, 你对ThreadLocal 有什么了解吗?
H. 假设先 postDelay 10ms, 再postDelay 1ms, 你简单描述一下, 怎么处理这2条消息?
I. 你知道主线程的Looper, 第一次被调用loop方法, 在什么时候吗? 哪一个类
J. 你对 IdleHandler 有多少了解?
K. 你了解 HandlerThread 吗?
L. 你对 Message.obtain() 了解吗, 或者你知道 怎么维护消息池吗;
/**
* 自定义广播
*/
public static final String LOGIN_ACTION = "com.archie.action.LOGIN_ACTION";
//广播接收器
private LoginBroadcastReceiver mReceiver = new LoginBroadcastReceiver();
//注册广播方法
private void registerLoginBroadcast(){
IntentFilter intentFilter = new IntentFilter(LoginActivity.LOGIN_ACTION);
LocalBroadcastManager.getInstance(mContext).registerReceiver(mReceiver,intentFilter);
}
//取消注册
private void unRegisterLoginBroadcast(){
LocalBroadcastManager.getInstance(mContext).unregisterReceiver(mReceiver);
}
/**
* 自定义广播接受器,用来处理登录广播
*/
private class LoginBroadcastReceiver extends BroadcastReceiver{
@Override
public void onReceive(Context context, Intent intent) {
//处理我们具体的逻辑,更新UI
}
}
/**
* 发送我们的局部广播
*/
private void sendBroadcast(){
LocalBroadcastManager.getInstance(this).sendBroadcast(
new Intent(LOGIN_ACTION)
);
}
小结:
1、LocalBroadcastManager在创建单例传参时,不用纠结context是取activity的还是Application的,它自己会取到tApplicationContext。
2、LocalBroadcastManager只适用于代码间的,因为它就是保存接口BroadcastReceiver的对象,然后直接调用其onReceive方法。
3、LocalBroadcastManager注册广播后,当该其Activity或者Fragment不需要监听时,记得要取消注册,注意一点:注册与取消注册在activity或者fragment的生命周期中要保持一致,例如onResume,onPause。
4、LocalBroadcastManager虽然 支持对同一个BroadcastReceiver可以注册多个IntentFilter,但还是应该将所需要的action都放进一个 IntentFilter,即只注册一个IntentFilter,这只是我个人的建议。
5、LocalBroadcastManager所发 送的广播action,只能与注册到LocalBroadcastManager中BroadcastReceiver产生互动。如果你遇到了通过 LocalBroadcastManager发送的广播,对面的BroadcastReceiver没响应,很可能就是这个原因造成的。
/**
* 双重检查
*/
public class SingletonDoubleCheck {
private SingletonDoubleCheck() { }
private static volatile SingletonDoubleCheck instance;//代码1
public static SingletonDoubleCheck getInc() {
if (null == instance) {//代码2
synchronized (SingletonDoubleCheck.class) {
if (null == instance) {//代码3
instance = new SingletonDoubleCheck();//代码4
}
}
}
return instance;
}
}
/**
* 静态内部类实现单例
*
*/
public class SingleDemo4 {
private static SingleDemo4 instance;
private static class SingleDemo4Holder {
private static final SingleDemo4 instance = new SingleDemo4();
}
private SingleDemo4() {
if (instance != null) {
throw new RuntimeException();
}
}
/**
* 调用这个方法的时候,JVM才加载静态内部类,才初始化静态内部类的类变量。由于由JVM初始化,保证了线程安全性,
* 同时又实现了懒加载
* @return
*/
public static SingleDemo4 getInstance() {
return SingleDemo4Holder.instance;
}
}
更多详细单例见单例的五种实现方式,及其性能分析
在代码 在多线程中 两个线程可能同时进入代码2, synchronize保证只有一个线程能进入下面的代码,
此时一个线程A进入一个线程B在外等待, 当线程A完成代码3 和代码4之后,
线程B进入synchronized下面的方法, 线程B在代码3的时候判断不过,从而保证了多线程下 单例模式的线程安全,
另外要慎用单例模式,因为单例模式一旦初始化后 只有进程退出才有可能被回收,如果一个对象不经常被使用,尽量不要使用单例,否则为了几次使用,一直让单例存在占用内存。
接着上一篇Android 面试题笔记(一)
13、Window和DecorView是什么?DecorView又是如何和Window建立联系的?
DecorView的作用
DecorView是顶级View,本质就是一个FrameLayout
包含了两个部分,标题栏和内容栏
内容栏id是content,也就是activity中setContentView所设置的部分,最终将布局添加到id为content的FrameLayout中
获取content:ViewGroup content = findViewById(R.android.id.content)
获取设置的View:content.getChidlAt(0)
Window是什么?
表示一个窗口的概念,是所有View的直接管理者,任何视图都通过Window呈现(单击事件由Window->DecorView->View; Activity的setContentView底层通过Window完成)
Window是一个抽象类,具体实现是PhoneWindow
创建Window需要通过WindowManager创建
WindowManager是外界访问Window的入口
Window具体实现位于WindowManagerService中
WindowManager和WindowManagerService的交互是通过IPC完成
DecorView又是如何和Window建立联系的?
在Activity的启动流程中,处理onResume()的相关方法中,将DecorView作为Window的成员变量保存到Window内部
DecorView与Window建立联系又有什么用呢?例如Activity的onSaveInstanceState()进行数据保存时,就通过window内部的DecorView触发整个View树进行状态保存
//ActivityThread.java
final void handleResumeActivity(IBinder token, …) {
//1. 创建DecorView,设置为不可见INVISIBLE
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
//2. 获取到WindowManager, addView方法将DecorView添加到Window中
ViewManager wm = a.getWindowManager();
wm.addView(decor, l);
//3. 将DecorView设置为visible
r.activity.makeVisible();
}
系统层的:
首先屏幕是 大约16.6ms刷新一次(固定的),当界面需要改变时, CPU开始计算,将计算结果 赋予 GPU 的buffer缓存起来,等待刷新时间的到来,然后根据buffer的数据刷新界面。如果当前界面没有变化,CPU不用计算,也不会给GPU的buffer赋值啥的,这个buffer也就没变化,等到刷新时间的到来,会依旧根据buffer刷新屏幕
结论是:界面改不改变都会刷新界面,只是在于CPU是否计算这点区别
UI刷新卡顿,基本都在于卡在CPU计算这一环节,对于根据GPU 的buffer刷新这一环节,在系统里有很高的优先级
2.文件共享
优点:简单易用
缺点:不适合高并发的场景,不能做到即时通讯。
使用场景:无并发访问的情景,简单的交换数据,实时性要求不高。
3.AIDI
优点:功能强大,支持一对多并发通信,支持实时通信。
缺点:一定要处理好线程同步的问题
使用场景:一对多进行通讯,有RPC(远程过程调用协议)的需求
4.Messenger(信使)
优点:功能一般,支持一对多串行通信,支持实时通信。
缺点:不能很好的处理高并发场景,不支持RPC,数据通过Message进行传输,因此只能支持Bundle支持的数据类型。
使用场景:低并发的一对多的实时通讯,没有RPC的需求或者说没有返回结果的RPC(不调用服务端的相关方法)
5.ContentProvider
优点:主要用于数据访问,支持一对多的并发数据共享。
缺点:受约束,主要针对数据源的增删改查。
使用场景:一对多的数据共享。
6.Socket(套接字)
优点:功能强大,通过读写网络传输字节流,支持一对多的并发的实时通讯。
缺点:不支持直接的RPC(这里我也不是很明白,间接的怎么实现?)
使用场景:网络的数据交换
20、请简述一下String、StringBuffer和StringBuilder的区别?
(1)IntentFilter是和intent相匹配的,其中action,category,组成了匹配规则。同时intentFilter还可以设置优先级,其中默认是0,范围是【-1000,1000】,值越大优先级越高。并且IntentFilter多被通过AndroidManifest.xml的形式使用。
(2) 使用场景
activity的隐式启动和广播的匹配
(3)IntentFilter的匹配规则
IntentFilter的过滤信息有action,category,data.一个组件可以包含多个intent-filter,一个intent只要能完全匹配一组intent-filter即可成功的启动对应的组件。
24、回答一下什么是强、软、弱、虚引用以及它们之间的区别?
强引用是使用最普遍的引用。如果一个对象具有强引用,那垃圾回收器绝不会回收它。当内存空间不足,Java虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足的问题。
如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存(下文给出示例)。
软引用可以和一个引用队列 ReferenceQueue 联合使用,如果软引用所引用的对象被垃圾回收器回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。
弱引用可以和一个引用队列 ReferenceQueue 联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 ReferenceQueue 联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
- 25、AsyncTask的优点和缺点?
优点:使用方便,既可以执行串行任务,也可以执行并行任务
缺点:默认使用串行任务执行效率低,不能充分利用多线程加快执行速度;如果使用并行任务执行,在任务特别多的时候会阻塞UI线程获得CPU时间片,后续做线程收敛需要自定义AsynTask,将其设置为全局统一的线程池,改动量比较大
1、通知渠道 — Notification Channels
2、画中画模式 — PIP
3、自适应图标 — Adaptive Icons
4、定时作业调度
5、后台限制
6、广播限制
7、后台位置限制
8、WebView API
9、多显示器支持
10、 统一的布局外边距和内边距
11、指针捕获
12、输入和导航
13、新的 StrictMode 检测程序
14、指纹手势
15、更新的 ICU4J Android Framework API
30、Android9.0新特性?
1、室内WIFI定位
2、“刘海”屏幕支持
3、通知
4、增强体验
5、通道设置、广播以及免打扰
6、多相机支持和相机更新
7、新的图片解码
8、动画
9、HDR VP9视频,HEIF图像压缩和媒体API
10、JobScheduler中的数据成本敏感度
11、神经网络API 1.1
12、改进表单自动填充
13、安全增强
14、Android 备份加密
31、请谈谈你对 MVC 和 MVP 的理解?
1.MVC
用户首先通过View发起交互,View调用Controller执行业务逻辑,Controller修改Model,然后View通过观察者模式检测到Model的变化(具体表现形式可以是Pub/Sub或者是触发Events),刷新界面显示。
从这里可以看出,主要业务逻辑都在Controller中,Controller会变得很重。MVC比较明显的缺点:
View依赖特定的Model,无法组件化
View和Controller紧耦合,如果脱离Controller,View难以独立应用(功能太少)
2.MVP
为了克服MVC的上述缺点,MVP应运而生。在MVP中,View和Model是没有直接联系的,所有操作都必须通过Presenter进行中转。View向Presenter发起调用请求,Presenter修改Model,Model修改完成后通知Presenter,Presenter再调用View的相关接口刷新界面。这样,View就不需要监听具体Model的变化了,只需要提供接口给Presenter调用就可以了。MVP具有以下优点:
View可以组件化,不需要了解业务逻辑,只需提供接口给Presenter
便于测试:只需要给Presenter mock一个View,实现View的接口即可
- 32、谈谈Android的事件分发机制?
会经过Activity->ViewGroup->view,一次往下传递事件,如果一直不拦截再回调回来。
主要经过三个方法,dispatchTouchEvent(分发事件),oninterceptTouchEvent(是否拦截View中不存在),onTouchEvent(处理)。
三个方法的用法是,先用dispatchTouchEvent来分发事件,然后用oninterceptTouchEvent来判断是否拦截该任务(此方法在dispatchTouchEvent内部),如果不拦截直接dispatch向下回调,如果拦截就调用自己的onTouchEvent来处理事件。
如果由setOnClickListener方法会先执行onClick.
更多事件分发机制见讲讲 Android 的事件分发机制
33、谈谈ArrayList和LinkedList的区别?
ArrayList和LinkedList的大致区别:
1.ArrayList是实现了基于动态数组的数据结构,LinkedList是基于链表结构。
2.对于随机访问的get和set方法,ArrayList要优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinkedList比较占优势,因为ArrayList要移动数据。
性能上的缺点:
1.对ArrayList和LinkedList而言,在列表末尾增加一个元素所花的开销都是固定的。对 ArrayList而言,主要是在内部数组中增加一项,指向所添加的元素,偶尔可能会导致对数组重新进行分配;而对LinkedList而言,这个开销是 统一的,分配一个内部Entry对象。
2.在ArrayList集合中添加或者删除一个元素时,当前的列表所所有的元素都会被移动。而LinkedList集合中添加或者删除一个元素的开销是固定的。
3.LinkedList集合不支持 高效的随机随机访问(RandomAccess),因为可能产生二次项的行为。
4.ArrayList的空间浪费主要体现在在list列表的结尾预留一定的容量空间,而LinkedList的空间花费则体现在它的每一个元素都需要消耗相当的空间
34、handlerThread使用场景分析及原理?
当我们需要向子线程发送消息处理耗时操作时可使用handlerThread,详细使用介绍见
handlerThread使用场景分析及源码解析
第一个不同主要是历史原因。Hashtable是基于陈旧的Dictionary类的,HashMap是Java 1.2引进的Map接口的一个实现。
public class HashMap
public class Hashtable
而HashMap继承的抽象类AbstractMap实现了Map接口:
public abstract class AbstractMap
Hashtable 中的方法是同步的,而HashMap中的方法在默认情况下是非同步的。在多线程并发的环境下,可以直接使用Hashtable,但是要使用HashMap的话就要自己增加同步处理了。
Hashtable中,key和value都不允许出现null值,否则会抛出NullPointerException异常。
而在HashMap中,null可以作为键,这样的键只有一个;可以有一个或多个键所对应的值为null。当get()方法返回null值时,即可以表示 HashMap中没有该键,也可以表示该键所对应的值为null。因此,在HashMap中不能由get()方法来判断HashMap中是否存在某个键, 而应该用containsKey()方法来判断。
Hashtable、HashMap都使用了 Iterator。而由于历史原因,Hashtable还使用了Enumeration的方式 。
HashTable直接使用对象的hashCode。而HashMap重新计算hash值。
HashTable中的hash数组初始大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
在JAVA编程中,有3中典型的死锁类型:
静态的锁顺序死锁
动态的锁顺序死锁
协作对象之间发生的死锁
典型死锁例子
注意以下代码都是错误代码
1.静态的锁顺序死锁
class Test{
final Object objA = new Object();
final Object objB = new Object();
public void a(){
//注意这里 先A后B
synchronized(objA){
synchronized(objB){
//sth....
}
}
}
public void b(){
//注意这里 先B后A
synchronized(objB){
synchronized(objA){
//sth....
}
}
}
}
2.动态的锁顺序死锁
动态的锁顺序死锁是指两个线程调用同一个方法时,传入的参数颠倒造成的死锁。如下情景,一个线程调用了transferMoney(转账)方法并传入参数accountA,accountB;另一个线程调用了transferMoney方法并传入参数accountB,accountA。此时就可能发生在静态的锁顺序死锁中存在的问题,即:第一个线程获得了accountA锁并等待accountB锁,第二个线程获得了accountB锁并等待accountA锁。
3.协作对象之间发生的死锁
有时,死锁并不会那么明显,比如两个相互协作的类之间的死锁,比如:一个线程调用了A对象的a方法,另一个线程调用了B对象的b方法。此时可能会发生,第一个线程持有A对象锁并等待B对象锁,另一个线程持有B对象锁并等待A对象锁。
使用场景
equal一般比较内容相等 比如字符串相等
==一般比较数值 或者null判断
hashcode我们一般用来判断来两个对象是否相等,但这里需要注意的是 两个对象的hashcode相等,两个对象不一定相等,两个相等的对象hashcode一定相等。
我们为什么要这样判断呢?
因为判断两个对象相等重写equal的重载方法比较多,需要判断 传递性、非空性、自反性、一致性、对称性
执行startService时,Service会经历onCreate->onStartCommand。当执行stopService时,直接调用onDestroy方法。调用者如果没有stopService,Service会一直在后台运行,下次调用者再起来仍然可以stopService。
执行bindService时,Service会经历onCreate->onBind。这个时候调用者和Service绑定在一起。调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind->onDestroy。这里所谓的绑定在一起就是说两者共存亡了。
多次调用startService,该Service只能被创建一次,即该Service的onCreate方法只会被调用一次。但是每次调用startService,onStartCommand方法都会被调用。Service的onStart方法在API 5时被废弃,替代它的是onStartCommand方法。
第一次执行bindService时,onCreate和onBind方法会被调用,但是多次执行bindService时,onCreate和onBind方法并不会被多次调用,即并不会多次创建服务和绑定服务。
2、调用者如何获取绑定后的Service的方法
onBind回调方法将返回给客户端一个IBinder接口实例,IBinder允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。我们需要IBinder对象返回具体的Service对象才能操作,所以说具体的Service对象必须首先实现Binder对象。
3、既使用startService又使用bindService的情况
如果一个Service又被启动又被绑定,则该Service会一直在后台运行。首先不管如何调用,onCreate始终只会调用一次。对应startService调用多少次,Service的onStart方法便会调用多少次。Service的终止,需要unbindService和stopService同时调用才行。不管startService与bindService的调用顺序,如果先调用unbindService,此时服务不会自动终止,再调用stopService之后,服务才会终止;如果先调用stopService,此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后,服务才会自动停止。
那么,什么情况下既使用startService,又使用bindService呢?
如果你只是想要启动一个后台服务长期进行某项任务,那么使用startService便可以了。如果你还想要与正在运行的Service取得联系,那么有两种方法:一种是使用broadcast,另一种是使用bindService。前者的缺点是如果交流较为频繁,容易造成性能上的问题,而后者则没有这些问题。因此,这种情况就需要startService和bindService一起使用了。
另外,如果你的服务只是公开一个远程接口,供连接上的客户端(Android的Service是C/S架构)远程调用执行方法,这个时候你可以不让服务一开始就运行,而只是bindService,这样在第一次bindService的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是远程服务,那么效果会越明显(当然在Servcie创建的是偶会花去一定时间,这点需要注意)。
4、本地服务与远程服务
本地服务依附在主进程上,在一定程度上节约了资源。本地服务因为是在同一进程,因此不需要IPC,也不需要AIDL。相应bindService会方便很多。缺点是主进程被kill后,服务变会终止。
远程服务是独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被kill的是偶,该服务依然在运行。缺点是该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。
对于startService来说,不管是本地服务还是远程服务,我们需要做的工作都一样简单。
synchronized 可以保证原子性。他可以保证 在同一时刻,只有一个线程可以访问被 synchronized 修饰的方法,或者代码块。
volatile 不能保证原子性。当时在使用这个关键字后。当被Volatitle 修饰字段的值发生改变后,其他线程会立刻知道这个值已经发生变化了。volatitle 可以保证可见性和有序性。
优化方案1(定义一个变量l来保存一趟交换中两两交换的次数,如果l==0,则说明排序已经完成,退出for循环)
优化方案2(假如有一个长度为50的数组,在一趟交换后,最后发生交换的位置是10,那么这个位置之后的40个数必定已经有序了,记录下这位置,下一趟交换只要从数组头部到这个位置就可以了)
定义一个变量n来保存一趟交换中最后一次发生交换的位置,并把它传递给下一趟交换
/**
* 排序思想:
* 对一组数字进行从小到大或者从大到小的进行排序。
* 它是通过让相邻的两个元素进行比较,大的元素向下沉,小的元素向上冒
* arr[0]与arr[1]进行比较,如果前者大于后者,则交换位置
* 然后arr[1]与arr[2]进行比较,以此类推。当进行到n-1轮后,排序完成。
*/
import java.util.Arrays;
public class Sort {
public static void main(String[] args){
int arr[]= {100,90,101,23,13,75};
int temp=0;
for(int i=0;iarr[j+1]) {
temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
}
}
System.out.println("第["+(i+1)+"]轮,排序结果:"+ Arrays.toString(arr));
}
System.out.print("================================");
int arr2[]= {100,90,101,23,13,75};
sort2(arr2);
}
/**
* 优化思路:
* 假如在第1轮比较当中,发现所有的元素都没有进行交换,则说明此原数据就是有序的,不需要再进行排序
* @param arr
*/
public static void sort2(int arr[]){
int temp=0;
int flag=0;
for(int i=0;iarr[j+1]) {
temp=arr[j+1];
arr[j+1]=arr[j];
arr[j]=temp;
//如果有交换的行为,则flag=1
flag=1;
}
}
//说明上面 内for循环中,没有交换任何元素。
if(flag==0) {
break;
}
System.out.println("第["+(i+1)+"]轮,排序结果:"+Arrays.toString(arr));
}
}
}
确保线程互斥的访问同步代码
保证共享变量的修改能够及时可见
有效解决重排序问题。
synchronized 方法
有效避免了类成员变量的访问冲突:
private synchronized void init(){
aa = 2;
}
synchronized 代码块
这时锁就是对象,谁拿到这个锁谁就可以运行它所控制的那段代码。当有一个明确的对象作为锁时,就可以这样写程序,但当没有明确的对象作为锁,只是想让一段代码同步时,可以创建一个特殊的 instance 变量(它得是一个对象)来充当锁。
public final void a(){
synchronized (lock){
//代码
}
}
@Override
public void run() {
}
//1.创建RemoteViews实例
RemoteViews mRemoteViews=new RemoteViews("com.example.remoteviewdemo", R.layout.remoteview_layout);
//2.构建一个打开Activity的PendingIntent
Intent intent=new Intent(MainActivity.this,MainActivity.class);
PendingIntent mPendingIntent=PendingIntent.getActivity(MainActivity.this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
//3.创建一个Notification
mNotification = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(mPendingIntent)
.setContent(mRemoteViews)
.build();
//4.获取NotificationManager
manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
Button button1 = (Button) findViewById(R.id.button1);
button1.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
//弹出通知
manager.notify(1, mNotification);
}
});
逆向代码,例如反编译
与注解相结合的框架,如 Retrofit
单纯的反射机制应用框架,例如 EventBus(事件总线)
动态生成类框架 例如Gson
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* 对于任何一个类,我们都能够知道这个类有哪些方法和属性。对于任何一个对象,
* 我们都能够对它的方法和属性进行调用。
* 我们把这种动态获取对象信息和调用对象方法的功能称之为 反射机制
*/
/**
* 所谓反射其实是获取类的字节码文件,
* 也就是.class文件,那么我们就可以通过Class这个对象进行获取
*/
public class HookTest {
public static void main(String[] args) {
//第一种方式
LoopTest loopTest = new LoopTest();
Class aClass = loopTest.getClass();
System.out.println(aClass.getName());
//第二种方式
Class aclass2 = LoopTest.class;
System.out.println(aclass2.getName());
//第三种方式
try {
Class aclass3 = Class.forName("LoopTest");
System.out.println(aclass3.getName());
}catch (ClassNotFoundException ex){
ex.printStackTrace();
}
/**
* 那么这3中方式我们一般选用哪种方式呢?第一种已经创建了对象,那么这个时候就不需要去进行反射了,
* 显得有点多此一举。第二种需要导入类的包,依赖性太强。所以我们一般选中第三种方式。
*/
/**
* 三、通过反射获取类的构造方法、方法以及属性
*/
/**
* 1、获取构造方法
*/
Constructor[]constructors = aclass2.getConstructors();
System.out.println("获取构造方法:");
for (Constructor constructor1 : constructors){
System.out.println(constructor1.getName());
}
System.out.println("获取类的属性:");
Field[] fields = aclass2.getFields();
//88888
System.out.println("获取类的方法:");
Method[]methods = aclass2.getMethods();
for (Method method : methods){
System.out.println(method.getName());
}
/**
* 反射执行方法
*/
try { Class aclass4 = Class.forName("LoopTest");
Method method = aclass4.getDeclaredMethod("method",String.class);
Constructor ct = aclass4.getConstructor(null);
Object obj = ct.newInstance(null);
method.invoke(obj,"反射调用");
} catch (Exception e) {
e.printStackTrace();
}
/**
* Android中使用场景:其实很多用过的EventBus 、Retrofit 都有涉猎 可以去看看源码
*/
}
}
java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
静态编译:在编译时确定类型,绑定对象。
动态编译:在运行时确定类型,绑定对象。
反射机制的优缺点:
优点:运行期类型的判断,动态加载类,提高代码灵活度。
缺点:性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能比直接的java代码要慢很多。
ExecutorService、Callable、Future三个接口实际上都是属于Executor框架。返回结果的线程是在JDK1.5中引入的新特征,有了这种特征就不需要再为了得到返回值而大费周折了。
可返回值的任务必须实现Callable接口。类似的,无返回值的任务必须实现Runnable接口
kotlin和java都是运行在java虚拟机的语言。编译后都会生成.class文件。而虚拟机运行的正是.class文件。所以两者都可以用来写Android。再说说个人的一些看法。java作为一门相对时间长一点的语言。相对来说更万能一些。基本上能完成所有的开发场景。而且,因为时间够久,相对来说问题也很少,虽然大家都吐槽分号,类型转换,空指针这些傻瓜操作,但是我并没有觉得不写这些就能对我的开发有质的的提升,唯一让我想学kt的动力就是google的Android实例将来要用kt写。而kotlin作为一门新语言,有他自己的优点,也有一些缺点。具体什么缺点大家看下面的文章吧。
从java到kotlin,再从kotlin回归java
Error是java程序运行中不可预料的异常情况,这种异常发生以后,会直接导致JVM不可处理或者不可恢复的情况。所以这种异常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等。
其中的Exception又分为检查性异常和非检查性异常。两个根本的区别在于,检查性异常 必须在编写代码时,使用try catch捕获(比如:IOException异常)。非检查性异常 在代码编写使,可以忽略捕获操作(比如:ArrayIndexOutOfBoundsException),这种异常是在代码编写或者使用过程中通过规范可以避免发生的。 切记,Error是Throw不是Exception 。
HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
最后一点在Android 9.0 如果用http进行传输,需要在application节点下设置 android:usesCleartextTraffic=“true”
单例模式
常见应用场景:网络请求的工具类、sp存储的工具类、弹窗的工具类等
工厂模式
常见应用场景:activity的基类等
责任链模式
常见应用场景:OKhttp的拦截器封装
观察者模式
常见应用场景:Rxjava的运用
代理模式
常见应用场景:AIDL的使用
建造者模式
常见应用场景:Dialog的创建
代码修复主要有三个方案,分别是底层替换方案、类加载方案和Instant Run方案。3.1 类加载方案类加载方案基于Dex分包方案,什么是Dex分包方案呢?这个得先从65536限制和LinearAlloc限制说起。
65536限制
随着应用功能越来越复杂,代码量不断地增大,引入的库也越来越多,可能会在编译时提示如下异常:com.android.dex.DexIndexOverflowException:methodIDnotin[0, 0xffff]: 65536
这说明应用中引用的方法数超过了最大数65536个。产生这一问题的原因就是系统的65536限制,65536限制的主要原因是DVM Bytecode的限制,DVM指令集的方法调用指令invoke-kind索引为16bits,最多能引用 65535个方法。
LinearAlloc限制
在安装时可能会提示INSTALL_FAILED_DEXOPT。产生的原因就是LinearAlloc限制,DVM中的LinearAlloc是一个固定的缓存区,当方法数过多超出了缓存区的大小时会报错。为了解决65536限制和LinearAlloc限制,从而产生了Dex分包方案。Dex分包方案主要做的是在打包时将应用代码分成多个Dex,将应用启动时必须用到的类和这些类的直接引用类放到主Dex中,其他代码放到次Dex中。当应用启动时先加载主Dex,等到应用启动后再动态的加载次Dex,从而缓解了主Dex的65536限制和LinearAlloc限制。Dex分包方案主要有两种,分别是Google官方方案、Dex自动拆包和动态加载方案;
热更新 / 热修复
不安装新版本的软件直接从网络下载新功能模块对软件进行局部更新
热更新和插件化的区别
区别有两点:
1.插件化的内容在原来的App中没有,而热更新在原来 App 做了 改动
2. 插件化在代码中有固定的入口,而热更新则可能改变任何一个位置的代码
热更新的原理
classsLoder 的 dex 文件替换
直接修改字节码文件
了解热更新就必须了解 loadeClass() 的类加载过程
宏观上: loadClass() 是一个带缓存,自上而下的加载过程(即网上说的[双亲委托机制])
对于一个具体的 ClassLoader
先从自己缓存中取
自己缓存没有,就在 父 ClassLoader 要 (parent.loadClass())
父 View 没有,就自加载(findClass)
BaseDexClassLoader 或者 它的子类(DexClassLoader,PathClassLoader) 的 findClass()
通过它的 pathList.findClass()
它的 pathList .loadClass() 通过 pathClassLoader 等
所以热更新关键在于,把补丁 dex 文件加载到 Element 对象插入到 dexElement 前面才行.插入后面会被忽阅掉
正确的做法是:反射
自己用补丁创建一个PathClassLoader
2.把补丁 PathClassLoader 里面的 elments 替换到旧的里面去
注意:
1.尽早加载热更新(通用手段把加载过程放到Application.attachBaseContext())
把 补丁 PathClassLoader 里面的 elements 替换到旧的里面去
热更新下载完成后在需要时先杀死进程才能补丁生效
优化:热更新没必要把所有的内容都打过来,只要把类拿过来就行了
d8 把 指定 的 class 打包 进 dex
5.完整化: 从网上加载
再优化: 把打包过程写一个task
从ConcurrentHashMap代码中可以看出,它引入了一个“分段锁”的概念,具体可以理解为把一个大的Map拆分成N个小的HashTable,根据key.hashCode()来决定把key放到哪个HashTable中。
在ConcurrentHashMap中,就是把Map分成了N个Segment,put和get的时候,都是现根据key.hashCode()算出放到哪个Segment中:
ConcurrentHashMap中默认是把segments初始化为长度为16的数组。
总结:以上就是ConcurrentHashMap的工作机制,通过把整个Map分为N个Segment(类似HashTable),可以提供相同的线程安全,但是效率提升N倍,默认提升16倍。
`public class LruCachePhoto {
/**
* 图片 缓存技术的核心类,用于缓存下载好的所有图片,
* 在程序内存达到设定值后会将最少最近使用的图片移除掉
*/
private LruCache mMenoryCache;
public LruCachePhoto() {
//获取应用最大可用内存
int maxMemory = (int) Runtime.getRuntime().maxMemory();
//设置 缓存文件大小为 程序最大可用内存的 1/8
int cacheSize = maxMemory / 8;
mMenoryCache = new LruCache(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
/**
* 从 LruCache 中获取一张图片,如果不存在 就返回 null
*
* @param key LurCache 的键,这里是 图片的地址
* @return 返回对应的 Bitmap对象,找不到则为 null
*/
public Bitmap getBitmapFromMemoryCache(String key) {
return mMenoryCache.get(key);
}
/**
* 添加一张图片
* * @param key key
* @param bitmap bitmap
*/
public void addBitmapToCache(String key, Bitmap bitmap) {
if (getBitmapFromMemoryCache(key) == null) {
mMenoryCache.put(key, bitmap);
}
}
}`
Dalvik:是Google写的一个用于Android的虚拟机,但其严格来说并不算JVM(没有遵守Java虚拟机规范,比如其字节码格式是dex而非class)
该虚拟机在5.0时被ART替代
ART:是Android Runtime的缩写,严格来说并不是一个虚拟机,在4.4~6.0时采用安装时全部编译为机器码的方式实现,7.0时默认不全部编译,采用解释执行+JIT+空闲时AOT以改善安装耗时
ART在安卓4.4时加入,5.0取代dalvik作为唯一实现直到现在。
59、谈谈你是如何优化App启动过程的?
(1)尽量不要在Application里做耗时操作,能放子线程的放子线程,能延后初始化的延后
(2)启动页可以做成一个view在主页面加载,同时主页面的一些操作可以在这个过程中开始初始化
(3)启动页的view层级尽量简单
60、谈一谈单例模式,建造者模式,工厂模式的使用场景?如何合理选择?
(1)单例模式,一般是指将消耗内存、属性和对象支持全局公用的对象,应该设置为单例模式,如持久化处理(网络、文件等)
(2)建造者模式,一般见于开发的框架或者属性时可以直接链式设置属性,比如我们看到的AlertDialog,一般用在某些辅助类(如BRVAH的BaseViewHolder)或者开发的框架的时候方便连续设置多个属性和调用多个方法。
(3)工厂模式,一般用于业务的实体创建,在创建的过程中考虑到后期的扩展。在Android源码中比较常见的有BitmapFactoryLayoutInflater.Factory,在实体编码的过程中,比如BRVAH的多布局,如果数据类型比较多或者后期需要扩展,则可以通过工厂布局的方式,将实现MultiItemEntity
接口的实体通过工厂模式创建:
61、谈谈布局优化的技巧?
1、降低Overdraw(过度绘制),减少不必要的背景绘制
2、减少嵌套层次及控件个数
3、使用Canvas的clipRect和clipPath方法限制View的绘制区域
4、通过imageDrawable方法进行设置避免ImageView的background和imageDrawable重叠
5、借助ViewStub按需延迟加载
6、选择合适的布局类型
7、熟悉API尽量借助系统现有的属性来实现一些UI效果
8、尽量减少控件个数,对 TextView 左边或者右边有图片可是试用 drawableLeft,drawableRight
61、说一下线程的几种状态?
1.初始(NEW) ,创建线程对象
2.运行(RUNNABLE),此时就绪且正在运行一起称为运行
3.阻塞(BLOCKED),线程阻塞
4.等待(WAITING),等待中断等操作
5.超时等待(TIMED_WAITING),可以指定时间返回,不一定需要操作
6.终止(TERMINATED),线程执行完毕
62、简单介绍下ContentProvider是如何实现数据共享的?
使用 ContentProvider 可以将数据共享给其他应用,让除本应用之外的应用也可以访问本应用的数据。它的底层是用 SQLite 数据库实现的,所以其对数据做的各种操作都是以 Sql 实现,只是在上层提供的是 Uri,用户只需要关心操作数据的 uri 就可以了,ContentProvider 可以实现不同 app 之间共享。详细使用见ContentProvider跨程序共享数据(一)
63、谈谈App的电量优化?
(1)GPS
——使用要谨慎,如精确度不高可用WiFi定位或者基站定位,可用;非要用的话,注意定位数据的复用和定位频率的阈值
(2)Process和Service
——按需启动,用完就退出
(3)网络数据交互
——减少网络网络请求次数和数据量;WiFi比手机网络省电
(4)CPU
——减少I/O操作(包括数据库操作),减少大量的计算
(5)减少手机硬件交互
——使用频率优化和选择低功耗模式
(6)避免轮循。可以利用推送。如果非要轮循,合理的设置频率。
应用处于后台时,避免某些数据的传输,比如感应器,定位,视频缓存。
页面销毁时,取消掉网络请求。
限制访问频率,失败后不要无限的重连。
合理的选择定位精度和频率。
使用缓存。如果数据变化周期比较长,可以出一个配置接口,用于记录那些接口有变化。没变化的直接用缓存。
减少广播的使用频率。可以用观察者,startActivityForResult等代替。
64、Java 线程中notify 和 notifyAll有什么区别?
当线程状态为等待、超时等待会调用notify 和 notifyAll方法通知线程更改状态,此时
当线程数量为1时,notify 和 notifyAll的效果一样,会唤醒一个线程,并获取锁
当线程数量大于1时,notify会唤醒一个线程,并获取锁,notifyAll会唤醒所有线程并根据算法选取其中一个线程获取锁,区别在于此时使用notify可能会出现死锁的情况
65、谈一谈你对binder的机制的理解?
Binder机制:
1.为了保证进程空间不被其他进程破坏或干扰,Linux中的进程是相互独立或相互隔离的。
2.进程空间分为用户空间和内核空间。用户空间不可以进行数据交互;内核空间可以进行数据交互,所有进程共用一个内核空间。
3.Binder机制相对于Linux内传统的进程间通信方式:(1)性能更好;Binder机制只需要拷贝数据一次,管道、消息队列、Socket等都需要拷贝数据两次;而共享内存虽然不需要拷贝,但实现复杂度高。(2)安全性更高;Binder机制通过UID/PID在内核空间添加了身份标识,安全性更高。
4.Binder跨进程通信机制:基于C/S架构,由Client、Server、Server Manager和Binder驱动组成。
5.Binder驱动实现的原理:通过内存映射,即系统调用了mmap()函数。
6.Server Manager的作用:管理Service的注册和查询。
7.Binder驱动的作用:(1)传递进程间的数据,通过系统调用mmap()函数;(2)实现线程的控制,通过Binder驱动的线程池,并由Binder驱动自身进行管理。
8.Server进程会创建很多线程处理Binder请求,这些线程采用Binder驱动的线程池,由Binder驱动自身进行管理。一个进程的Binder线程池默认最大是16个,超过的请求会阻塞等待空闲的线程。
9.Android中进行进程间通信主要通过Binder类(已经实现了IBinder接口),即具备了跨进程通信的能力。
66、什么是线程池?如何创建一个线程池?
线程池:
1.线程池:创建多个线程,并管理线程,为线程分配任务并执行。
2.使用线程池的好处:多个线程的创建会占用过多的系统资源,造成死锁或OOM
3.线程池的作用:(1)可以复用创建好的线程,减少线程的创建或销毁的开销;(2)提高响应速度,当任务到达时,不需要等待就可以立即执行;(3)可有效控制最大并发的线程数,提高系统资源的利用率。防止死锁或OOM;(4)可以提供定时和定期的执行方式。
4.线程池参数:corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、workQueue(阻塞队列)、keepAliveTime(保活时间)、threadFactory(线程工厂,用于生成线程)。
5.线程池提交任务:有两个方法可以向线程池提交任务,分别是execute()和submit()。
execute():用于提交不需要返回值的任务,无法判断任务是否被线程执行成功。
submit():用于提交需要返回值的任务,会返回一个future类型的对象,来判断任务是否执行成功,还可以通过future的get()方法获取返回值,get()方法会阻塞当前线程直到任务完成。
5.线程池的工作流程:
(1)有新任务时,判断当前线程数是否超过corePoolSize,如果小于corePoolSize,即使有空闲线程可以执行任务,也会创建一个新的线程用来执行该任务;
(2)如果超过corePoolSize,就把任务放在workQueue(阻塞队列)中,等待被执行,前提是workQueue是有界队列;
(3)如果workQueue满了,判断当前线程数是否小于maximumPoolSize,如果小于maximumPoolSize就创建一个线程用来执行任务。
(4)如果当前线程数大于maximumPoolSize,就会执行线程池的饱和策略。
6.线程池的饱和策略:(1)默认策略:直接抛出异常;(2)用调用者所在的线程(提交任务的那个线程)执行任务;(3)丢弃阻塞队列中最靠前的任务,执行当前任务;(4)直接丢弃任务。
7.线程池的状态:
(1)RUNNING:接收提交的任务。
(2)SHUTDOWN:不再接收新提交的任务,继续处理阻塞队列中的任务。
(3)STOP:不再接收新的任务,也不会处理阻塞队列中的任务,并会终止正在执行的任务。
(4)TIDYING:所有的任务已终止,ctl记录的任务数量为0,会执行钩子函数terminated()。
(5)TERMINATED:线程池彻底终止。
8.关闭线程池的方法:ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别是shutdown()和shutdownNow()。
原理:都是循环遍历线程池的工作线程,然后依次调用线程的intercept()方法来中断线程。
shutdown():将线程池状态设置为SHUTDOWN。
shutdownNow():将线程池状态设置为STOP。
67、给View设置的透明度的三种方法
1,java代码实现
text = (TextView) findViewById(R.id.text);
text.getBackground().setAlpha(12);
setAlpha()的括号中可以填0–255之间的数字。数字越大,越不透明。
注意点:在5.0以上系统时,有些机型会出现莫名其妙的颜色值不起作用,变成透明了,也就是用此方法会导致其他共用一个资源的布局(例如:@color/white)透明度也跟着改变。
比如text用上述方法设置成透明后,项目中,其他用到text颜色值的控件,都变成透明了。
原因:在布局中多个控件同时使用一个资源的时候,这些控件会共用一个状态,例如ColorState,如果你改变了一个控件的状态,其他的控件都会接收到相同的通知。这时我们可以使用mutate()方法使该控件状态不定,这样不定状态的控件就不会共享自己的状态了。
text.getBackground().mutate().setAlpha(12);
2,在xml布局中进行设置
android:alpha的值为0~1之间的数。数字越大,越不透明。1表示完全不透明,0表示完全透明。
3,在xml布局中通过android:background设置
颜色和不透明度 (alpha) 值以十六进制表示法表示。任何一种颜色的值范围都是 0 到 255(00 到 ff)。对于 alpha,00 表示完全透明,ff 表示完全不透明。android:background的值的格式为”#AARRGGBB”。AA即透明度,R、G、B是红绿蓝三色。每一位均为0–F的十六位数。其中透明度的数值越大,越不透明
java代码
//java代码生成的对应表
for (int i = 100; i>=0; i--) {
double j = (i / 100.0d);
int alpha = (int) Math.round(255-j * 255);
String hex = Integer.toHexString(alpha).toUpperCase();
if (hex.length() == 1) hex = "0" + hex;
int percent = (int) (j*100);
System.out.println(String.format("%d%% — %s", percent, hex));
}
透明度对照表
透明度 16进制表示
100% 00(全透明)
99% 03
98% 05
97% 07
96% 0A
95% 0D
94% 0F
93% 12
92% 14
91% 17
90% 1A
89% 1C
88% 1E
87% 21
86% 24
85% 26
84% 29
83% 2B
82% 2E
81% 30
80% 33
79% 36
78% 38
77% 3B
76% 3D
75% 40
74% 42
73% 45
72% 47
71% 4A
70% 4D
69% 4F
68% 52
67% 54
66% 57
65% 59
64% 5C
63% 5E
62% 61
61% 63
60% 66
59% 69
58% 6B
57% 6E
56% 70
55% 73
54% 75
53% 78
52% 7A
51% 7D
50% 80
49% 82
48% 85
47% 87
46% 8A
45% 8C
44% 8F
43% 91
42% 94
41% 96
40% 99
39% 9C
38% 9E
37% A1
36% A3
35% A6
34% A8
33% AB
32% AD
31% B0
30% B3
29% B5
28% B8
27% BA
26% BD
25% BF
24% C2
23% C4
22% C7
21% C9
20% CC
19% CF
18% D1
17% D4
16% D6
15% D9
14% DB
13% DE
12% E0
11% E3
10% E6
9% E8
8% EB
7% ED
6% F0
5% F2
4% F5
3% F7
2% FA
1% FC
0% FF(完全不透明)
不透明度对照表
不透明度—十六进制值
100% — FF(完全不透明)
99% — FC
98% — FA
97% — F7
96% — F5
95% — F2
94% — F0
93% — ED
92% — EB
91% — E8
90% — E6
89% — E3
88% — E0
87% — DE
86% — DB
85% — D9
84% — D6
83% — D4
82% — D1
81% — CF
80% — CC
79% — C9
78% — C7
77% — C4
76% — C2
75% — BF
74% — BD
73% — BA
72% — B8
71% — B5
70% — B3
69% — B0
68% — AD
67% — AB
66% — A8
65% — A6
64% — A3
63% — A1
62% — 9E
61% — 9C
60% — 99
59% — 96
58% — 94
57% — 91
56% — 8F
55% — 8C
54% — 8A
53% — 87
52% — 85
51% — 82
50% — 80
49% — 7D
48% — 7A
47% — 78
46% — 75
45% — 73
44% — 70
43% — 6E
42% — 6B
41% — 69
40% — 66
39% — 63
38% — 61
37% — 5E
36% — 5C
35% — 59
34% — 57
33% — 54
32% — 52
31% — 4F
30% — 4D
29% — 4A
28% — 47
27% — 45
26% — 42
25% — 40
24% — 3D
23% — 3B
22% — 38
21% — 36
20% — 33
19% — 30
18% — 2E
17% — 2B
16% — 29
15% — 26
14% — 24
13% — 21
12% — 1F
11% — 1C
10% — 1A
9% — 17
8% — 14
7% — 12
6% — 0F
5% — 0D
4% — 0A
3% — 08
2% — 05
1% — 03
0% — 00(全透明)