自己一直做Java、Android相关的知识总结->KnowledgeSummary系列。这个GitHub的repo想作为自己对于Android核心知识点以及一些面试题的总结,因为很多知识点理解的不够深刻,所以通过这个来强迫自己做总结,目前已经初具规模,我基本每天都会更新这个repo,后续还会持续更新下去,大家有兴趣可以点个star关注下,感谢。
在做这个知识总结系列,发现有的比较零散的面试题不太适合开个doc文档来写,于是想着把这些零散的面试题做个总结,所以想收录在这篇文章里面,并且每个面试题尽量提供一个参考答案,因为如果只是把面试题列出来,我举得一点意思都没有,这样的面试题网上一搜一大把,所以我希望为每一个面试题提供一个参考,但不能保证这些答案完全正确。
我的目标是这份面试题能够长期更新,因为比较多,暂时就不分类了。
原文链接
如何计算一个View的层级
Art和Dalvik区别
socket判断http请求或http响应的传输结束
OnLowMemory和OnTrimMemory的比较
BlockCanary核心原理分析
onRestart 什么时候调用
Handler 如何防止内存泄漏
TCP可靠性的保证机制总结
synchronized 和 ReentrantLock 的区别和使用选择
synchronized,volitale的异同
SparseArray和ArrayMap使用场景
Android 中 getRawX()和 getX()区别
Android中的MotionEvent手势事件
onInterceptTouchEvent()函数与 onTouchEvent()的区别
Http 1.0和Http 2.0的区别
View.getLocationInWindow和View.getLocationOnScreen区别
http和https的区别
为什么要有内部类,静态内部类和普通内部类区别是什么
Java中四种线程池的总结
Intent传递数据的限制大小
onStartCommand 的几种模式
RelativeLayout的onMeasure方法是怎么Measure的
主线程的死循环是否一致耗费CPU资源
Service 和 IntentService 的区别
SQLite增删改查以及升级的sql语句
Service的生命周期Activity之间的通信方式
SurfaceView和TextureView的区别
onSaveInstanceState和onRestoreInstanceState调用时机
LeakCanary原理
Android系统为什么会设计ContentProvider
Service和Activity通信
OOM 是否可以try catch
AlertDialog,Toast 对Activity生命周期的影响
HTTP与TCP的区别和联系
viewstub可以多次inflate么?多次inflate会怎样?
onWindowFocusChanged 执行时机
【参考】:
int i = 0;
private void getParents(ViewParent view){
if (view.getParent() == null) {
Log.v("tag", "最终==="+i); return;
}
i++;
ViewParent parent = view.getParent();
Log.v("tag", "i===="+i);
Log.v("tag", "parent===="+parent.toString());
getParents(parent);
}
因为
public abstract class ViewGroup extends View implements ViewParent
,
ViewGroup是ViewParent的实现类,所以可以直接转, LinearLayout是ViewGroup 的子类。
【参考】:
Android 4.4发布了一个ART运行时,准备用来替换掉之前一直使用的Dalvik虚拟机
Art: Android Runtime,编译机制:AOT,预编译机制
Dalvik:编译机制:JIT,即时编译
ART 的机制与 Dalvik 不同。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(JIT,Just-In-Time)转换为机器码,这会拖慢应用的运行效率,而在ART 环境中,应用在安装的时候,字节码就会预先编译成机器码
,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。
什么是Dalvik
Dalvik是Google公司自己设计用于Android平台的Java虚拟机。Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的核心组成部分之一,它可以支持已转换为.dex(即Dalvik Executable)格式的Java应用程序的运行,.dex格式是专为Dalvik应用设计的一种压缩格式,适合内存和处理器速度有限的系统。Dalvik经过优化,允许在有限的内存中同时运行多个虚拟机的实例,并且每一个Dalvik应用作为独立的Linux进程执行。独立的进程可以防止在虚拟机崩溃的时候所有程序都被关闭。
什么是ART
Android操作系统已经成熟,Google的Android团队开始将注意力转向一些底层组件,其中之一是负责应用程序运行的Dalvik运行时。Google开发者已经花了两年时间开发更快执行效率更高更省电的替代ART运行时。ART代表Android Runtime,其处理应用程序执行的方式完全不同于Dalvik,Dalvik是依靠一个Just-In-Time(JIT)编译器去解释字节码。开发者编译后的应用代码需要通过一个解释器在用户的设备上运行,这一机制并不高效,但让应用能更容易在不同硬件和架构上运行。ART则完全改变了这套做法,在应用安装的时候就预编译字节码到机器语言,这一机制叫Ahead-Of-Time(AOT)编译。在移除解释代码这一过程后,应用程序执行将更有效率,启动更快。
ART优点:
ART缺点:
【参考】:
先把 header 直到\r\n\r\n 整个地址记录下来
如果是短连接,没有启用 keepalive,则可以通过是否关闭了连接来判断是否传输结束, 即在读取时可判断 read() != -1。传输完毕就关闭 connection,即 recv 收到 0 个字节。
如果是长连接,那么一个 socket(tcp)可能发送和接收多次请求,那么如何判断每次的 响应已经接收?
- 先读请求头,一直到\r\n\r\n 说明请求头结束,然后解析 http 头,如果 Content-Length=x 存在,则知道 http 响应的长度为 x。从头的末尾直接读取 x 字节就是响应内容。
- 如果 Content-Length=x 不存在,那么头类型为 Transfer-Encoding: chunked 说明响应 的长度不固定,则在响应头结束后标记第一段流的长度,即直到流里有\r\n0\r\n\r\n 结束
- 如果 recv 返回 SOCKET_ERROR 时,说明对方已经断开连接,但是可能是非正常断开 (断网或者客户端进程结束
【参考】:
OnLowMemory 被回调时,已经没有后台进程;而 onTrimMemory 被回调时,还有后台进程。
OnLowMemory 是在最后一个后台进程被杀时调用,一般情况是 low memory killer 杀进程 后触发;而 OnTrimMemory 的触发更频繁,每次计算进程优先级时,只要满足条件,都会触发。
通过一键清理后,OnLowMemory 不会被触发,而 OnTrimMemory 会被触发一次。
【参考】:
public static void loop() {
final Looper me = myLooper();
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();
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
final Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;
final long traceTag = me.mTraceTag;
if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
}
final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
final long end;
try {
msg.target.dispatchMessage(msg);
end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
} finally {
if (traceTag != 0) {
Trace.traceEnd(traceTag);
}
}
if (slowDispatchThresholdMs > 0) {
final long time = end - start;
if (time > slowDispatchThresholdMs) {
Slog.w(TAG, "Dispatch took " + time + "ms on "
+ Thread.currentThread().getName() + ", h=" +
msg.target + " cb=" + msg.callback + " msg=" + msg.what);
}
}
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();
}
}
原理:我们只需要计算打印这两天 log 的时间差,就能得到 dispatchMessage 的耗时,android 提供了 Looper.getMainLooper().setMessageLogging(Printer printer)来设置这个 logging 对 象,所以只要自定义一个 Printer,然后重写 println(String x)方法即可实现耗时统计了。
通过在 DispatchMessage 的方法的执行时间,来判断卡顿,管是哪种回调方式,回调一定发 生在 UI 线程。因此如果应用发生卡顿,一定是在 dispatchMessage 中执行了耗时操作。我 们通过给主线程的 Looper 设置一个 Printer,打点统计 dispatchMessage 方法执行的时间, 如果超出阀值,表示发生卡顿,则 dump 出各种信息,提供开发者分析性能瓶颈。 mBlockListener.onBlockEvent。
【参考】:
【参考】:
除了写弱引用这个方法后,还有一个就是 handler.removeCallbacksAndMessages(null);,就 是移除所有的消息和回调,简单一句话就是清空了消息队列。注意,不要以为你 post 的是 个 Runnable 或者只是 sendEmptyMessage。你可以看一下源码,在 handler 里面都是会把 这些转成正统的 Message,放入消息队列里面,所以清空队列就意味着这个 Handler 直接被 打成原型了,当然也就可以回收了。
【参考】:
【参考】:
- 不能中断一个正在试图获得锁的线程
- 试图获得锁时不能像 ReentrantLock 中的 trylock 那样设定超时时间 ,当一个线程获得了对象锁后,其他线程访问这个同步方法时,必须等待或阻塞,如果那个线程发生了死循环, 对象锁就永远不会释放
- 每个锁只有单一的条件,不像 condition 那样可以设置多个
- 如果 synchronized 关键字适合程序,尽量使用它,可以减少代码出错的几率和代码数量; (减少出错几率是因为在执行完 synchronized 包含完的最后一句语句后,锁会自动释放, 不需要像 ReentrantLock 一样手动写 unlock 方法;)
- 如果特别需要 Lock/Condition 结构提供的独有特性时,才使用他们;(比如设定一个线程 长时间不能获取锁时设定超时时间或自我中断等功能。)
- 许多情况下可以使用 java.util.concurrent 包中的一种机制,它会为你处理所有的加锁情况; (比如当我们在多线程环境下使用 HashMap 时,可以使用 ConcurrentHashMap 来处理多线程并发)
【参考】:
【参考】:
假设数据量都在千级以内的情况下:
【参考】:
【参考】:
【参考】:
HTTP1.0
无状态、无连接
HTTP1.1
持久连接
请求管道化
增加缓存处理(新的字段如 cache-control)
增加 Host 字段、支持断点传输等(把文件分成几部分)
HTTP2.0
二进制分帧
多路复用(或连接共享)
头部压缩
【参考】:
【参考】:
https 通信过程
HTTPS 和 HTTP 的区别主要如下:
内部类和外部类区别
静态内部类和普通内部类
静态内部类与非静态内部类之间存在一个最大的区别,我们知道非静态内部类在编译完成之 后会隐含地保存着一个引用,该引用是指向创建它的外围内。 但是静态内部类却没有。没有这个引用就意味着:
静态内部类的创建是不需要依赖于外围类,可以直接创建 静态内部类不可以使用任何外围类的非 static 成员变量和方法,而内部类则都可以
注意:
【参考】:
具体的 4 种常用的线程池实现如下:(返回值都是 ExecutorService)
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit, BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
}
我们执行线程时都会调用到 ThreadPoolExecutor 的 execute()方法,现在我们来看看这个方法的源码(就是下面这段代码了,这里面有一些注释解析),我直接来解释一下吧:在这段代码中我们至少要看懂一个逻辑:当当前线程数小于核心池线程数时,只需要添加一个线程并且启动它,如果线程数数目大于核心线程池数目,我们将任务放到 workQueue中,如果连WorkQueue满了,那么就要拒绝任务了。
【参考】:
用Intent传递数据,实际上走的是跨进程通信(IPC),跨进程通信需要把数据从内核copy到进程中,每一个进程有一个接收内核数据的缓冲区,默认是1M;如果一次传递的数据超过限制,就会出现异常。
不同厂商表现不一样有可能是厂商修改了此限制的大小,也可能同样的对象在不同的机器上大小不一样。
【参考】:
START_NOT_STICKY
如果返回 START_NOT_STICKY,表示当 Service 运行的进程被 Android 系统强制杀掉之后, 不会重新创建该Service。当然如果在其被杀掉之后一段时间又调用了startService,那么该 Service 又将被实例化。那什么情境下返回该值比较恰当呢?
如果我们某个 Service 执行的工作被中断几次无关紧要或者对 Android 内存紧张的情况下需 要被杀掉且不会立即重新创建这种行为也可接受,那么我们便可将 onStartCommand 的返 回值设置为 START_NOT_STICKY。
举个例子,某个 Service 需要定时从服务器获取最新数据:通过一个定时器每隔指定的 N 分 钟让定时器启动 Service 去获取服务端的最新数据。当执行到 Service 的 onStartCommand 时,在该方法内再规划一个 N 分钟后的定时器用于再次启动该 Service 并开辟一个新的线程 去执行网络操作。假设 Service 在从服务器获取最新数据的过程中被 Android 系统强制杀掉, Service 不会再重新创建,这也没关系,因为再过 N 分钟定时器就会再次启动该 Service 并重 新获取数据。
START_STICKY
如果返回 START_STICKY,表示 Service 运行的进程被 Android 系统强制杀掉之后,Android 系统会将该 Service 依然设置为 started 状态(即运行状态),但是不再保存 onStartCommand 方法传入的 intent 对象,然后 Android 系统会尝试再次重新创建该 Service,并执行 onStartCommand 回调方法,但是 onStartCommand 回调方法的 Intent 参数为 null,也就是 onStartCommand 方法虽然会执行但是获取不到 intent 信息。如果你的 Service 可以在任意 时刻运行或结束都没什么问题,而且不需要 intent 信息,那么就可以在 onStartCommand 方 法中返回 START_STICKY,比如一个用来播放背景音乐功能的 Service 就适合返回该值。
START_REDELIVER_INTENT
如果返回 START_REDELIVER_INTENT,表示 Service 运行的进程被 Android 系统强制杀掉之后,与返回START_STICKY的情况类似,Android 系统会将再次重新创建该 Service,并执行 onStartCommand 回调方法,但是不同的是,Android 系统会再次将 Service 在被杀掉之前 最后一次传入 onStartCommand 方法中的 Intent 再次保留下来并再次传入到重新创建后的 Service 的 onStartCommand 方法中,这样我们就能读取到 intent 参数。只要返回 START_REDELIVER_INTENT,那么 onStartCommand 重的 intent 一定不是 null。如果我们的 Service 需要依赖具体的 Intent 才能运行(需要从 Intent 中读取相关数据信息等),并且在强 制销毁后有必要重新创建运行,那么这样的 Service 就适合返回 START_REDELIVER_INTENT
【参考】:
发现 RelativeLayout 会根据 2次排列的结果对子View各做一次 measure。 而在做横向的测量时,纵向的测量结果尚未完成,只好暂时使用 myHeight 传入子View系统。
这样必然会导致子View的高度和 RelativeLayout 的高度不同时,第 3 点中所说的优化会失效,在View系统足够复杂时,效率问题就会出现。
【参考】:
在主线程使用 Looper.loop 可以保证主线程一直在运行,事实上,在 Looper.loop 死循环之 前,已经创建了一个 Binder 线程:
thread.attach 会建立 Binder 通道,创建新线程。attach(false)会创建一个 ApplicaitonThread 的 Binder 线程,用于接受 AMS 发来的消息,该 Binder 线程通过 ActivityThread 的 H 类型 Handler 将消息发送给主线程。ActivityThread 并不是线程类,只是它运行在主线程。
Handler 底层采用 Linux 的 pipe/epoll 机制,MessageQueue 没有消息的时候,便阻塞在 Looper.mQueue.next 方法中,此时主线程会释放 CPU 资源进入休眠,直到下个事件到达, 当有新消息的时候,通过往 pipe 管道写数据来唤醒主线程工作。所以主线程大多数时候处 于休眠状态,不会阻塞。
【参考】:
【参考】:
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
this(context, name, factory, version, null);
}
public SQLiteDatabase getWritableDatabase() {
synchronized (this) {
return getDatabaseLocked(true);
}
}
private SQLiteDatabase getDatabaseLocked(boolean writable) {
.......
db.beginTransaction();
try {
if (version == 0) {
onCreate(db);
} else {
if (version > mNewVersion) {
onDowngrade(db, version, mNewVersion);
} else {
onUpgrade(db, version, mNewVersion);
}
}
db.setVersion(mNewVersion);
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
}
onUpgrade()会被触发,可以在该方法中编写数据库升级逻辑。具体的数据库升级逻辑示例可参考这里.
常用的SQL增删改查:
【参考】:
Service是运行在主线程中的,即主线程。
Service的生命周期
【参考】:
【参考】:
相同点:都继承于View,可在独立线程绘制和渲染。
不同点:
从性能和安全性角度出发,使用播放器优先选SurfaceView。
1.在android 7.0上系统surfaceview的性能比TextureView更有优势,支持对象的内容位置和包含的应用内容同步更新,平移、缩放不会产生黑边。 在7.0以下系统如果使用场景有动画效果,可以选择性使用TextureView
2.SurfaceView优点及缺点优点:可以在一个独立的线程中进行绘制,不会影响主线程,使用双缓冲机制,播放视频时画面更流畅
缺点:Surface不在View hierachy中,它的显示也不受View的属性控制,所以不能进行平移,缩放等变换,也不能放在其它ViewGroup中。SurfaceView 不能嵌套使用
TextureView优点及缺点
优点:支持移动、旋转、缩放等动画,支持截图
缺点:必须在硬件加速的窗口中使用,占用内存比SurfaceView高,在5.0以前在主线程渲染,5.0以后有单独的渲染线程
【参考】:
03-09 12:14:32.529 2298-2298/com.example.myapplication I/MY_TEST: onPause
03-09 12:14:32.556 2298-2298/com.example.myapplication I/MY_TEST: onCreate2
03-09 12:14:32.557 2298-2298/com.example.myapplication I/MY_TEST: onStart2
03-09 12:14:32.557 2298-2298/com.example.myapplication I/MY_TEST: onResume2
03-09 12:14:32.981 2298-2298/com.example.myapplication I/MY_TEST: onSaveInstanceState 一个参数
03-09 12:14:32.981 2298-2298/com.example.myapplication I/MY_TEST: onStop
分割线-------------------------------------------------------------------------
03-09 12:15:28.715 2298-2298/com.example.myapplication I/MY_TEST: onPause2
03-09 12:15:28.763 2298-2298/com.example.myapplication I/MY_TEST: onCreate
03-09 12:15:28.764 2298-2298/com.example.myapplication I/MY_TEST: onStart
03-09 12:15:28.764 2298-2298/com.example.myapplication I/MY_TEST: onRestoreInstanceState
03-09 12:15:28.767 2298-2298/com.example.myapplication I/MY_TEST: onActivityResult
03-09 12:15:28.767 2298-2298/com.example.myapplication I/MY_TEST: onResume
03-09 12:15:29.141 2298-2298/com.example.myapplication I/MY_TEST: onStop2
03-09 12:15:29.141 2298-2298/com.example.myapplication I/MY_TEST: onDestroy2
【参考】:
弱引用WeakReference
被强引用的对象就算发生 OOM 也永远不会被垃圾回收机回收;
被弱引用的对象,只要被垃圾回收器发现就会立即被回收;
被软引用的对象,具备内存敏感性,只有内存不足时才会 被回收,常用来做内存敏感缓存器;
虚引用则任意时刻都可能被回收,使用较少。
引用队列ReferenceQueue
我们常用一个 WeakReference reference = new WeakReference(activity);,这里我们创建了 一个 reference 来弱引用到某个 activity,当这个 activity 被垃圾回收器回收后,这个 reference 会被放入内部的 ReferenceQueue 中。也就是说,从队列 ReferenceQueue 取出 来的所有 reference,它们指向的真实对象都已经成功被回收了。
【参考】:
在开发中,假如,A、B 进程有部分信息需要同步,这个时候怎么处理呢?设想这么一个场 景,有个业务复杂的 Activity 非常占用内存,并引发 OOM,所以,想要把这个 Activity 放到 单独进程,以保证 OOM 时主进程不崩溃。但是,两个整个 APP 有些信息需要保持同步,比 如登陆信息等,无论哪个进程登陆或者修改了相应信息,都要同步到另一个进程中去,这个 时候怎么做呢?
第一种:一个进程里面的时候,经常采用SharePreference 来做,但是SharePreference 不支持多进程,它基于单个文件的,默认是没有考虑同步互斥,而且,APP对SP对象做了缓存, 不好互斥同步,虽然可以通过 FileLock 来实现互斥,但同步仍然是一个问题。 第二种:基于 Binder 通信实现 Service 完成跨进程数据的共享,能够保证单进程访问数据, 不会有互斥问题,可是同步的事情仍然需要开发者手动处理。
第三种:基于Android提供的ContentProvider来实现,ContentProvider 同样基于Binder, 不存在进程间互斥问题,对于同步,也做了很好的封装,不需要开发者额外实现。
ContentProvider 只是 Android 为了跨进程共享数据提供的一种机制, 本身基于 Binder 实现,
在操作数据上只是一种抽象,具体要自己实现
ContentProvider 只能保证进程间的互斥,无法保证进程内,需要自己实现
ContentResolver 接口的 notifyChange 函数来通知那些注册了监控特定 URI 的 ContentObserver 对象,使得它们可以相应地执行一些处理。ContentObserver 可以通过 registerContentObserver 进行注册。
既然是对外提供数据共享,那么如何限制对方的使用呢?
android:exported 属性非常重要。这个属性用于指示该服务是否能够被其他应用程序组件调 用或跟它交互。如果设置为 true,则能够被调用或交互,否则不能。设置为 false 时,只有 同一个应用程序的组件或带有相同用户 ID 的应用程序才能启动或绑定该服务。
【参考】:
Activity 调用 bindService (Intent service, ServiceConnection conn, int flags)方法,得到 Service 对象的一个引用,这样 Activity 可以直接调用到 Service 中的方法,如果要主动通知 Activity, 我们可以利用回调方法
Service 向 Activity 发送消息,可以使用广播,当然 Activity要注册相应的接收器。比如 Service 要向多个 Activity发送同样的消息的话,用这种方法就更好。
【参考】:
一般不适合这么处理
Java 中管理内存除了显式地 catch OOM 之外还有更多有效的方法:比如 SoftReference, WeakReference, 硬盘缓存等。
在 JVM 用光内存之前,会多次触发 GC,这些 GC 会降低程序运行的效率。
如果 OOM 的原因不是 try 语句中的对象(比如内存泄漏),那么在 catch 语句中会继续抛出 OOM
【参考】:
无论 Dialog 弹出覆盖页面,对 Activity 生命周期没有影响,只有再启动另外一个 Activity 的时候才会进入 onPause 状态,而不是想象中的被覆盖或者不可见,同时通过 AlertDialog 源码或者 Toast 源码我们都可以发现它们实现的原理都是 windowmanager.addView();来添加的, 它们都是一个个 view ,因此不会对 activity 的生命周 期有任何影响。
【参考】:
TCP 连接
手机能够使用联网功能是因为手机底层实现了 TCP/IP 协议,可以使手机终端通过无线网络,建立 TCP 连接。TCP 协议可以对上层网络提供接口,使上层网络数据的传输建立在“无差别” 的网络之上。
建立起一个 TCP 连接需要经过“三次握手”:
握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传递数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求,断开过程需要经过“四次挥手”。
HTTP 连接
HTTP 连接最显著的特点是客户端发送的每次请求都需要服务器回送响应,在请求结束后,
理想状态下,TCP 连接一旦建立,在通信双方中的任何一方主动关闭连 接之前,TCP 连接
都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开 TCP 连接的请求,
断开过程需要经过“四次握手”(过程就不细写 了,就是服务器和客户端交互,最终确定断
开)
HTTP 协议即超文本传送协议(Hypertext Transfer Protocol ),是 Web 联网的基础,也是手
机联网常用的协议之一,HTTP 协议是建立在 TCP 协议之上的一种应用。
会主动释放连接。从建立连接到关闭连接的过程称为“一次连接”。
TPC/IP 协议是传输层协议,主要解决数据如何在网络中传输,而 HTTP 是应用层协议,主要 解决如何包装数据。TCP 协议对应于传输层,而 HTTP 协议对应于应用层,从本质上来说, 二者没有可比性。Http 协议是建立在 TCP 协议基础之上的,当浏览器需要从服务器获取网 页数据的时候,会发出一次 Http 请求。Http 会通过 TCP 建立起一个到服务器的连接通道。 简单地说,当一个网页打开完成后,客户端和服务器之间用于传输 HTTP 数据的 TCP 连接不会关闭,如果客户端再次访问这个服务器上的网页,会继续使用这一条已经建立的连接
Keep-Alive 不会永久保持连接,它有一个保持时间,可以在不同的服务器软件(如 Apache)
中设定这个时间。Http 是无状态的短连接,而 TCP 是有状态的长连接。
【参考】:
那么多进程应该能为我们带来什么呢?
我们都知道,android 平台对应用都有内存限制,其实这个理解有点问题,应该是说 android 平台对每个进程有内存限制,比如某机型对对进程限制是 24m,如果应用有两个进程,则该应该的总内存限制是 2*24m。使用多进程就可以使得我们一个 apk 所使用的内存限制加大几倍。 所以可以借此图片平台对应用的内存限制,比如一些要对图片、视频、大文件进程处理的好内存的应用可以考虑用多进程来解决应用操作不流畅问题。
开启多进程模式:
在 Android 中使用多进程只有一种方法,那就是在 AndroidManifest 中给四大组件 (Activity,Service,Receiver,ContentProvider)指定 android:process 属性.除此之外没有其他的办 法,也就是说我们无法给一个线程活一个实体类指定其运行时所在的进程.其实还有另一种非常规的多进程方法,那就是通过 JNI 在 native 层去 fork一个新的进程,但这种方法属于特殊情 况,并不是常用的创建多进程的方式,所以我们也暂不考虑这种情况
进程名以":“开头的进程属于当前应用的私有进程,其他应用的组件不可以和它跑在同一个进 程中,而进程名不以”:"开头的进程属于全局进程,其他应用通过 ShareUID 方式可以和它跑在 同一个进程中.
用多进程的好处与坏处
好处:
1)分担主进程的内存压力。 当应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的 内存空间了。当然还有其他好处,有心人会发现 2)使应用常驻后台,防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方 被杀就重新启动它。Android 后台进程里有很多应用是多个进程的,因为它们要常驻后台,特别是即时通讯或者社交应用,不过现在多进程已经被用烂了。
典型用法是在启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情, 或者开机启动这个进程,然后做监听等。
坏处:消耗用户的电量。
多占用了系统的空间,若所有应用都这样占用,系统内存很容易占满而导致卡顿。 应用程序架构会变得复杂,因为要处理多进程之间的通信。这里又是另外一个问题了。
多进程的缺陷 进程间的内存空间是不可见的。开启多进程后,会引发以下问题:
1)Application 的多次重建。 2)静态成员的失效。 3)文件共享问题。 4)断点调试问题。
【参考】:
在使用 viewstub 的时候要注意一点,viewstub 只能 inflate 一次,而且 setVisibility 也会间接的调用到 inflate,重复 inflate 会抛出异常: java.lang.IllegalStateException:ViewStub must have a non-null ViewGroup viewParent。
解决方法为设置一个 Boolean 类型的变量,标记 viewstub 是否已经 inflate,如果 viewstub 还未 inflate 则执行初始化操作,反之则不进行操作。其中要使用 ViewStub 中的 OnInflateListener()监听事件来判断是否已经填充,从而保证 viewstub 不重复的 inflate。
解决方法:
1.定义 boolean 变量和 ViewStub boolean isInflate = false; ViewStub mViewStub;
2.初始化 ViewStub,并为 ViewStub 添加 OnInflateListener()监听事件
mViewStub = (ViewStub)findViewById(R.id.viewstub_match_single);
mViewStub.setOnInflateListener(new OnInflateListener() { @Override
public void onInflate(ViewStub stub, View inflated) {
isInflate = true; }
});
3.填充 ViewStub
private void initViewStub(){//填充 ViewStub 的方法
if(!isInflate){//如果没有填充则执行 inflate 操作
View view = stubMatchSingle.inflate();
//初始化 ViewStub 的 layout 里面的控件
TextView mTv = (TextView) view.findViewById(R.id.txt_url); mTv.setOnClickListener(this);
}
}
【参考】:
android 在不加载图片的前提下获得图片的宽高
public static int[] getImageWidthHeight(String path){
BitmapFactory.Options options = new BitmapFactory.Options();
/**
* 最关键在此,把 options.inJustDecodeBounds = true;
* 这里再 decodeFile(),返回的 bitmap 为空,但此时调用 options.outHeight 时,已经
包含了图片的高了 */
options.inJustDecodeBounds = true;
Bitmapbitmap=BitmapFactory.decodeFile(path,options);// 此时返回的bitmap为null /**
/*
*options.outHeight 为原始图片的高
*/
return new int[]{options.outWidth,options.outHeight};
}
通过 BitmapFactory 从不同位置获取 Bitmap
【参考】:
真正的 visible 时间点是 onWindowFocusChanged()函数被执行时,当前窗体得到
或失去焦点的时候的时候调用。这是这个活动是否是用户可见的最好的指标。