各类乱七八糟的技术整理

都是个人的理解, 如有不对, 敬请指正, 感激不尽.


LayoutInflater

LayoutInflater.from(context)返回的其实并不是单例,Activity内的源码如下:

    @Override
    public Object getSystemService(String name) {
        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
            if (mInflater == null) {
                mInflater = LayoutInflater.from(getBaseContext()).cloneInContext(this);
            }
            return mInflater;
        }
        return getBaseContext().getSystemService(name);
    }

其中,LayoutInflater.from(getBaseContext())getBaseContext().getSystemService(name)都是取自单例map,但是LayoutInflater执行了cloneInContext(this)操作,这里面return new PhoneLayoutInflater


Android编译器演化

编译: 代码 => 字节码 => 机器码

  1. 2.2 - 4.4之间, dalvik虚拟机使用JIT进行编译, 运行较慢;
  2. 4.4之后, 由dalvik换为了ART(Android Runtime), 使用AOT方式编译, 安装时编译完成, 加快运行时速度.
  3. 7.0以后使用混合编译(有12种编译方法): 大体思路是首次运行使用类似dalvik的JIT机制, 后面根据使用情况编译对热代码进行AOT预编译.

手写Vector

这是一个三角形Vector

<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24.0"
    android:viewportHeight="24.0">
    <path
        android:fillColor="#FFF"
        android:pathData="M0,24 L24,24 12,0 Z" />
</vector>
  • android:viewportWidth/Height是绘图的图纸宽高, path中的坐标以此宽高定位.
  • android:pathData中的M是指画笔移动坐标, L是画线, 后面跟随起始点坐标, Z是指封闭图案.

点击Dialog的边缘小范围不会触发cancel的原因

看源码发现是因为存在WINDOW_TOUCH_SLOP的误差范围, 如果不需要此误差范围, 可以重写DialogonTouch方法中使用的isOutside判定.


JAVA的IO模型

  • BIO: Blocking阻塞IO, 线程死循环取数据;
  • NIO: NoBlocking非阻塞型, 线程不会死循环;
  • AIO: ASynchronize异步IO, 可以实现异步读写, 自带通知;

JIT和AOT编译

  • JIT: Just In Time 即时编译
  • AOT: Ahead Of Time 预编译

个人理解:

  • JIT是指在程序运行时, 实时把字节码编译为机器码, 因为在运行中编译, 所以可以实现热重载, 弊端是抢占运行资源, 导致卡顿;
  • AOT是指在程序安装时把字节码一并编译为机器码, 这样不会在运行中执行编译导致耗时, 所以运行速度更快, 但是编译发生在程序安装时, 所以不能实现热重载;

Dart采用双编译, debug包采用JIT, release包采用AOT, 既保证了开发便捷, 又可以实现正式包的高效运行.


类加载顺序

  1. 父类静态代码块(包括静态初始化块,静态属性,但不包括静态方法)
  2. 子类静态代码块(包括静态初始化块,静态属性,但不包括静态方法 )
  3. 父类非静态代码块( 包括非静态初始化块,非静态属性 )
  4. 父类构造方法
  5. 子类非静态代码块 ( 包括非静态初始化块,非静态属性 )
  6. 子类构造方法

静态块:用static声明,JVM加载类时执行,仅执行一次。有多个静态变量或块时,按声明顺序加载
构造块:类中直接用{}定义,每一次创建对象时执行
执行顺序优先级:静态块>main()>构造块>构造方法

HandlerThread, IntentService

HandlerThread 是一个创建了 Looper循环 的Thread, 可以直接获取到其 Looper 提供给外部的 handler 使用, 并且简单封装了两个quit方法.

IntentService 是内部创建了一个 HandlerThread / Handler(内部类ServiceHandler) 的 Service, 每次 startService 时通过 intent 传递数据, 在 onStartCommand 中调用 onStart, 通过 Handler 发送给了 Thread 执行

    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            onHandleIntent((Intent)msg.obj); // 需要用户重写的处理消息的方法
            stopSelf(msg.arg1); // 每个消息执行完成后关闭service
        }
    }

逆变/协变

逆变/协变是使用泛型通配符时出现的概念, 没有通配符就没有逆变协变之说

逆变: java 里的 <? super Parent>, kotlin 里的 <in Parent>, 父类泛型对象可以赋值给子类泛型对象
协变: java 里的 <? extends Parent>, kotlin 里的 <out Parent>, 子类泛型对象可以赋值给父类泛型对象

逆变: myClass<Child> = myClass<Parent>;
协变: myClass<Parent> = myClass<Child>;

类里面的泛型使用时 如果是输入类型的话, 需要把T声明为逆变的; 如果是输出类型的话, 就要把T声明为协变的.

注意, 这里与泛型边界要区别开,

  • java实现泛型边界: class AAA<T extends Parent>, 多重边界class AAA<T extends Father & Mother> 其中Mother必须是接口;
  • kotlin实现泛型边界: class AAA<T : Parent>, 多重边界class AAA<T>() : AAAFather() where T:Father,T:Mother

生命周期

  • 任意位置直接调用 onDestroy 都会崩溃. 在 onCreate 中调用 finish 会直接执行到 onDestroy, 在 onStart 里面 finish 会执行 onStop-onDestroy, 在 onResume 中执行会走完 onPause-onStop-onDestroy.
  • activity 和 fragment 完整的生命周期, 来自 https://github.com/xxv/android-lifecycle
    完整生命周期图

kotlin 的 open 和 abstract

open 使类可以被继承, abstract 的功能 = open 的功能 + 可以声明 abstract 方法;


内部类

  • java 中 new BaseCallBack(){…} 的用法都是匿名内部类, 其实只有 new BaseCallBack() 的话就会提示了: abstract类不能实例化
  • 非静态内部类(无论是否匿名)会持有外部类的引用
    	Handler handler = new Handler();  // 这是实例化
    	
    	Handler handler = new Handler() { // 这是创建了匿名内部类, 然后直接实例化为父类
    		@Override
    		...
    	}
    	
    	Handler myHandler = new MyHandler(); // 创建内部类并实例化, 与上面写法是相同的效果, 只不过不是匿名的内部类了
    	class MyHandler extends Handler {
    		@Override
    		...
    	}
    	
    	// 与上面是相同的效果, 都是持有外部类实例的引用, 这里可以把externalClass放在弱引用里, 防止内存泄漏
    	Handler myStaticHandler = new MyStaticHandler(this); 
    	static class MyStaticHandler extends Handler{
    		Class externalClass;
    		MyStaticHandler(Class externalClass){
    			this.externalClass = externalClass;
    		}
    		@Override
    		...
    	}
    
  • 静态内部类就类似于单独的类文件了, 不依赖任何类

软引用/弱引用

软引用 SoftReference 只有在内存不足时才会回收(Android3.1以后推荐使用LruCache), 弱引用 WeakReference 在gc后就会被回收.
声明静态内部类handler/再利用弱引用 可以解决Activity内的handler内存泄漏问题.

public class MyActivity extends Activity {
	        private MyHandler mHandler;
	
	        @Override
	        protected void onCreate(Bundle savedInstanceState) {
	                super.onCreate(savedInstanceState);
	                mHandler = new MyHandler(this);
	        }
	
	        @Override
	        protected void onDestroy() {
	                mHandler.removeCallbacksAndMessages(null);
	                super.onDestroy();
	        }
	
	        static class MyHandler extends Handler {
	                private WeakReference<MyActivity> mOuter;
	
	                public MyHandler(MyActivity activity) {
	                        mOuter = new WeakReference<MyActivity>(activity);
	                }
	
	                @Override
	                public void handleMessage(Message msg) {
	                        MyActivity outer = mOuter.get();
	                        if (outer != null) {
	                                // Do something with outer as your wish.
	                        }
	                }
	        }
	}

RxJava的merge/zip/flatmap

  • Retrofit+RxJava使用中, 可以使用zip操作符合并多个请求, 并在所有的请求全部成功后一起回调, 可以把所有的repsonse放在一个ArrayList里处理, 其中response顺序与zip传入的Observable顺序是一致的;
  • merge是各自回调各自的, 不会互相等待, 要求回调的类型均一致;
  • flatmap实现多个请求的链式调用, 即一个完成后再开始另一个;

T和?的区别

其实除了都不能指定具体类型之外, 这两个没啥一样的. T是泛型的一种, 一般用来指Type, 另外常用的还有:

  1. K(Key);
  2. V(Value);
  3. E(Enum).

泛型用来指定特定的一种类型, 而?作为通配符可以代指多种类型, 一般有三种用法:

  1. 单独的?: 用来指所有类型;
  2. ? extends T: 指所有T的子类类型;
  3. ? super T: 指所有T的父类;

Https的支持##

关于 OkHttp 和 WebView 对 https 的支持简单说有两种:
1. 全部信任: 这种方式可以支持任意 host 里的接口请求, WebView 会直接忽略 https 的不安全提醒. 这种方式会导致app在上架 GooglePlay 的时候被拒绝.
2. 信任特定的证书: 推荐这种方式, 因为接口无需访问其他的服务器, 仅为自己服务器添加证书支持即可.

两种处理方式的参考: 信任特定证书以及双向证书验证 , 全部信任


Parcelable 和 Serializable 的区别

Serializable基于反射, 效率差且内存开销大, 建议使用Parcelable.
在kotlin中可以使用 @Parcelize 注解避免实现大量 Parcelable 方法(当前为实验功能). 注意, 需要在 app 的 build.gradle 中添加

androidExtensions {
	    experimental = true
	}
@Parcelize
	data class QYColumnResponse(
	        var appVersion: String = "",
	        var contentList: List<QYColumn> = ArrayList(),
	        var msg: String = "",
	        var status: String = ""
	) : Parcelable

单例和静态类的区别##

  1. 单例是类, 拥有 封装,继承,多态 等面向对象的特性, 对于较为复杂的逻辑更加适合; 静态类是方法集合, 相对独立的方法用静态类会更方便;
  2. 如果需要资源释放时, 单例会更加方便;

Android系统架构

Andorid系统分为四层, 每一层均封装底层方法, 并向上暴漏接口.
每一层都有Binder的参与.

  1. Linux Kernel 内核层, 处理底层的IO口/硬件驱动等;
  2. Native中间层, 使用C/C++实现;
  3. Framework应用程序框架, 提供各类 ServiceManager, Content Providers等;
  4. Application应用层.

内存泄漏

本质是某对象已经不会再使用了, 但是jvm无法释放的情况, 主要原因有两个:

  1. 被其他对象强引用;
  2. 资源没有关闭;

常见的有以下几种情况:

  1. Handler/Thread在Activity/Fragment等界面中创建非静态内部类, 在界面销毁后没有释放;
  2. 静态 Context 类;
  3. 单例持有 Activity 对象;
  4. 某些资源未关闭, 例如: BraodcastReceiver, ContentObserver, File, Cursor, Stream, Bitmap等;

ANR

应用程序未响应, 主要有三种情况

  1. KeyDispatchTimeout(5秒): 主要情况, 触摸或按键响应超时;
  2. BroadcastTimeout(10秒): 广播事件接收处理超时, onReceive执行超过10秒时出现;
  3. ServiceTimeout(20秒): 服务超时, 不常见;

OOP面向对象

面向对象的三大特性: 封装, 继承, 多态

面向对象设计的六大原则:

  1. 单一职责原则;
  2. 开放封闭原则: 扩展性开放, 更改性封闭;
  3. 里氏替换原则: 子类在任意位置都能替换父类, 除了泛型相关;
  4. 依赖倒置原则: 具体依赖抽象, 上层依赖下层, 就是说上层仅依赖于下层的抽象而不是具体实现来编写;
  5. 接口隔离原则: 模块之间使用接口隔离, 避免强耦合, 例如MVP的VP间;
  6. 迪米特原则, 也叫最少知道原则: 即对与自己相关的类知道的最少;

图片格式

  1. RGB_565: 2byte, 仅色值, 没有透明度
  2. ARGB_8888: 4byte
  3. ARGB_4444: 2byte, 质量较差, 不推荐使用
  4. ALPHA_8: 1byte, 仅存储位图透明度, 没有色值
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值