每日总结
对每天的学习以及复习内容进行总结
JAVA基础知识
基本数据
数据类型 默认值 大小
boolean false 1比特
char ‘\u0000’ 2字节
byte 0 1字节
short 0 2字节
int 0 4字节
long 0L 8字节
float 0.0f 4字节
double 0.0 8字节
JAVA与C++的对比:
- 都是面向对象的语言,都支持封装,继承,多态
- Java不提供指针来直接管理内存,更加安全
- Java存在垃圾回收机制来自动回收内存,不需要手动释放内存
- Java单继承,多接口,但是C++有多继承
Java常用集合
常用集合(容器):
List:ArrayList、LinkedList、Vector、Stack
ArrayList:底层是数组,数组扩容时,会将老数组中的元素重新拷贝一份到新的数组中,每次数组容量增长大约是其容量的1.5倍;查找元素快、增删元素慢
LinkedList:双向链表,查找元素慢、 增删元素快
Vector:底层Object数组,线程安全,效率低,加了Sychorized关键字修饰
Iterator:中有两个方法,next和hasNext
Stack:先进后出的数据结构,不允许有遍历行为;栈不支持迭代器
Set:HashSet、TreeSet
**HashSet:**由哈希表支持,不保证set的迭代顺序,并允许使用一个null元素。基于HashMap实现,API也是对HashMap的行为进行了封装
**TreeSet:**底层实际上是一个TreeMap,而TreeMap集合底层是一个二叉树;无序不可重复,但是可以按照元素的大小顺序自动排序
Map:HashMap、TreeMap
**HashMap:**由数组+链表的形式存储数据,基于哈希表的Map接口的非同步实现,允许使用null值和null键;底层使用数组实现,进行数组扩容需要重新计算扩容后每个元素在数组中的位置,很耗性能
**TreeMap:**底层是基于红黑树实现的,基于key有序的key value散列表
Hashtable:基于哈希表的Map接口的同步实现,不允许使用null值和null键,底层数组实现,数组中每一项是个单链表,即数组和链表的结合体;synchronized是针对整张Hash表的,即每次锁住整张表让线程独占
ConcurrentHashMap:允许多个修改操作并发进行,其关键在于使用了锁分离技术,只要多个并发发生在不同的段上,它们就可以并发进行;ConcurrentHashMap使用多个子Hash表,也就是段,允许多个读操作并发进行,读操作并不需要加锁
**LinkedHashMap:**继承于HashMap,底层使用哈希表和双向链表来保存所有元素,并且它是非同步,允许使用null值和null键
LinkedHashSet:继承于HashSet、又基于LinkedHashMap来实现的。LinkedHashSet底层使用LinkedHashMap来保存所有元素,它继承与HashSet,其所有的方法操作上又与HashSet相同。
List,Set,Map之间的区别
- List:List在Java中有序,元素值可以重复,
- Set:Set可以存储唯一,无序的对象
- Map:存储键值对,多个key可以存储相同的值,但是key不能重复
ArrayList与LinkedList的区别 - ArrayList和LinkedList都是不同步的,也就是不保证线程安全
- ArrayList底层使用Object数组,LinkedList底层使用双向链表
- ArrayList采用数组存储,所以插入和删除元素时的时间复杂度受元素位置影响,LinkedList使用链表存储,所以,插入和删除元素时不受元素位置的影响
- LinkedList不支持快速随机访问,ArryList支持
- ArraryList的空间浪费主要体现在在list的链表的结尾会预留空间,LinkedList空间体现在它的每一个元素需要比ArraryList更多的空间
Array和ArrayList的区别
Array可以容纳基本类型和对象;而ArrayList只能容纳对象
Array是指定大小的;而ArrayList可自动扩容
Array没有提供ArrayList那么多方法列表,多维数组更适合用Array
ArraryList和vector的区别?为什么要用ArraryList代替Vector
Vector的所有方法都是同步的,可以多线程安全访问vector对象,单线程访问vector对象,会比较耗费时间。
ArraryList是不同步的,所以在不需要保证线程安全时使用它
HashMap和HashTable的区别
HashMap是非线程安全的,HashTable是线程安全的,HashTable内部的方法都经过synchronized修饰
HashMap的效率比HashTable要高,因为线程安全的问题
HashMap中,null值可以作为键,这样的键只能有一个,可以有一个或多个键对应的值为null。但是在HashTable中只要有一个null,就抛出异常
创建时如果不指定容量初始值,Hashtable 默认的初始大小为11,之后每次扩充,容量变为原来的2n+1。HashMap 默认的初始化大小为16。之后每次扩充,容量变为原来的2倍
JDK1.8 以后的 HashMap 在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。Hashtable则 没有这样的机制
HashMap和HashSet的区别
HashSet 底层就是基于 HashMap 实现的
ConcurrentHashMap 和 Hashtable 的区别
JDK1.7的 ConcurrentHashMap 底层采用 分段的数组+链表 实现,JDK1.8 采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。
Hashtable 和 JDK1.8 之前HashMap 的底层数据结构类似都是采用 数组+链表
在JDK1.7的时候,ConcurrentHashMap(分段锁) 对整个桶数组进行了分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。
JDK1.8直接用 Node 数组+链表+红黑树的数据结构来实现,并发控制使用 synchronized 和 CAS 来操作
Hashtable(同一把锁) :使用 synchronized 来保证线程安全,效率非常低下
JAVA创建线程的4种方式: - 直接继承Thread类,重写run方法,使用 Thread类对象的start()开启线程
- 实现runnable接口,将此实现类作为参数传递到 Thread类的构造器中,创建 Thread类的对象
- 创建一个实现 Callable接口的实现类, 将此 Callable接口实现类的对象作为参数传递到 FutureTask构造器中,创建 FutureTask对象,将 FutureTask的对象作为参数传递到 Thread类的构造器中,创建 Thread对象,并调用 start()
- 从线程池里面获取
探究安卓里面的设计模式
1.观察者模式(Observer Pattern)
使用场景:
BroadCast监听系统广播或者程序内部自己发送的广播
EventBus 广播原理一样
LiveData 数据更新通知观察者
Adapter 中数据变化时,notifyDataSetChange
2.适配器模式(Adapter Pattern)
把一个类的接口转换为另一个类期待的接口类型,从而是接口不匹配的两个类兼容工作ListView RecycleView使用适配器模式,通过创建适配器,让数据正确的供RecycleView使用
3.代理模式(Proxy Pattern)
为当前类提供一个代理类给其他类访问,以防止当前类直接暴露出去。
Activity的管理者是ActivityManager,但最终的管理者为ActivityManagerService(AMS),在Android7.0及以前版本中, ActivityManager通过调用ActivityManagerProxy进行一些操作,而ActivityManagerProxy会调用AMS,真正的工作是由AMS来完成的。
4.工厂模式(Factory Pattern)
给外部提供一个统一创建特定对象的类,降低创建对象的复杂性。
Android中的BitmapFactory支持用不同的方式创建Bitmap对象。
5.单例模式(Singleton Pattern)
进程中只创建一个实例。Android8.0以上版本,IActivityManager和IActivityTaskManager实例都是单例,使用了系统的单例实现,
6.责任链模式
一条请求沿着一条链挨个儿传递,直到有对象处理它为止。
Android中UI事件传递使用了责任链模式
OkHttp的拦截器也是使用责任链模式
7.建造者模式(Builder Pattern)
将一个复杂对象的创建和表示分离,使用相同的构建过程创建不同的对象。
AlertDialog使用了建造者模式。
内存泄漏的案例: 可在app里面集成LeakGanary检查内存泄漏
在进行异步操作时,我们经常会使用到Handler类。常见的写法如下。
public class MainActivity extends Activity { … private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { } }; }
当使用内部类或匿名内部类的方式创建Handler时,Handler对象会隐式地持有一个外部类对象的引用(这里的外部类是Activity)。一般在一个耗时任务中会开启一个子线程,如网络请求或文件读写操作,我们会使用到Handler对象。但是,如果在任务未执行完时,Activity被关闭了,Activity已不再使用,此时由GC来回收掉Activity对象。由于子线程未执行完毕,子线程持有Handler的引用,而Handler又持有Activity的引用,这样直接导致Activity对象无法被GC回收,即出现内存泄漏。
解决方法主要在于两点:
1.将Handler声明为静态内部类。因为静态内部类不会持有外部类的引用,所以不会导致外部类实例出现内存泄露。
2.在Handler中添加对外部Activity的弱引用。由于Handler被声明为静态内部类,不再持有外部类对象的引用,导致无法在handleMessage()中操作Activity中的对象,所以需要在Handler中增加一个对Activity的弱引用。
public class MainActivity extends Activity {
private final MyHandler mHandler = new MyHandler(this);
private static class MyHandler extends Handler {
private final WeakReference<MainActivity> mActivity;
public MyHandler(MainActivity activity) {
this.mActivity = new WeakReference<MainActivity>(activity);
}
@Override
public void handleMessage(Message msg) {
MainActivity mainActivity = mActivity.get();
if (mainActivity == null) {
return;
}
// your code here
}
}
}
关于WeakReference的说明:
WeakReference即弱引用,与强引用(即我们常说的“引用”)相对。它的特点是,GC在回收时会忽略掉弱引用,即就算有弱引用指向某对象,但只要该对象没有被强引用指向,该对象就会在被GC检查到时回收掉。
对于上面在子线程中下载文件的示例代码,改为采用WeakReference之后,如果任务未执行完但是用户却关闭了MainActivity,但由于仅有一个来自MyHandler的弱引用指向MainActivity,所以GC仍然会在检查时把MainActivity回收掉。这样,内存泄露的问题就不会出现了。
两种单例模式
// 饿汉单例
public final class Singleton {
private static final Singleton instance = new Singleton();
private Singleton(){
}
public static Singleton getInstance(){
return instance;
}
}
// 懒汉单例
public class Singleton {
private static Singleton instance;
private static final Object syn = new Object();
private Singleton() {
}
public static Singleton getInstance(){
if (instance == null){
synchronized (syn){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
探究AIDL
安卓官网对于AIDL的介绍:https://developer.android.google.cn/guide/components/aidl?hl=zh_cn
主要参考: https://juejin.cn/post/6844903496882323464
服务端 :创建了一个service,并在service内部声明了一个IBinder对象,它是一个匿名实现的IMyAidlInterface.Stub的实例
客户端 : 将服务器端的aidl拷贝到客户端,这边特别要注意,拷贝后的客户端的aidl文件包目录必须与服务器端保持一致,拷贝完后同样时编译工程,让客户端也生成对应的java文件.绑定完服务,就是进行调用
AIDL生成的Java文件:
/*
* This file is auto-generated. DO NOT MODIFY.
*/
// Declare any non-default types here with import statements
public interface IMyAidlInterface extends android.os.IInterface
{
/** Default implementation for IMyAidlInterface. */
public static class Default implements com.example.aidlserver.IMyAidlInterface
{
/**
* 自己添加的方法
*/
@Override public int add(int value1, int value2) throws android.os.RemoteException
{
return 0;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.aidlserver.IMyAidlInterface
{
private static final String DESCRIPTOR = "com.example.aidlserver.IMyAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.aidlserver.IMyAidlInterface interface,
* generating a proxy if needed.
*/
public static com.example.aidlserver.IMyAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.example.aidlserver.IMyAidlInterface))) {
return ((com.example.aidlserver.IMyAidlInterface)iin);
}
return new com.example.aidlserver.IMyAidlInterface.Stub.Proxy(obj);
}
@Override public android.os.IBinder asBinder()
{
return this;
}
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_add:
{
data.enforceInterface(descriptor);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.add(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.example.aidlserver.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
@Override public android.os.IBinder asBinder()
{
return mRemote;
}
public String getInterfaceDescriptor()
{
return DESCRIPTOR;
}
/**
* 自己添加的方法
*/
@Override public int add(int value1, int value2) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeInt(value1);
_data.writeInt(value2);
boolean _status = mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().add(value1, value2);
}
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.example.aidlserver.IMyAidlInterface sDefaultImpl;
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
public static boolean setDefaultImpl(com.example.aidlserver.IMyAidlInterface impl) {
// Only one user of this interface can use this function
// at a time. This is a heuristic to detect if two different
// users in the same process use this function.
if (Proxy.sDefaultImpl != null) {
throw new IllegalStateException("setDefaultImpl() called twice");
}
if (impl != null) {
Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.example.aidlserver.IMyAidlInterface getDefaultImpl() {
return Proxy.sDefaultImpl;
}
}
/**
* 自己添加的方法
*/
public int add(int value1, int value2) throws android.os.RemoteException;
}
Stub的目录结构也不复杂,一个构造函数,一个asInterface方法,一个asBinder方法,一个onTransact方法,一个Proxy代理类,这边Proxy与Stub同时实现了我们定义的aidl,且Proxy中实现了我们在aidl中定义的add/addUser方法。 在客户端,我们绑定服务的时候通过Stub.asInterface()返回aidl对象,查看asInterface源码,我们不难发现,客户端获取到的其实时Stub.Proxy,一个远程服务的代理。所以客户端调用add/addUser方法其实调用的时Stub.Proxy中实现的add/addUser,在Stub.Proxy中我们可以看到一句核心代码mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
追溯源头,mRemote其实就是IMyAidlInterface.Stub,随着mRemote.transact传递到了IMyAidlInterface.Stub.onTransact, onTransact中执行了add/addUser,回调到了我们在服务端定义Service中声明IBidner时重写的add/addUser,这就是AIDL的整个流程。
安卓代码架构(MVC,MVVM,MVP)
主要参考: https://juejin.cn/post/6997295828079476772
这三种架构的目的都是分离,避免将过多的逻辑全部堆积在一个类中
MVC架构: MVC是模型(Model)-视图(View)-控制器(Controller)的缩写,用一种业务逻辑、数据、界面显示分离的方法组织代码。其实Android Studio创建一个项目的模式就是一个简化的mvc模式。
Model:实体类(数据的获取、存储、数据状态变化)。
View:布局文件
Controller:Activity(处理数据、业务和UI)。
MVP架构: Activity类的职责不断增加,以致变得庞大臃肿
MVP是MVC架构的一个演化版,全称是Model-View-Presenter。将MVC中的V和C结合生成MVP中的V,引入新的伙伴Presenter。
Model:实体类(数据的获取、存储、数据状态变化)。
View:布局文件+Activity。
Presenter:中介,负责完成View与Model间的交互和业务逻辑。
MVVM架构: 是 Model-View-ViewModel 的简写。MVVM与MVP的结构还是很相似的,就是将Presenter升级为ViewModel。在MVVM中,View层和Model层进行了双向绑定(即Data Binding),所以Model数据的更改会表现在View上,反之亦然。ViewModel就是用来根据具体情况处理View或Model的变化。
Model:实体类(数据的获取、存储、数据状态变化)。
View:布局文件+Activity。
ViewModel: 关联层,将Model和View进行绑定,Model或View更改时,实时刷新对方。
/frameworks/base/core/res/res/values/config.xml是framework层的配置文件。