東川印記

一本東川,笑看爭龍斗虎;寰茫兦者,度橫佰昧人生。

己亥挨虐集

2019年5月7日星期二




闲散岁中,架齐崩猝,作挨虐集




2019年04月29日15:14:42
五分钟就感觉要挂,硬是被摁住虐了俩小时。。。。
那人一边问一边写,硬是写满了三页纸啊
现在,只记得这么几个了

1,SharedPreference与线程安全
内部大量使用synchronized,但多进程操作有几率出现文件被删除
getSharePreference第一次读取磁盘文件后,后续从内存读取Map<String, Object>
apply同步回写内存,异步回写磁盘;
commit与apply的区别是等异步写磁盘任务结束后才返回;


2,String与StringBufferStringBuilder效率对比
final class String
final class StringBuffer:synchronized
final class StringBuilder:char[] value;



3,HashMap,HashTable,C...Hash对比
HashMap:非线程安全,键值允许空,jdk1.2出现;
HashTable:线程安全synchronized,键值不允许空,jdk1.0出现;
ConCurrentHashMap:线程安全的HashMap,键值不允许空,高并发性;



4,四种启动模式,Brodcast为什么要NewTask
官方developer.android.com/guide/components/tasks-and-back-stack.html?hl=zh-cn

standard
singleTop
singleTask
singleInstance

Application Context中不存在Activity任务栈,需要使用NEW_TASK来创建一个新的任务栈。
taskAffinity指定将Activity推入哪个任务栈,默认的startActivity()推入与调用方相同的任务栈;



5,Handler流程,为什么会有出现内存溢出
Handler:消息的发送与接收;

Looper:消息循环;
MessageQueue:消息队列,单链表;
Message:消息载体
简单示例dc.test.surname.xu.XuActivity


1)final class Looper:
   

/ sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
private static Looper sMainLooper;  // guarded by Looper.class

final MessageQueue mQueue;
final Thread mThread;
① 为当前线程创建一个Looper

//创建一个新的Looper并存入当前线程的ThreadLocal
private static void prepare(boolean quitAllowed) {
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));
}
          new Looper()时,创建MessageQueue,并设置当前线程变量
private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}


②消息泵,从ThreadLocal中取当前线程Looper,然后获取当前线程MessageQueue,进入死循环,不断索取,永无休止
public static void loop() {
    final Looper me = myLooper();   //myLooper()从mThreadLocal中取当前线程looper
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    final MessageQueue queue = me.mQueue;   //当前线程的消息队列

    // Make sure the identity of this thread is that of the local process,确保此线程的标识是本地进程的标识,
    // and keep track of what that identity token actually is.并跟踪该身份令牌实际上是什么。
    Binder.clearCallingIdentity();
    final long ident = Binder.clearCallingIdentity();

    // Allow overriding a threshold with a system prop. e.g.允许使用系统道具覆盖阈值。 例如
    // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'
    final int thresholdOverride =
            SystemProperties.getInt("log.looper."
                    + Process.myUid() + "."
                    + Thread.currentThread().getName()
                    + ".slow", 0);

    boolean slowDeliveryDetected = false;

    for (;;) {
        Message msg = queue.next(); // might block翻译过来是可能阻止
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // This must be in a local variable, in case a UI event sets the logger如果UI事件设置了记录器,则必须位于局部变量中
        final Printer logging = me.mLogging;
        if (logging != null) {
            logging.println(">>>>> Dispatching to " + msg.target + " " +
                    msg.callback + ": " + msg.what);
        }

        ....
        try {
            //调用发送该消息的Handler的dispatchMessage方法处理该消息
            //这里的msg.target == Handler,是在Handler发送消息的时候进行赋值的
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        }
        
        ....

        if (logging != null) {
            logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
        }

        // Make sure that during the course of dispatching the  确保在调度过程中
        // identity of the thread wasn't corrupted.线程的身份没有被破坏。
        final long newIdent = Binder.clearCallingIdentity();
        if (ident != newIdent) {
            Log.wtf(TAG, "Thread identity changed from 0x"
                    + Long.toHexString(ident) + " to 0x"
                    + Long.toHexString(newIdent) + " while dispatching to "
                    + msg.target.getClass().getName() + " "
                    + msg.callback + " what=" + msg.what);
        }

        msg.recycleUnchecked();
    }
}


2)final class MessageQueue

几个理完了,最后理这个

一般讲Handler机制的对MessageQueue都一句话带过,就这么不受重视吗,还是因为他实际靠native支持
①几个变量

// True if the message queue can be quit.用于标示消息队列是否可以被关闭,主线程的消息队列不可关闭
private final boolean mQuitAllowed;

@SuppressWarnings("unused")
private long mPtr; // used by native code该变量用于保存native代码中的MessageQueue的指针

Message mMessages;//在MessageQueue中,所有的Message是以链表的形式组织在一起的,该变量保存了链表的第一个元素,也可以说它就是链表的本身
//当Handler线程处于空闲状态的时候(MessageQueue没有其他Message时),可以利用它来处理一些事物,该变量就是用于保存这些空闲时候要处理的事务
private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>();
// 注册FileDescriptor以及感兴趣的Events,例如文件输入、输出和错误,设置回调函数,最后调用nativeSetFileDescriptorEvent注册到C++层中,
// 当产生相应事件时,由C++层调用Java的DispathEvents,激活相应的回调函数
private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;
//用于保存将要被执行的IdleHandler
private IdleHandler[] mPendingIdleHandlers;
//标示MessageQueue是否正在关闭
private boolean mQuitting;

// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.
// 标示 MessageQueue是否阻塞
private boolean mBlocked;

// The next barrier token.
// Barriers are indicated by messages with a null target whose arg1 field carries the token.
//在MessageQueue里面有一个概念叫做障栅,它用于拦截同步的Message,阻止这些消息被执行,
//只有异步Message才会放行。障栅本身也是一个Message,只是它的target为null并且arg1用于区分不同的障栅,
//所以该变量就是用于不断累加生成不同的障栅。
private int mNextBarrierToken;
②构造函数
MessageQueue(boolean quitAllowed) {
    mQuitAllowed = quitAllowed; //设置是否可以退出
    mPtr = nativeInit();        //初始化native层
}
③默认的消息插入
简化理解,就是Message里有一个next变量,根据Message的when这个时间变量,通过next形成责任链模式,人体蜈蚣那种。。。。

boolean enqueueMessage(Message msg, long when) {
    if (msg.target == null) {
        throw new IllegalArgumentException("Message must have a target.");
    }
    if (msg.isInUse()) {
        throw new IllegalStateException(msg + " This message is already in use.");
    }

    synchronized (this) {   //加锁
        if (mQuitting) {
            IllegalStateException e = new IllegalStateException(
                    msg.target + " sending message to a Handler on a dead thread");
            Log.w(TAG, e.getMessage(), e);
            msg.recycle();
            return false;
        }

        msg.markInUse();
        msg.when = when;
        Message p = mMessages;  //把之前的一个设为p
        boolean needWake;
        if (p == null || when == 0 || when < p.when) {
            // New head, wake up the event queue if blocked.
            //新头,如果被阻止则唤醒事件队列。
            msg.next = p;
            mMessages = msg;
            needWake = mBlocked;
        } else {
            // Inserted within the middle of the queue.  Usually we don't have to wake插入队列中间。 通常我们不必醒来
            // up the event queue unless there is a barrier at the head of the queue在事件队列中,除非队列头部有障碍
            // and the message is the earliest asynchronous message in the queue.并且消息是队列中最早的异步消息。
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for (;;) {
                prev = p;   //上一个设置为旧的msg
                p = p.next;//旧的设置为旧的的下一个
                if (p == null || when < p.when) { //如果旧的下一个没有或,就跳出去
                    break;
                }
                if (needWake && p.isAsynchronous()) {
                    needWake = false;
                }
            }
            msg.next = p; // invariant: p == prev.next 新消息的下一个设置为 旧的
            prev.next = msg;//上一个的下一个设置为新的消息,根据时间把消息插入合适位置
        }

        // We can assume mPtr != 0 because mQuitting is false.
        if (needWake) {
            nativeWake(mPtr);
        }
    }
    return true;
}


3)class Handler
构造函数
public Handler(Callback callback, boolean async) {
    if (FIND_POTENTIAL_LEAKS) {
        final Class<? extends Handler> klass = getClass();
        if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
                (klass.getModifiers() & Modifier.STATIC) == 0) {
            Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
                klass.getCanonicalName());
        }
    }

    mLooper = Looper.myLooper();    //获取当前线程Looper
    if (mLooper == null) {
        throw new RuntimeException(
            "Can't create handler inside thread " + Thread.currentThread()
                    + " that has not called Looper.prepare()");
    }
    mQueue = mLooper.mQueue;    //获取当前线程Looper中的MessageQueue
    mCallback = callback;
    mAsynchronous = async;
}


发送消息
final boolean sendMessage(new Message())—>
final boolean sendMessageDelayed(Message msg, long delayMillis) —>
boolean sendMessageAtTime(Message msg, long uptimeMillis:SystemClock.uptimeMillis() + delayMillis)—>
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;    //取当前进程MessageQueue
    if (queue == null) {
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}
boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)—>
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    msg.target = this;      //将发送的Message的target指向当前Handler
    if (mAsynchronous) {
        msg.setAsynchronous(true);  //设置消息异步
    }
    return queue.enqueueMessage(msg, uptimeMillis); //往当前线程的MessageQueue插入一条消息
}
MessageQueue里加入了一条消息,
在Looper.loop()死循环中,queue.next()会取到消息,之后向下直到msg.target.dispatchMessage(msg);即Handler.dispatchMessage()方法
void dispatchMessage(Message msg)—>
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);//这里是Message的Callback,也就是开启我们在postDelayed传入的Runnable
    } else {
        if (mCallback != null) {    ////mCallback:构造器传入的实现
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg); ////handler自己的handler,一般由派生之类来实现
    }
}


扩展:
1)UI线程为什么不需要手动Looper.prepare()
final class ActivityThread
public static void main(String[] args) {
    Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");

    // CloseGuard defaults to true and can be quite spammy.  We
    // disable it here, but selectively enable it later (via
    // StrictMode) on debug builds, but using DropBox, not logs.
    CloseGuard.setEnabled(false);

    Environment.initForCurrentUser();

    // Set the reporter for event logging in libcore
    EventLogger.setReporter(new EventLoggingReporter());

    // Make sure TrustedCertificateStore looks in the right place for CA certificates
    final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());
    TrustedCertificateStore.setDefaultUserDirectory(configDir);

    Process.setArgV0("<pre-initialized>");

    Looper.prepareMainLooper();

    // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.
    // It will be in the format "seq=114"
    long startSeq = 0;
    if (args != null) {
        for (int i = args.length - 1; i >= 0; --i) {
            if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {
                startSeq = Long.parseLong(
                        args[i].substring(PROC_START_SEQ_IDENT.length()));
            }
        }
    }
    ActivityThread thread = new ActivityThread();
    thread.attach(false, startSeq);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // End of event ActivityThreadMain.
    Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
}
ActivityThread作为程序的入口,已经创建了主线程Looper:Looper.prepareMainLooper();
2)上面main函数里,调用了Looper.loop(),那为什么不阻塞应用生命周期
在ActivityThread变量中,有一个这个
final H mH = new H();
这个H类,就是应用消息分发的本源吧
class H extends Handler {
    public static final int BIND_APPLICATION        = 110;
    public static final int EXIT_APPLICATION        = 111;
    public static final int RECEIVER                = 113;
    public static final int CREATE_SERVICE          = 114;
    public static final int SERVICE_ARGS            = 115;
    public static final int STOP_SERVICE            = 116;
    ....
    
    public void handleMessage(Message msg) {
        if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
        switch (msg.what) {
            case BIND_APPLICATION:
                Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
                AppBindData data = (AppBindData)msg.obj;
                handleBindApplication(data);
                Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
                break;
            case EXIT_APPLICATION:
                if (mInitialApplication != null) {
                    mInitialApplication.onTerminate();
                }
                Looper.myLooper().quit();
                break;
           //只有case,没有default
        }
        Object obj = msg.obj;
        if (obj instanceof SomeArgs) {
            ((SomeArgs) obj).recycle();
        }
        if (DEBUG_MESSAGES) Slog.v(TAG, "<<< done: " + codeToString(msg.what));
    }
}
找了找旧的教程,发现这里面明确存在LAUNCH_ACTIVITY,RESUME_ACTIVITY之类的case,新版本竟然没了。。。。

3)为什么会出现内存泄漏
private Handler handler = new Handler(){};
这句话编译器会警告 This Handler class should be static or leaks might occur (anonymous android.os.Handler) more... (⌃F1)
官方的解释:
我在Android代码库中发现了一些内存泄漏,为此我写了调试代码进行测试,像你说的一样,Message会持有一个对Handler的引用,当这个Handler是非静态内部类的时候,又会持有一个对外部类的引用(比如Activity)。如果发送一条延时的Message,由于这个Message会长期存在于队列中,就会导致Handler长期持有对Activity的引用,从而引起视图和资源泄漏。当你发送一条延时的Mesaage,并且把这个Message保存在某些地方(例如静态结构中)备用,内存泄漏会变得更加严重。

简单说,就是 Message.target 持有Handler,Handler是非静态内部类的时候会持有外部类引用,如Activity。

4)补Android消息机制时序图
Android message mechanism timing diagram
竟然被压缩了
高清无码大图备用图床


6,手写线程安全的单例
Thunderbird对格式的支持真是渣的可以啊


/**
 * 借助内部类 实现线程安全的单例模式
 * 属于懒汉式单例,因为Java机制规定,内部类SingletonHolder只有在getInstance()
 * 方法第一次调用的时候才会被加载(实现了lazy),而且其加载过程是线程安全的。
 * 内部类加载的时候实例化一次instance。
 */
private SlackInstance() {
}

private static class Holder {
    private final static SlackInstance INSTANCE = new SlackInstance();
}

public static SlackInstance getInstance() {
    return Holder.INSTANCE;
}


单例的多种写法,原来前不久就总结过啊:
dc.test.surname.sun


7,kotlin的inline
将内联函数的函数体复制到调用处实现内联
还是不能吹的太高了。。。。


8,自定义View的事件分发顺序及ViewGroup的事件分发顺序
点击之后,事件传递为 Activity —>ViewGroup—>View
1)Activity的事件分发
dispatchTouchEvent(MotionEventt)—>
    ACTION_DOWN—>onUserInteraction()
    getWindow()[PhoneWindow].superDispatchTouchEvent(MotionEvent)—>
          mDecor.superDispatchTouchEvent(MotionEvent)—>
                  ViewGroup.dispatchTouchEvent(MotionEvent);//到2

    onTouchEvent(MotionEvent); —>
        判断是否在边界外;

2)ViewGroup的事件分发
dispatchTouchEvent(MotionEvent)—>
    onInterceptTouchEvent(MotionEvent)—>
        return false;//重写此方法,决定此处是否拦截事件
    for遍历所有子View,找到当前点击的子View—>
           子View.dispatchTouchEvent(MotionEvent);//到3
            没有子View,return super(父View=View).dispatchTouchEvent(MotionEvent);

    



3)View的事件分发

dispatchTouchEvent(MotionEvent)—>
    条件:已setOnTouchListener(OnTouchListener)+控件ENABLED+实现回调OnTouchListener并return true—>

        return true;//dispatchTouchEvent结束
    上面条件不成立,进入View.onTouchEvent(MotionEvent) —>
          该View可CLICKABLE,可点击,return true;//又结束
                其中上面判断CLICKABLE后swtich判断手势,
                    ACTION_UP—>
                            performClick();
                                    分发onClick()事件;

               

9,Context继承关系
找个图先顶着


10,子线程中Toast
toast子线程报错,跟Context没关系。。。。
是因为Toast内部实现使用了Handler,用Handler是为了用队列和时间控制排队显示。。。。




11,int与Integer

基本数据类型int
封装类 Integer
java自动装箱(包装)与拆箱(解包)


12,四种引用类型
1)强引用StrongReference
2)软引用SoftReference
内存不足时回收

3)弱引用WeakReference
垃圾回收线程扫描时,回收

4)虚引用PhantomReference

任何时候都有可能被垃圾回收器回收



13,四种进程类型
1)前台进程foreground process
一般为正在与用户交互

2)可见进程visible process
可见但不可操作

3)服务进程service process
不直接可见,如数据上传下载

4)缓存/后台进程cached/background process

不可见



----------
2019年04月30日16:08:48

去员工自称某头部公司老板姓下的大厦12层还是15层面,又被鞭笞了俩小时。。。。
我特么还早去了一个半小时,真是找虐啊。。。。
结果 一上来就是 java的基础,多线程加锁,都好多年没用过了好伐。。。。
现在都喜欢一边问一边写了吗,写了大概四五页纸左右吧。。。。
14,内存泄漏与内存溢出,java与android中引发的情况
  • 内存溢出:Out of Memory,申请内存时,没有足够的空间;
  • 内存泄漏:Memory Leak,申请内存后,无法释放,最终会导致OOM;
1)java中引发内存泄漏
①静态集合
static HashMap等集合,集合内存储对象new Object();此时map会一直持有obj;
②addListener()没有删除;
③数据库连接、网络连接、IO连接没有关闭;
④单例中持有外部对象;



2)android中引发
①static Context引用Activity,如单例模式传入Activity;
②广播未注销、文件流/数据流未释放、图片Bitmap未回收、无限循环动画未停止;

③非静态内部类默认持有外部类引用,静态内部类默认不持有外部类引用;
④多线程创建 非静态内部类或匿名内部类,默认持有外部引用;
⑤非静态内部类的Handler;



15,加锁,两种加锁,手写单例及使用加锁实现单例,多种加锁类的实现方式
一个锁问了半个小时多。。。。
package dc.test.surname.he
synchronized四种修饰对象:

1)修饰一个代码块
2)修饰一个方法
3)修饰一个静态方法
4)修饰一个类



区别:
synchronized:
①执行控制,阻止其他线程获取当前对象监控锁;
②标记的变量可以被编译器优化;
③可以应用在变量,方法,类级别;
④可能会阻塞线程;


volatile:
①内存可见性;
②禁止指令重排序优化;
③仅能应用在变量级别;
④不会阻塞线程;



16,内存读写有了HashMap为什么还要再有Binder

已经有了HashMap,为什么缓存还要Binder,是因为Binder使用了Parcel
HashMap实现了Serializable,序列化使用反射机制,持久化时以IO流写入磁盘;
Parcelable以IBinder为信息载体,内存读写,但无法实现持久化,适用内存之间数据传递;



17,java的几种引用,android的几种启动模式
这个问题好,但是就 SingleTask产生分歧,到底清栈吗?
特意写了个示例试了下,会清栈的好伐,面试官的答案是错的也没法说什么啊  - -!

清栈的话 会从先入栈的开始清


18,HashMap,ArrayList,Stack,Vector
申请存储空间/扩容方式

1)LIst元素是有序的,可重复;

①ArrayList:
    初始10,1.5倍扩容int newCapacity = oldCapacity + (oldCapacity >> 1); 上限为Integer.MAX_VALUE - 8;
    线程不安全,底层数据结构为数组;

②Vector:
    初始10,1倍扩容,
    底层数据结构为数组,线程安全;
③LinkedList:双向链表



2)Map是双列集合,无序,key不可重复

①HashMap:
    初始16(2^4),数据超过元素0.75倍时,1倍扩容,上限为Integer.MAX_VALUE
②HashTable:HashMap的线程安全版,键值不允许空;
③LinkedHashMap:保存插入顺序;
④TreeMap:基于红黑二叉树的,非线程安全,不允许空;


3)Set元素无序,不可重复;
①HashSet:
    初始16,数据超过元素0.75倍时扩容,1倍扩容;
    线程不安全,底层数据结构为HashMap,实现Set接口;

②TreeSet:基于TreeMap



19,大概到android的问题了吧,include,merge,ViewStub区别
include:直接嵌入;
merge:直接嵌入子布局;
ViewStub:宽高为0,默认不可见,显示前不实例化里面布局,懒加载;



20,Handler原理,Handler post Runnable会不会引发内存泄漏

一般的handlerMessage会引发内存泄漏是因为Message.target持有Activity对象;
然后看下 post Runnable干了什么

public final boolean post(Runnable r)
{
   return  sendMessageDelayed(getPostMessage(r), 0);
}
然后 getPostMessage是从MessagePool里取的
private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
然后,看到了延迟发送,那就注定无法释放了吧



突然发现 runOnUiThread实际调用的也是Handler
public final void runOnUiThread(Runnable action) {
    if (Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}


21,如何优化内存

1)java中
  1. 减少静态变量;
  2. 适当的单例;
  3. 减少对象创建,享元模式;
  4. 尽量使用局部变量;
  5. 基本类型与包装类型以优化效率;
  6. StringBuffer与StringBuilder;
  7. 大集合时,避免多次扩容;
  8. 减少对变量重复计算,如for(;i<list.size();)时会反复计算size();
  9. 在finally块中释放资源;
  10. 使用System.arraycopy()代替循环复制;


2)android中
  1. 通过Memory Monitor dump .hprof文件查看堆分配;
  2. 通过MAT工具具体分析.hprof文件;
  3. 通过Leak库实时监测;
  4. Memory Churn内存抖动:大量对象被创建又在短时间内释放;
  5. Bitmap;
  6. 滥用后台Service;
  7. 使用优化过的集合如SparseArray,SparseBooleanArray,LongSparseArray等;
  8. 避免枚举;
  9. 去除无用library,避免巨大library,使用Proguard混淆压缩;


22,Retrofit2使用了那些设计模式
1)动态代理模式
RetrofitInstance.getInstance().getRetrofit().create(cls)
create方法通过动态代理模式

2)建造者模式
 //对象初始化
        retrofit = new Retrofit.Builder()
                .baseUrl(CandyContext.DOMAIN_BASE) // 设置 网络请求 Url
                .addConverterFactory(GsonConverterFactory.create()) //设置使用Gson解析(记得加入依赖)
                .client(getTokenClient())
                .build();


3)适配器模式

上面可以指定.addCallAdapterFactory(),此为适配器模式



23,模块化设计,两个模块中方法如何调用
注解;
路由;
AOP/DI;
AIDL/Provider;



24,Tinker合并方式
TinkerPatchService处理patch文件,拷贝到私有目录,dexdiff合并到dex,patch合并完成重启时加载;



25,其他补充
View绘制流程;
常用算法;
Activity启动流程;
MVP VS MVVM;

2019年05月07日19:03:46


想废掉Blogspot,毫无友好可言。。。。


--
senRsl
2019年04月29日15:11:02

没有评论 :

发表评论