闲散岁中,架齐崩猝,作挨虐集
2019年04月29日15:14:42
五分钟就感觉要挂,硬是被摁住虐了俩小时。。。。
那人一边问一边写,硬是写满了三页纸啊
现在,只记得这么几个了
1,SharedPreference与线程安全
3,HashMap,HashTable,C...Hash对比
HashMap:非线程安全,键值允许空,jdk1.2出现;
HashTable:线程安全synchronized,键值不允许空,jdk1.0出现;
ConCurrentHashMap:线程安全的HashMap,键值不允许空,高并发性;
4,四种启动模式,Brodcast为什么要NewTask
5,Handler流程,为什么会有出现内存溢出
Handler:消息的发送与接收;
Looper:消息循环;
MessageQueue:消息队列,单链表;
Message:消息载体
简单示例dc.test.surname.xu.XuActivity
1)final class Looper:
②消息泵,从ThreadLocal中取当前线程Looper,然后获取当前线程MessageQueue,进入死循环,不断索取,永无休止
2)final class MessageQueue
几个理完了,最后理这个
一般讲Handler机制的对MessageQueue都一句话带过,就这么不受重视吗,还是因为他实际靠native支持
①几个变量
简化理解,就是Message里有一个next变量,根据Message的when这个时间变量,通过next形成责任链模式,人体蜈蚣那种。。。。
3)class Handler
构造函数
发送消息
final boolean sendMessage(new Message())—>
final boolean sendMessageDelayed(Message msg, long delayMillis) —>
boolean sendMessageAtTime(Message msg, long uptimeMillis:SystemClock.uptimeMillis() + delayMillis)—>
在Looper.loop()死循环中,queue.next()会取到消息,之后向下直到msg.target.dispatchMessage(msg);即Handler.dispatchMessage()方法
void dispatchMessage(Message msg)—>
扩展:
1)UI线程为什么不需要手动Looper.prepare()
final class ActivityThread
2)上面main函数里,调用了Looper.loop(),那为什么不阻塞应用生命周期
在ActivityThread变量中,有一个这个
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消息机制时序图
竟然被压缩了
高清无码大图备用图床
6,手写线程安全的单例
Thunderbird对格式的支持真是渣的可以啊
单例的多种写法,原来前不久就总结过啊:
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中引发的情况
①静态集合
static HashMap等集合,集合内存储对象new Object();此时map会一直持有obj;
②addListener()没有删除;
③数据库连接、网络连接、IO连接没有关闭;
④单例中持有外部对象;
2)android中引发
①static Context引用Activity,如单例模式传入Activity;
②广播未注销、文件流/数据流未释放、图片Bitmap未回收、无限循环动画未停止;
③非静态内部类默认持有外部类引用,静态内部类默认不持有外部类引用;
④多线程创建 非静态内部类或匿名内部类,默认持有外部引用;
⑤非静态内部类的Handler;
15,加锁,两种加锁,手写单例及使用加锁实现单例,多种加锁类的实现方式
一个锁问了半个小时多。。。。
1)修饰一个代码块
2)修饰一个方法
3)修饰一个静态方法
4)修饰一个类
区别:
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干了什么
突然发现 runOnUiThread实际调用的也是Handler
21,如何优化内存
1)java中
2)android中
22,Retrofit2使用了那些设计模式
1)动态代理模式
2)建造者模式
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,毫无友好可言。。。。
2019年04月29日15:14:42
五分钟就感觉要挂,硬是被摁住虐了俩小时。。。。
那人一边问一边写,硬是写满了三页纸啊
现在,只记得这么几个了
1,SharedPreference与线程安全
内部大量使用synchronized,但多进程操作有几率出现文件被删除2,String与StringBuffer与StringBuilder效率对比
getSharePreference第一次读取磁盘文件后,后续从内存读取Map<String, Object>;
apply同步回写内存,异步回写磁盘;
commit与apply的区别是等异步写磁盘任务结束后才返回;
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:
① 为当前线程创建一个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;
new Looper()时,创建MessageQueue,并设置当前线程变量//创建一个新的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)); }
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)—>
boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis)—>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); }
MessageQueue里加入了一条消息,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插入一条消息 }
在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
ActivityThread作为程序的入口,已经创建了主线程Looper:Looper.prepareMainLooper();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"); }
2)上面main函数里,调用了Looper.loop(),那为什么不阻塞应用生命周期
在ActivityThread变量中,有一个这个
这个H类,就是应用消息分发的本源吧final H mH = new H();
找了找旧的教程,发现这里面明确存在LAUNCH_ACTIVITY,RESUME_ACTIVITY之类的case,新版本竟然没了。。。。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)); } }
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消息机制时序图
竟然被压缩了
高清无码大图备用图床
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;
①静态集合
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干了什么
然后 getPostMessage是从MessagePool里取的public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
然后,看到了延迟发送,那就注定无法释放了吧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中
- 减少静态变量;
- 适当的单例;
- 减少对象创建,享元模式;
- 尽量使用局部变量;
- 基本类型与包装类型以优化效率;
- StringBuffer与StringBuilder;
- 大集合时,避免多次扩容;
- 减少对变量重复计算,如for(;i<list.size();)时会反复计算size();
- 在finally块中释放资源;
- 使用System.arraycopy()代替循环复制;
2)android中
- 通过Memory Monitor dump .hprof文件查看堆分配;
- 通过MAT工具具体分析.hprof文件;
- 通过Leak库实时监测;
- Memory Churn内存抖动:大量对象被创建又在短时间内释放;
- Bitmap;
- 滥用后台Service;
- 使用优化过的集合如SparseArray,SparseBooleanArray,LongSparseArray等;
- 避免枚举;
- 去除无用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
senRsl
2019年04月29日15:11:02
没有评论 :
发表评论