Android知识点

2020-07-03  本文已影响0人  jxcyly1985

Android

[TOC]

JAVA

JVM

ClassLoader

ClassLoader介绍

ClassLoader默认有3个加载器:

ClassLoader特征:

自定义classloader

ClassLoader相当于类的命名空间,起到了类隔离的作用。

多线程编程

原子性

可见性

有序性

可见性是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的

volatile关键字

Synchronized实现原理和锁

syncronized 语义和原理

synchronized的优化方向从无锁-->乐观锁--->悲观锁的过程,针对不同的场景减少线程同步成本

Java对象内存模型

对象头在处于锁状态和无锁状态的时候

ObjectMonitor监视器对象

乐观锁 & 悲观锁

乐观锁是操作的时候不加锁,假设没有冲突,如果有冲突就重试,直到成功为止。

CAS原理

CAS通过CPU的cmpxchg指令保证了CAS操作的原子性

CAS包含3个操作数

内存屏障

内存屏障,也称内存栅栏,内存栅障,屏障指令等, 是一类同步屏障指令,是CPU或编译器在对内存随机访问的操作中的一个同步点,使得此点之前的所有读写操作都执行后才可以开始执行此点之后的操作。

Volatile原理

缓存一致性协议

当CPU写数据,如果发现操作的是共享变量,并且在其他CPU存在该变量的缓存,则通知其他CPU置该缓存无效

那么其他CPU则需要从内存中重新读取数据,这个时候读取的数据就能保证一致性

Happen-before原则

程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作
volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作
线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行
对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始

Reentrantlock & AQS

CAS和volatile是实现concurrent包的基石

CAS保证了读-改-写的原子性操作,volatile保证了并发的可见性

各种Hash容器对比

HashMap &HashTable

为什么jdk1.7采用头部插入,而jdk1.8采用尾部插入

修改原因

主要原因是头插法会导致扩容时候的改变元素原本的位置,这样在多线程情况下会导致产生链表成环

从而引起链表遍历死循环

本质是同一个节点的next指针发生了变化,导致出现了互相指向next

orignal: 10->2

t1: 2->10

t2: 2->10->2 形成链表环

enumeration iterator区别

ConcurrentHashMap & HashTable

优化点

不足点:

put操作流程

final V put(K key, int hash, V value, boolean onlyIfAbsent) {       //(1)
            HashEntry<K,V> node = tryLock() ? null :
                scanAndLockForPut(key, hash, value);
            V oldValue;
            try {          //(2)
                HashEntry<K,V>[] tab = table;          //(3)
                int index = (tab.length - 1) & hash;          //(4)
                HashEntry<K,V> first = entryAt(tab, index);          //(5)
                for (HashEntry<K,V> e = first;;) {
                    if (e != null) {
                        K k;
                        if ((k = e.key) == key ||
                            (e.hash == hash && key.equals(k))) {
                            oldValue = e.value;
                            if (!onlyIfAbsent) {
                                e.value = value;
                                ++modCount;
                            }
                            break;
                        }
                        e = e.next;
                    }            //(6)
                    else {              
                        if (node != null)                 //(7)
                            node.setNext(first);
                        else  //(8)
                            node = new HashEntry<K,V>(hash, key, value, first);
                        int c = count + 1;              //(9)
                        if (c > threshold && tab.length < MAXIMUM_CAPACITY)
                            rehash(node);
                        else   //(10)
                            setEntryAt(tab, index, node);
                        ++modCount;
                        count = c;
                        oldValue = null;
                        break;
                    }
                }
            } finally {         //(11)
                unlock();
            }
            return oldValue;
   }
   
代码(1)首先第一步的时候会尝试获取锁,如果获取失败肯定就有其他线程存在竞争,则利用 scanAndLockForPut() 自旋获取锁。

代码(2)每一个Segment对应一个HashEntry[ ]数组。

代码(3)计算对应HashEntry数组的下标 ,每个segment中数组的长度都是2的N次方,所以这里经过运算之后,取的是hash的低几位数据。

代码(4)定位到HashEntry的某一个结点(对应链表的表头结点)。

代码(5)遍历链表。

代码(6)如果链表为空(即表头为空)

代码(7)将新节点插入到链表作为链表头。、

代码(8)根据key和value 创建结点并插入链表。

代码(9)判断元素个数是否超过了阈值或者segment中数组的长度超过了MAXIMUM_CAPACITY,如果满足条件则rehash扩容!

代码(10)不需要扩容时,将node放到数组(HashEntry[])中对应的位置

代码(11)最后释放锁。

NIO

NIO和BIO区别

Non-blocking 非阻塞IO

三大核心部分

数据从通道读取到缓冲区,或者从缓冲区写入通道中,selector用于监听多个通道的事件

stream和channel区别

directbuffer模式

大文件处理

MappedByteBuffer

MappedByteBuffer以及ByteBufer的底层原理

本质上内存映射是减少了物理文件数据拷贝到内核缓冲区,直接拷贝到用户的缓冲区空间

FileChannel优势

OKIO

ThreadLocal原理

threadlocal并不是用来解决多线程环境下的共享变量问题

而是用来提供线程捏的共享变量,从而在多线程环境下,实现线程间的数据的隔离

为什么ThreadLocal设置value的时候都使用ThreadLocal作为ThreadLocalMap的key却不会冲突

线程池原理

Android流程

基础

Ashmem 匿名共享内存

linux下一切都是文件,因此可以获取文件描述符fd

系统启动流程

应用启动流程

应用事件传递流程

渲染流程

surface跨进程传递

BufferQueue实现了BnGraphicBufferProducer和BnGraphicBufferConsumer接口

BufferQueue本质上提供了生产者和消费者对图形数据缓存区的统一管理

BufferQueue 实现原理是binder和共享内存机制

动画流程分析

动画流程分析

设备标识

唯一标识

内存管理

进程类型(5类)

内存LMK规则

UI&&屏幕刷新

ViewRootImpl

视图和窗口管理器的协议实现,是最顶层视图的抽象

SurfaceFlinger

针对图形系统的surface的建立,控制,管理的系统服务,是图形系统和显示系统对接的通道

VSync

surfurcelinger定时发送的数据帧刷新信号

Choreographer

编舞者解决Vsync和绘制不同步的问题,原理就是往Choreographer发送消息,最快也需要等到下一个Vsync信号来的时候才开始处理消息

用来和vsync信号同步,执行vsync的回调执行ViewRootImpltraversavl遍历动作,流程如下:

同步消息栅栏

同步消息栅栏,是为了保证VSync信号能及时的处理,是Android消息机制提供的异步信息,用户处理高优先级消息的一种机制。

在这里过程中,我们依然需要尽量保证每个同步消息的处理不要超过16ms,这样才会减少VSync信号处理不及时的概率。

要点

页面显示时机

Surface 和Buffer申请流程

View、Canvas与Surface的关系

绘制流程

View的绘制流程

深入浅出屏幕刷新原理

Android中View的更新方法:invalidate()和requestLayout()

操作系统

死锁

死锁的处理方式

死锁的必要条件

避免死锁的方法

避免死锁的技术

进程通信

binder细节

BinderProxy创建过程

Binder

使用binder的优势

Binder简述

binder内主要涉及到binder_nodebinder_thread的管理

binder_node和binder_thread都是通过红黑树进行保存

binder的主要过程

ioctl

ioctl是专用于设备输入输出操作的系统调用

ioctl调用传入设备有关的请求码,系统调用的功能取决于请求码

binder驱动执行请求码的实现

binder驱动解析和实现这些请求码

虚拟内存

虚拟内存

binder引用关系

用户态和内核态工作

进程 用户态 内核态 说明
client 创建BpBinder对象,关联Service组件handle BpBinder对象handle关联binder ref对象(handle是内核共享对象)
Binder ref对象关联binder node对象(binder node是内核共享对象)
Binder node对象关联service服务组件
server 创建service服务组件
binder驱动创建一个binder node对象对应service服务组件 说明只有当一个binder对象经过ServiceManager对象addService处理之后才会在binder驱动层面创建对应的binder node对对象

Binder数据模型

所有的设计最终会体现在模型上,因此分析基本的数据模型对象能更深刻的了解binder的设计原理

struct binder_proc

描述binder进程的数据对象,从这个对象中我们开始发现一些特征

struct binder_write_read

描述递交一个transaction的具体数据,在这个对象中我们会封装

这个过程的是需要传递给到binder驱动,会拷贝transaction对象

struct binder_work

binder_work表示一个transaction的特征,用于标识一个特定的操作行为

有多种类型的binder work用于标识不同的操作行为

struct binder_transaction

描述一次transaction的client和service的一次通信,主要包含的信息有

struct binder_transaction_data

描述transaction中的信息

struct binder_alloc

基于binder_transaction_data的数据信息生成buffer对象(数据缓冲区管理对象),分配page建立地址映射

描述binder进程的内存分配信息

flat_binder_object

用于包装传递的Binder对象,可能是本地对象,或者是句柄

Binder线程模型

Binder服务死亡

每一个binder对象都可以设置IBinder.DeathRecipient (死亡接收者回调)

/frameworks/base/core/java/android/os/Binder.java

final class BinderProxy implements IBinder {
    // Assume the process-wide default value when created
    volatile boolean mWarnOnBlocking = Binder.sWarnOnBlocking;

    public native boolean pingBinder();
    public native boolean isBinderAlive();
    ......
    ......
    ......
    public native String getInterfaceDescriptor() throws RemoteException;
    public native boolean transactNative(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException;
    public native void linkToDeath(DeathRecipient recipient, int flags)
            throws RemoteException;
/frameworks/native/libs/binder/BpBinder.cpp

status_t BpBinder::linkToDeath(
    const sp<DeathRecipient>& recipient, void* cookie, uint32_t flags)
{
    Obituary ob;
    ob.recipient = recipient;
    ob.cookie = cookie;
    ob.flags = flags;

    LOG_ALWAYS_FATAL_IF(recipient == NULL,
                        "linkToDeath(): recipient must be non-NULL");

    {
        AutoMutex _l(mLock);

        if (!mObitsSent) {
            if (!mObituaries) {
                mObituaries = new Vector<Obituary>;
                if (!mObituaries) {
                    return NO_MEMORY;
                }
                ALOGV("Requesting death notification: %p handle %d\n", this, mHandle);
                getWeakRefs()->incWeak(this);
                IPCThreadState* self = IPCThreadState::self();
                self->requestDeathNotification(mHandle, this);
                self->flushCommands();
            }
            ssize_t res = mObituaries->add(ob);
            return res >= (ssize_t)NO_ERROR ? (status_t)NO_ERROR : res;
        }
    }

    return DEAD_OBJECT;
}

binder驱动管理所有的binder句柄,同时对binder句柄设置该binder句柄的BpBinder的监听对象

/frameworks/native/libs/binder/IPCThreadState.cpp

status_t IPCThreadState::requestDeathNotification(int32_t handle, BpBinder* proxy)
{
    mOut.writeInt32(BC_REQUEST_DEATH_NOTIFICATION);
    mOut.writeInt32((int32_t)handle);
    mOut.writePointer((uintptr_t)proxy);
    return NO_ERROR;
}

status_t IPCThreadState::clearDeathNotification(int32_t handle, BpBinder* proxy)
{
    mOut.writeInt32(BC_CLEAR_DEATH_NOTIFICATION);
    mOut.writeInt32((int32_t)handle);
    mOut.writePointer((uintptr_t)proxy);
    return NO_ERROR;
}

IPC状态负责处理各种命令

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    RefBase::weakref_type* refs;
    status_t result = NO_ERROR;

    switch ((uint32_t)cmd) {
    case BR_ERROR:
        result = mIn.readInt32();
        break;

    case BR_OK:
        break;

    case BR_ACQUIRE:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        ALOG_ASSERT(refs->refBase() == obj,
                   "BR_ACQUIRE: object %p does not match cookie %p (expected %p)",
                   refs, obj, refs->refBase());
        obj->incStrong(mProcess.get());
        IF_LOG_REMOTEREFS() {
            LOG_REMOTEREFS("BR_ACQUIRE from driver on %p", obj);
            obj->printRefs();
        }
        mOut.writeInt32(BC_ACQUIRE_DONE);
        mOut.writePointer((uintptr_t)refs);
        mOut.writePointer((uintptr_t)obj);
        break;

    case BR_RELEASE:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        ALOG_ASSERT(refs->refBase() == obj,
                   "BR_RELEASE: object %p does not match cookie %p (expected %p)",
                   refs, obj, refs->refBase());
        IF_LOG_REMOTEREFS() {
            LOG_REMOTEREFS("BR_RELEASE from driver on %p", obj);
            obj->printRefs();
        }
        mPendingStrongDerefs.push(obj);
        break;

    case BR_INCREFS:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        refs->incWeak(mProcess.get());
        mOut.writeInt32(BC_INCREFS_DONE);
        mOut.writePointer((uintptr_t)refs);
        mOut.writePointer((uintptr_t)obj);
        break;

    case BR_DECREFS:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();
        // NOTE: This assertion is not valid, because the object may no
        // longer exist (thus the (BBinder*)cast above resulting in a different
        // memory address).
        //ALOG_ASSERT(refs->refBase() == obj,
        //           "BR_DECREFS: object %p does not match cookie %p (expected %p)",
        //           refs, obj, refs->refBase());
        mPendingWeakDerefs.push(refs);
        break;

    case BR_ATTEMPT_ACQUIRE:
        refs = (RefBase::weakref_type*)mIn.readPointer();
        obj = (BBinder*)mIn.readPointer();

        {
            const bool success = refs->attemptIncStrong(mProcess.get());
            ALOG_ASSERT(success && refs->refBase() == obj,
                       "BR_ATTEMPT_ACQUIRE: object %p does not match cookie %p (expected %p)",
                       refs, obj, refs->refBase());

            mOut.writeInt32(BC_ACQUIRE_RESULT);
            mOut.writeInt32((int32_t)success);
        }
        break;

    case BR_TRANSACTION:
        {
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));
            ALOG_ASSERT(result == NO_ERROR,
                "Not enough command data for brTRANSACTION");
            if (result != NO_ERROR) break;

            Parcel buffer;
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);

            const pid_t origPid = mCallingPid;
            const uid_t origUid = mCallingUid;
            const int32_t origStrictModePolicy = mStrictModePolicy;
            const int32_t origTransactionBinderFlags = mLastTransactionBinderFlags;

            mCallingPid = tr.sender_pid;
            mCallingUid = tr.sender_euid;
            mLastTransactionBinderFlags = tr.flags;

            //ALOGI(">>>> TRANSACT from pid %d uid %d\n", mCallingPid, mCallingUid);

            Parcel reply;
            status_t error;
            IF_LOG_TRANSACTIONS() {
                TextOutput::Bundle _b(alog);
                alog << "BR_TRANSACTION thr " << (void*)pthread_self()
                    << " / obj " << tr.target.ptr << " / code "
                    << TypeCode(tr.code) << ": " << indent << buffer
                    << dedent << endl
                    << "Data addr = "
                    << reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer)
                    << ", offsets addr="
                    << reinterpret_cast<const size_t*>(tr.data.ptr.offsets) << endl;
            }
            if (tr.target.ptr) {
                // We only have a weak reference on the target object, so we must first try to
                // safely acquire a strong reference before doing anything else with it.
                if (reinterpret_cast<RefBase::weakref_type*>(
                        tr.target.ptr)->attemptIncStrong(this)) {
                    error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                            &reply, tr.flags);
                    reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
                } else {
                    error = UNKNOWN_TRANSACTION;
                }

            } else {
                error = the_context_object->transact(tr.code, buffer, &reply, tr.flags);
            }

            //ALOGI("<<<< TRANSACT from pid %d restore pid %d uid %d\n",
            //     mCallingPid, origPid, origUid);

            if ((tr.flags & TF_ONE_WAY) == 0) {
                LOG_ONEWAY("Sending reply to %d!", mCallingPid);
                if (error < NO_ERROR) reply.setError(error);
                sendReply(reply, 0);
            } else {
                LOG_ONEWAY("NOT sending reply to %d!", mCallingPid);
            }

            mCallingPid = origPid;
            mCallingUid = origUid;
            mStrictModePolicy = origStrictModePolicy;
            mLastTransactionBinderFlags = origTransactionBinderFlags;

            IF_LOG_TRANSACTIONS() {
                TextOutput::Bundle _b(alog);
                alog << "BC_REPLY thr " << (void*)pthread_self() << " / obj "
                    << tr.target.ptr << ": " << indent << reply << dedent << endl;
            }

        }
        break;

    // 处理binder实体死亡
    case BR_DEAD_BINDER:
        {
            BpBinder *proxy = (BpBinder*)mIn.readPointer();
            proxy->sendObituary();
            mOut.writeInt32(BC_DEAD_BINDER_DONE);
            mOut.writePointer((uintptr_t)proxy);
        } break;

    case BR_CLEAR_DEATH_NOTIFICATION_DONE:
        {
            BpBinder *proxy = (BpBinder*)mIn.readPointer();
            proxy->getWeakRefs()->decWeak(proxy);
        } break;

    case BR_FINISHED:
        result = TIMED_OUT;
        break;

    case BR_NOOP:
        break;

    case BR_SPAWN_LOOPER:
        mProcess->spawnPooledThread(false);
        break;

    default:
        ALOGE("*** BAD COMMAND %d received from Binder driver\n", cmd);
        result = UNKNOWN_ERROR;
        break;
    }

    if (result != NO_ERROR) {
        mLastError = result;
    }

    return result;
}

Native进程

场景

c++实现方式

  1. 定义RPC通信接口
  2. 定义服务器端接口,实现BnInterface(BnInterface继承于BBinder)
  3. 服务器端实现onTransact方式,执行客户端请求
  4. 定义客户端,实现BpInterface
  5. 客户端在接口内实现remote调用transact调用向服务器端发送请求
  6. 定义服务端实现类,实现2中定义的服务端接口
  7. 在ServiceManager中添加6的服务端实现类
  8. 默认方法的code是从1开始计数

java实现方式(参考ActivityManagerNative方式)

JNI原理

网络知识

TCP/IP

tcp握手三次的原因

TCP可靠性定义

性能优化

稳定性

OOM

NativeCrash

流畅(卡顿)

工具

Profiler

SystemTrace
MethodTrace

功耗(电量,流量)

网络

屏幕

CPU

APK包瘦身

网络优化

图片优化

尺寸压缩和采样压缩都是缩小了图片的尺寸,只是使用的技术不同

StrictMode

算法

二叉树遍历

public class Tree{
    Object data;
    Tree left;
    Tree right;
    boolean accessed;
}

非递归算法

先序遍历(根左右)
  1. printf当前node节点(node.data)
  2. 压入当前node节点入栈
  3. 循环遍历左子树栈(把所有的左子树压入栈)
  4. 左子树遍历完毕,栈顶出栈
  5. 切换到栈顶树的右子树遍历
  6. 重复1->5过程,所有元素至此遍历完毕
public void preTraversal(Tree node){
    if(node == null){
        return;
    }
    Stack<Tree> treeNodeStatck = new Stack<>();
    while(node != null || !treeNodeStatck.isEmpty()){
        //循环遍历左子树,一直遍历到左子树结束
        while(node != null){
            //打印根节点值
            printf("node data " + node.data);
            treeNodeStatck.push(node);
            node = node.left;
        }
        //左子树遍历完毕,取出根节点
        node = treeNodeStatck.pop();
        //切换到右子树遍历
        node = node.right;
    }
}
中序遍历(左根右)
public void midTraversal(Tree node){
    if(node == null){
        return;
    }
    Stack<Tree> treeNodeStatck = new Stack<>();
    while(node != null || !treeNodeStatck.isEmpty()){
        //循环遍历左子树,一直遍历到左子树结束
        while(node != null){
            treeNodeStatck.push(node);
            node = node.left;
        }
        //子左树遍历结束,打印子左树根节点值
        node = treeNodeStatck.pop();
        printf("node data " + node.data);
        //切换到子左树根节点的右子树遍历
        node = node.right;
    }
    
}
后序遍历(左右根)
public void postTraversal(Tree node){
    if(node == null){
        return;
    }
    Stack<Tree> treeNodeStatck = new Stack<>();
    while(node !=null || !treeNodeStatck.isEmpty()){
        while(node != null){
            treeNodeStatck.push(node);
            node = node.left;
        }
        //子树根节点出栈
        node = treeNodeStatck.pop();
        //是否压入过栈,也就是说是否右子树已经遍历完
        if(node.accessed){
            printf("node data " + node.data);
            node = null;
        }else{
            //暂时压入子树根节点
            treeNodeStatck.push(node);
            node.accessed = true;
            //切换到子根节点的右子树遍历
            node = node.right;
            
        }
    }
    
}
要点
  1. 二叉树的子树依然是二叉树,二叉树的结构是个递归结构。
  2. 因此不管是先序,中序,后序,是遍历的二叉树的子树先后不同,只要存在node代表的树不为Null,那么遍历就不会结束。
  3. 每一颗树都应该看成一颗独立的树,二叉树遍历的本质就是对每一颗子树压栈和出栈的遍历过程。
  4. 任意一点树节点,都需要遍历左子树,和右子树
  5. 后序遍历唯一和其他遍历不同的是需要重新保存一下根节点,待右子树遍历完毕之后,重新访问。

链表反转

public class List{
    Node head;
}
public class Node{
        Node next;
        Object value;
}

链表本身是一个递归结构,每一个next的节点都可以作为一个新的链的开始,也就是作为一个子链。

所以链表反转的步骤即:

  1. 保存头节点,获取子节点作为子链表开始
  2. 保存子节点的下一个节点的子链,同时把当前子节点指向头节点
  3. 同时把当前子节点设置未头节点,循环2直到子节点为null结束
public void revert(List list){
    if(list == null){
        return;
    }
    //获取头结点
    Node head = list.head;
    if(head == null){
        return;
    }
    //获取头结点的下一个节点
    Node next = head.next;
    // 清除头节点的next节点
    head.next = null;
    //如果存在下一个节点,循环遍历
    while(next != null){
        //保存下下个节点
        Node tmp = next.next;
        //下一个节点执行head节点
        next.next = head;
        //下一个节点置为head节点
        head = next;
        //检查下下一个节点
        next = tmp;
    }   
}

查找子串

LRU算法

LRU算法,主要是针对get和put操作,更换容器的元素位置,把get和put操作使用的元素放置在容器的最头部

双向链表对比单链表

双向链表的删除和添加效率是O(1)比单链表效率高

设计模式

分类

创建型模式

单例模式

该类只有一个实例,且该类能自行创建这个实例的模式

原型模式

使用已经创建的实例作为原型,通过复制该对象来创建一个和原型相同或者类似的新对象

工厂方法

定义一个创建产品对象的工厂接口,将产品对象的实际创建工作延迟到具体子工厂类中

抽象工厂

提供创建一组相关或相互依赖对象的接口,而无需指定创建的产品的具体类型

构造者模式

对构造过程和显示分离,使同一种构造过程可以产生不同的构造表现

结构性模式

代理模式

对某个对象提供一个代理以控制该对象的访问。对于访问对象不适合或者不能直接饮用目标对象,代理对象作为访问对象和目标对象的中介。

适配器模式

将一个类的接口转换成客户希望的另外一个接口,使得原来由于接口不兼容而不能一起功能的类可以一起工作。

桥接模式

将抽象和实现分离,使抽象和实现可以独立变化。

通过组合关系代替继承关系,从而降低了抽象和实现这两个可变维度的耦合度。

本质上是通过抽象的框架对具体的实现类对象进行组合对象,同时抽象的框架可以变化,具体的实现类对象也可以变化

装饰模式

不改变现有对象结构的情况下,动态的给该对象增加一些职责(即增加其额外的功能)的模式

外观模式

通过为多个复杂的子系统提供一致的接口,从而使这些子系统更加容易被访问的模式。

该模式对外有一个统一的接口,外部应用程序不用关心内部子系统的具体的细节。

降低系统访问的复杂性。

享元模式

运用共享技术有效的支持大量细粒度对象的复用。

涉及到享元工厂共享状态非共享状态

组合模式

部分-整体模式,将对象组合成树状的层次结构的模式,使用户对单个对象和组合对象具有一致的访问性

结构模式中的对比

装饰模式 && 代理模式

行为模式

模板模式

定义一个操作中的算法骨架,而将算法的一些步骤延迟到子类中,使得子类可以不改变该算法结构的情况下重新定义该算法的某些特定步骤。

策略模式

该模式定义一系列算法,并将算法封装起来,使他们可以相互替换,且算法的替换不会影响算法的客户。

命令模式

将请求封装成一个对象,使发出请求的责任和执行请求的责任分隔开。

这样两者可以通过命令对象进行沟通,也方便将命令对象储存,传递,调用,增加和管理。

涉及元素命令对象,接收者,调用者

职责链模式

避免请求发送者和多个请求的处理者耦合在一起,将所有的请求处理者连成一条链,可将请求沿着这条链传递,直到有对象处理它为止。

状态模式

状态模式将与特定状态相关的行为局部化到一个状态中,并且将不同状态的行为分割开来,满足单一职责原则

观察者模式

多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。

中介者模式

定义一个中介对象来封装一系列对象之间的交互,使原有对象之间的耦合松散,且可以独立地改变他们之间的交互。中介者模式是迪米特法则的典型应用

迭代器模式

提供一个对象来顺序的访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。

访问者模式

访问者不同意味着节点的处理不同

作用于某种数据结构中各元素的操作分离出来封装成独立的类,使其在不改变数据结构的前提下,可以添加作用于这些元素新的操作,即不同的处理逻辑

设计原则

面向对象原则

质量

开发质量

运行质量

常用方法

依赖反转

依赖注入

设计工具

UML图

鲁棒图

检查用例规约是否正确和完善

场景-目标-决策

功能树(功能组)

架构

组件化

组件化优势

主流框架

LeakCanary

Flutter

路由框架

缺点

事件总线

事件总线的本质是转发调用请求

组件总线只负责通信,即转发调用请求和返回执行结果。

不需要下沉接口,面向通信协议编程

优点

缺点

美团modular-event-bus

优化点在于:

缺点:

ASM框架

AOP 的利器:ASM 3.0 介绍

CodeModel & JavaPoet

用于生成java文件的框架库

AOP框架

图片框架

网络框架

ORM优缺点

产品方面

小程序

小程序是一种全新的连接用户和服务的方式

从各家小程序的开发路径来看,都是基于用户(客户)的正向反馈,进行产品的增量迭代

2A3R定义:AARRR是获取用户(Acquisition)、提高活跃度(Activation)、提高留存率(Retention)、获取收入(Revenue)、自传播(Refer),这个五个单词的缩写,对应这一个产品生命周期中的5个重要环节。

关键词:

VS微信

微信社交的生态,微信小程序具有天然的分享属性,这个对比厂商的快应用来说是一个天然的优势

功能设计的影响

拉新
账号体系(登录/注册)

简洁高效的登录/注册机制对于用户的获取起来至关重要的影响

活跃 && 留存 && 自传播
精细运营(获取收入能力)

VS React Native && Weex

RN和Weex是跨平台应用的开发框架,从而使得Web人员基于前端技术栈可以开发移动应用

而小程序是平台,本质上提供新的交互方式的能力实现方案,是一个标准化的产品化平台产品

RN Weex跨平台开发框架可以理解是小程序的基础,小程序本身除了提供跨平台的开发框架之后,还需要负责

小程序提供了场景化的服务能力(运营能力)

RN Weex 特点

VS Javascript

使用javascript的原因是因为动态语言(脚本语言)的特征,从而可以实现动态化加载。

上一篇下一篇

猜你喜欢

热点阅读