本文是Android面试题整理中的一篇,结合右下角目录食用更佳,包括:html
- File
- SharedPreferences
- SQlite
- 网络
- ContentProvider
- 默认不是
- 能够设置模式MODE_MULTI_PROCESS作到进程同步,但由于该模式有不少坑,已经被Google弃用
- 官方建议使用ContentProvider
- commit是同步的提交,这种方式很经常使用,在比较早的SDK版本中就有了。这种提交方式会阻塞调用它的线程,而且这个方法会返回boolean值告知保存是否成功(若是不成功,能够作一些补救措施)。
- apply是异步的提交方式,目前Android Studio也会提示你们使用这种方式
- 文件存储分为内部存储和外部存储
- 内部存储
- Environment.getDataDirectory() = /data //这个方法是获取内部存储的根路径
- getFilesDir().getAbsolutePath() = /data/user/0/packname/files //这个方法是获取某个应用在内部存储中的files路径
- getCacheDir().getAbsolutePath() = /data/user/0/packname/cache //这个方法是获取某个应用在内部存储中的cache路径
- getDir(“myFile”, MODE_PRIVATE).getAbsolutePath() = /data/user/0/packname/app_myFile
- 外部存储
- Environment.getExternalStorageDirectory().getAbsolutePath() = /storage/emulated/0 //这个方法是获取外部存储的根路径
- Environment.getExternalStoragePublicDirectory(“”).getAbsolutePath() = /storage/emulated/0 这个方法是获取外部存储的根路径 3. getExternalFilesDir(“”).getAbsolutePath() = /storage/emulated/0/Android/data/packname/files 这个方法是获取某个应用在外部存储中的files路径 4. getExternalCacheDir().getAbsolutePath() = /storage/emulated/0/Android/data/packname/cache 这个方法是获取某个应用在外部存储中的cache路径
- 清楚数据和卸载APP时, 内外存储的file和cache都会被删除
- 内部存储file和cache不须要权限;外部存储低版本上(19如下)file和cache须要权限,高版本不须要权限;Environment.getExternalStorageDirectory()须要权限
- SQLite每一个数据库都是以单个文件(.db)的形式存在,这些数据都是以B-Tree的数据结构形式存储在磁盘上。
- 使用SQLiteDatabase的insert,delete等方法或者execSQL方法默认都开启了事务,若是操做顺利完成才会更新.db数据库。事务的实现是依赖于名为rollback journal文件,借助这个临时文件来完成原子操做和回滚功能。在/data/data//databases/目录下看到一个和数据库同名的.db-journal文件。
db.beginTransaction();
try {
...
db.setTransactionSuccessful();
} finally {
db.endTransaction();
}
复制代码
- Android中提供了SqLiteOpenHelper类,当版本更新时,会自动调用onUpgrade方法,咱们在此方法中升级
- 若是修改已有表,能够才有临时表的方法:
- 将已有表重命名成一个临时表
- 建立新表
- 拷贝
- 删除临时表
- 把数据库db文件放在res/raw下打包进apk
- 经过FileInputStream读取db文件,经过FileOutputStream将文件写入/data/data/包名/database下
- gravity是控制当前View内布局的位置
- layout_gravity是控制View在父布局中的位置
从ViewRootImpl的performTraversals开始,通过measure,layout,draw 三个流程。draw流程结束之后就能够在屏幕上看到view了。android
- View的MeasureSpec由父容器的MeasureSpec和其自身的LayoutParams共同肯定,
- 而对于DecorView是由它的MeasureSpec由窗口尺寸和其自身的LayoutParams共同肯定。
- measure -> onMeasure
- layout(onLayout方法是空的,由于他没有child了)
- draw -> ondraw
- measure -> onMeasure (onMeasure中须要调用childView的measure计算大小)
- layout -> onLayout (onLayout方法中调用childView的layout方法)
- draw -> onDraw (ViewGroup通常不绘制本身,ViewGroup默认实现dispatchDraw去绘制孩子)
- drawbackground
- 若是要视图显示渐变框,这里会作一些准备工做
- draw自身内容
- drawChild
- 若是须要, 绘制当前视图在滑动时的边框渐变效果
- 绘制装饰,如滚动条
RemoteView:RemoteViews实现了Parcelable接口,经过binder机制传递给远程进程,进程间view的显示git
- 为了解决多点触控问题,android在MotionEvent中引入了pointer概念
- 经过ACTION_DOWN、ACTION_POINTER_DOWN、ACTION_MOVE、ACTION_POINTER_UP、ACTION_UP来检测手机的动做
- 每一个手指的位置能够经过getX(pointIndex)来得到,这样咱们就能判断出滑动的距离
- 缩放有多种实现: 1. ImageView能够经过setImageMatrix(martix)来实现 2. 自定义View能够缩放Canvas的大小 3. 还能够设置LayoutParams来改变大小
- Scroller 一般用来实现平滑的滚动
- 实现平滑滚动:
- 新建Scroller,并设置合适的插值器
- 在View的computeScroll方法中调用scroller,查看当前应该滑动到的位置,并调用view的scrollTo或者scrollBy方法滑动
- 调用Scroller的start方法开始滑动
- 滑动时会调用onScrollChange方法,在该方法中监听状态
- 判断childView.getMeasureHeight(总高度) == getScrollY(滑动的高度) + chilView.getHeight(可见高度)
- 思想:委托子View处理,子View不能处理则本身处理
- 委托过程:activity -> window -> viewGroup -> view
- 处理事件方法的优先级:onTouchListener > onTouchEvent > onClickListener
伪代码
public boolean dispatchTouchEvent(MotionEvent ev){
boolean consume = false;
if(onInterceptTouchEvent(ev)){
consume = onTouchEvent(ev)
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
复制代码
- 完整的事件一般包括Down、Move、Up,当down事件被拦截下来之后,move和up就再也不走intercept方法,而是直接被传递给当前view处理
- 一个点击或者活动事件包含ACTION_DOWN,ACTION_MOVE,ACTION_UP等
- 当子View处理了ACTION_DOWN事件以后,后续的ACTION_MOVE,ACTION_UP都会直接交由这个子View处理
- 若是此时父View拦截了ACTION_MOVE,ACTION_UP,那么子View会收到一个ACTION_CANCEL
- 场景举例:点击一个控件,并滑动到控件外,此时次控件会收到一个ACTION_CALNCEL
外部拦截:重写onInterceptTouchEvent方法
内部拦截:重写dispatchTouchEvent方法,同时配合requestDisAllowInterceptTouchEvent方法github
- 复用convertView,对convetView进行判空,当convertView不为空时重复使用,为空则初始化,从而减小了不少没必要要的View的建立、减小findViewById的次数,
- 采用ViewHolder模式缓存item条目的引用
- 避免在getview方法中作耗时操做
- 避免使用半透明或者在活动中取消半透明
- 图片异步加载,待滑动中止后再加载
- 局部刷新
RecyclerView 与 ListView 相似,都是经过缓存view提升性能,可是RecyclerView有更高的可定制性。下面是使用时的一些设置,经过这些设置来达到对view样式的自定义:其中一、2是必须设置的,三、4可选web
- 想要控制其item们的排列方式,请使用布局管理器LayoutManager
- 若是要建立一个适配器,请使用RecyclerView.Adapter (Adapter经过范型的方式,帮助咱们生成ViewHolder)
- 想要控制Item间的间隔,请使用RecyclerView.ItemDecoration
- 想要控制Item增删的动画,请使用RecyclerView.ItemAnimator
扩展:RecyclerView能够很方便的进行局部刷新(notifyItemChanged())
RecyclerView的Measure和Layout是委托LayoutManager进行的面试
- 调用notifyItemChange方法
- 若是想刷新某个item的局部,能够有两种方法
- RecyclerView采用四级缓存,ListView采用两级缓存
- 四级缓存:
- mAttachedScrap:用于屏幕内的itemView快速复用,不须要create和bind
- mCacheViews:用于屏幕外的itemView快速复用,默认为两个,经过postion查找,不须要create和bind
- mViewCacheExtentsion:须要用户定制,默认不实现
- mRecyclerPool:默认上限5个;不须要create,须要bind;多个RecyclerView能够共用一个pool
- 总结:缓存方面和ListView最大区别是mCacheViews能够缓存屏幕外的item,而且不须要从新bind
- 重写onLayoutChildren方法
- 调用detachAndScrapAttachedViews方法,缓存View
- 计算并设置每一个children的位置
- 调用fill方法
- 重写fill方法进行布局
- 重写canScrollVertically和scrollVerticallyBy方法,支持滑动
- View主要适用于主动更新的状况下,而SurfaceView主要适用于被动更新,例如频繁地刷新;
- View在主线程中对画面进行刷新,而SurfaceView一般会经过一个子线程来进行页面刷新
- 而SurfaceView在底层实现机制中就已经实现了双缓冲机制
布局全都继承自ViewGroup算法
- FrameLayout(框架布局) :没有对child view的摆布进行控制,这个布局中全部的控件都会默认出如今视图的左上角。
- LinearLayout(线性布局):横向或竖向排列内部View
- RelativeLayout(相对布局):以view的相对位置进行布局
- TableLayout(表格布局):将子元素的位置分配到行或列中,一个TableLayout由许多的TableRow组成
- GridLayout:和TableLayout相似
- ConstraintLayout(约束布局):和RelativeLayout相似,还能够经过GuideLine辅助布局,适合图形化操做推荐使用
- AbsoluteLayout(绝对布局):已经被废弃
相同层次下,由于相对布局会调用两次measure,因此线性高 当层次较多时,建议使用相对布局shell
- invalidate 会调用onDraw进行重绘,只能在主线程
- postInvalidate 能够在其余线程
- requestLayout会调用onLayout和onMeasure,不必定会调用onDraw
- Activity.runOnUiThread(Runnable)
- View.post(Runnable),View.postDelay(Runnable,long)
- Handler
- 无论是view的postDelayed方法,仍是Handler的post方法,经过包装后最终都会走Handler的sendMessageAtTime方法
- 随后会经过MessageQueue的enqueueMessage方法将message加入队列,加入时按时间排序,咱们能够理解成Message是一个有序队列,时间是其排序依据
- 当Looper从MessageQueue中调用next方法取出message时,若是尚未到时间,就会阻塞等待
- 2中当有新的message加完成后,会检查当前有没有3中设置的阻塞,需不须要唤起,若是须要唤起则唤起
- setText后会对text作一些处理,如设置AutoLink,Ellipsize等
- 在合适的位置调用TextChangeListener
- 调用requestLayout和invalidate方法
- View 的draw方法会根据不少标识位来决定是否须要调用onDraw,包括是否绑定在当前窗口等
- 构造方法
- onFinishInflate:该方法当View及其子View从XML文件中加载完成后会被调用。
- onVisibilityChanged
- onAttachedToWindow
- onMeasure
- onSizeChanged
- onLayout
- onDraw
- onWindowFocusChanged
- onWindowVisibilityChanged
- onDetachedFromWindow
- 维护一个缓存view的数组,数组长度是 adapter的getViewItemTypeCount
- 经过getItemViewType得到缓存view 的数组,取出缓存的view
save:用来保存Canvas的状态。save以后,能够调用Canvas的平移、放缩、旋转、错切、裁剪等操做。 restore:用来恢复Canvas以前保存的状态。防止save后对Canvas执行的操做对后续的绘制有影响。数据库
- 替换内核:不一样的rom厂商对webview的优化不一样,可能出现兼容性和速度问题,能够替换成X5内核等来解决兼容性问题,同时提升加载速度
- WebView初始化提早:WebView初始化是一个耗时的过程,咱们能够预先将WebView初始化好,例如使用全局webview
- 开启缓存,能够明显提高第二次加载的速度
- 优化dns解析速度:使用和api同样的域名,这样dns不用二次解析,能够提升速度
- 预加载:将须要的文件资源经过native方法提早加载好或者打包进apk,须要使用时直接使用
- 延迟加载:延迟加载部分资源,在界面要显示的数据加载完成后再加载,如图片资源,js等
- 对于webview内存泄漏:单独开一个进程,Activity销毁时手动回收资源
- WebView中拦截网址:设置setWebViewClient,重写shouldOverrideUrlLoading
- js与native交互:
- mWebView.getSettings().setJavaScriptEnabled(true);
- mWebView.addJavascriptInterface(new JSInterface(), "jsInterface");
- 其余js调用Native方案:经过prompt, alert等,在webClient中重写拦截相应的方法
非静态内部类会持有外部类的引用,在一些状况下极可能形成内存泄漏,因此通常声明为静态内部类,但并非说必定要生命成静态内部类。
- 实现Thread.UncaughtExceptionHandler()接口,在uncaughtException方法中完成对崩溃的上报和对App的重启。
- 实现自定义Application,并在Application中注册1中Handler实例。
最近最少使用算法:内部经过LinkedHashMap来实现
- 都是序列化的方式
- Serializable只需实现Serializable接口便可
- Parcelable须要实现Parcelable接口,并重写writeToParcel和describeContents方法,而且实现一个Creator
- Serializable虽然操做简单,可是须要大量IO操做,效率慢;Parcelable自已实现封送和解封(marshalled &unmarshalled)操做不须要用反射,数据也存放在Native内存中,效率要快不少,在Android中更推荐使用Parcelable
- 因为不一样版本Parcelable可能存在不一样,在网络和磁盘存储时,推荐使用Parcelable
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// 首先只解析图片资源的边界
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);
//计算缩放值
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 用计算出来的缩放值解析图片
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
public static int calculateInSampleSize(
BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
int inSampleSize = 1;
if (height > reqHeight || width > reqWidth) {
final int halfHeight = height / 2;
final int halfWidth = width / 2;
// Calculate the largest inSampleSize value that is a power of 2 and keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}
return inSampleSize;
}
复制代码
- 首次加载 Android App 时,确定要经过网络交互来获取图片,以后咱们能够将图片保存至本地SD卡和内存中
- 以后运行 App 时,优先访问内存中的图片缓存
- 若内存中没有,则加载本地SD卡中的图片
随着Google对Android系统的更新,以及国内厂商堆Room的定制,一些保活的手段已经失效或者再也不适用,这里列举一些保活手段,实际中经常是多个方式并用
- 联系Room厂商加入白名单(或者引导用户手动加入白名单)
- 利用系统漏洞root进行提权,或者直接把本应用变成系统应用
- Service设置成START_STICKY:kill 后会被重启(等待5秒左右),重传Intent,保持与重启前同样
- 提高service优先级:经过android:priority = "1000"这个属性设置最高优先级(可能已经失效)
- onDestroy方法里重启service(效果很差)
- 监听广播(除了监听系统广播外,还能够利用友盟等第三方sdk作到应用相互唤起)
- 启动两个进程service相互监听,互相唤起(大部分room上失效)
- 在屏幕上保留1像素
- 注册系统同步服务
- 建立Native进程,双进程互相监听保活(5.0 以上失效)
- 利用JobSheduler保活
- 在Application中定义一个static常量,赋值为-1
- 在欢迎界面改成0
- 在BaseActivity判断该常量的值
- 不恰当的使用static变量(或者向static集合中添加数据却忘了必要时移除数据)
- 忘记关闭各类链接,如IO流等
- 不恰当的内部类:由于内部类持有外部类的引用,当内部类存活时间较长时,致使外部类也不能正确的回收(常发生在使用Handler的时候)
- 不恰当的单例模式:例如错误的将某个Activity给单例持有,或者在不应使用单例的地方使用了单例
- 使用错误的Context:Application 和 Activity的context生命周期不同
- webview形成的内存泄漏
- 在发生地点能够捕获
- 可是OOM每每是因为内存泄漏形成的,泄漏的部分多数状况下不在try语句块里,因此catch后不久就会再次发生OOM
- 对待OOM的方案应该是找到内存泄漏的地方以及优化内存的占用
- ANR是Application Not Responding 的缩写,当应用程序无响应时,会弹出ANR窗口,让用户选择继续等待仍是关闭应用。
- 处理超过期间会形成ANR的地方:触摸操做等(5s);BroadCast(10s);Service(20s)
- 避免:不要在主线程中作耗时操做
- 出现运行时异常(如nullpointer/数组越界等),而咱们又没有try catch捕获,可能形成Force Close
- 避免:须要咱们在编程时谨慎处理逻辑,提升代码健壮性。如对网络传过来的未知数据先判空,再处理;此外还能够经过静态代码检查来帮助咱们提升代码质量
- 此外,咱们还能够在Application初始化时注册UncaultExceptionHandler,来捕捉这些异常重启咱们的程序
- DDMS是一个程序执行查看器,在里面能够看见线程和堆栈等信息
- TraceView是一个性能分析器
扩展: Android Studio 3 中用Profiler代替了DDMS,能够监视分析CPU,网络,内存的实时状况,更加方便
- 经过LinkedHashMap实现的
- LinkedHashMap的特性:LinkedHashMap是一个双向链表,当构造函数中accessOrder为true时:调用put()方法,会在集合头部添加元素时,会调用trimToSize()判断缓存是否已满,若是满了就用LinkedHashMap的迭代器删除队尾元素,即近期最少访问的元素。当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法得到对应集合元素,同时会更新该元素到队头
- 不一样模块间解偶,方便复用
- 单个模块支持独立编译,并行开发,提升开发和测试效率
- 组件化是已复用为目的的
- 多个团队可能公用一个组件
- SparseArray经过两个数组实现,key为int型,value为Object型
- 适用于数据量较小时
- 经过二分法插入和删除数据
- 启动分为冷启动(包含初始化Application)和热启动,冷启动又能够分为第一次启动(会有额外的初始化数据库等操做)和不是第一次启动
- 开发时本地能够用adb命令获取启动时常:adb shell am start -w packagename/activity
- 线上能够在各个关键地方埋点统计时常
- 启动优化的目的是提升用户感知的启动速度
- 能够采用TraceView等分析耗时,找出优化方向
- 优化的思路:
- 利用提早展现出来的Window,设置Theme,快速展现出来一个界面,给用户快速反馈的体验(启动后再改变回来)
- 异步初始化一些第三方SDK
- 延迟初始化
- 针对性的优化算法,减小耗时
- 性能调优包括不少方面:包括代码执行效率、网络、布局、内存、数据库、业务逻辑,打包速度等
- 须要针对具体问题具体分析,找到性能瓶颈:
- 善于借助工具,例如使用traceview跟踪,或者打点统计,profiler,leak canary等
- 不用域名,用ip直连
- 请求合并与拆分
- 请求数据的缩小:删除无用数据,开启Gzip压缩
- 精简的数据格式:json/webp/根据设备和网络环境的不一样采用不一样分辨率图片
- 数据缓存
- 预加载
- 链接的复用:使用http2.0 (效率提高30%)
扩展:JD Android客户端网络性能调优之HTTP/2协议升级
- 合理的使用include/merge/viewstub等
- 减小布局层次,减小没必要要的view和节点
- 使用布局缓存,避免重复inflate
其余:能够用hierarchy viewer等工具进行分析
- 下降执行时间:如优化算法和数据结构/利用多线程/缓存(包括对象缓存、线程池、IO 缓存、网络缓存等)/JNI/需求优化
- 同步改异步:例如使用surfaceview
- 错峰:提早或者延迟操做,好比启动中第三方sdk的加载
- 分析APK
- 使用Android Studio的分析器分析apk结构
- 使用lint分析无用代码和资源
- 或者使用第三方工具,如NimbleDroid/ClassShark
- 删除无用资源
- 对无用资源和代码删除
- 优化结构,对重复资源去重
- 对依赖去重,依赖多个功能相似的sdk时,只保留一个
- 去除不须要的依赖,如语言support包可能包含多种语言,配置只保留咱们须要的资源等
- 开启gradle的ProGuard/Code shrinking/minifyEnabled等,自动去除不须要的资源和代码】
- 压缩已用资源
- 选取适当的图片格式,如webp
- 对图片进行压缩
- 选取合适的依赖包,而不是直接依赖V7包等
- 使用微信的打包插件AndResGuard对图片进行压缩
- 使用facebook的ReDex对dex文件进行压缩
- 经过网络按需加载
- 提升电脑配置
- 优化Android studio配置
- 加大内存;
- 使用守护进程;
- 开启instant Run
- 使用离线gradle
- 使用新版的gradle
- 优化项目
- 使用第三方编译如[阿里的Freeline](https://www.freelinebuild.com/docs/zh_cn/###
- debug模式下能够不开启混淆等
- 模块化,多模块时使用aar包避免反复编译
- myHandler.postDelayed(mLoadingRunnable, DEALY_TIME);
getWindow().getDecorView().post(new Runnable() {
@Override
public void run() {
myHandler.post(mLoadingRunnable);
}
});
复制代码
// 拿到主线程的MessageQueue
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override public boolean queueIdle() {
// 在这里去处理你想延时加载的东西
delayLoad();
// 最后返回false,后续不用再监听了。
return false;
}
});
复制代码
- ArrayMap 是Android中的一种用时间换空间的容器,用来替代HashMap
- 不适合大量数据或者有大量删除
- 采用二分查找
- 原理: 0. 使用两个数组进行存储
- 数组1储存key的HashCode
- 数组2储存key和value;value位置为key位置左移1位再加1
- hash冲突的解决:寻找下一个空位,由于查询时会比较hashcode和key,因此不会有问题
- 为咱们封装了thread和handler,并在内部实现了线程池,是一个轻量级的线程方案
- 缺点:不一样版本实现方式可能不同,例如线程池有多是串行的(最新版本是并行的)
- 缺点:可定制化程度不高,例如咱们不能很方便地cancel线程
- 不一样版本的AsyncTask 不同,最新版本中是串行的
- 不会,由于串行执行时用的是一个ArrayDeque来存放Runnable
- 扩展:AsyncTask内部有并行的ThreadPoolExecutor,储存队列用的是LinkedBlockingQueue,大小是128;若是咱们并行执行,会抛出运行时异常
- CPU对视图进行必要的计算:measure等
- 经过OpenGL 将CPU 处理过的数据交给GPU
- GPU进行栅格化并存入缓存
- Android 系统每隔16.6ms发出一个垂直同步信号,通知渲染
- 生命周期不同
- Application 不能showDialog
- Application startActivity时必须new一个Task
- Application layoutInflate直接使用默认主题,可能与当前主题不同
- 注册Zygote的socket监听端口,应用接收启动应用程序的消息
- 调用preload()方法加载系统资源,包括预加载类,Framework资源等
- 调用startSystemServer()方法启动SystemServer进程
- 调用runSelectLoop()方法进入监听和接收消息循环
它们的存在主要是为了线程间通讯,通讯的方式为:
- 在线程中调用Looper.prepare方法,生成一个looper与当前线程绑定(在生成looper的过程当中,其构造方法在其内部建立了一个MessageQueue)
- 调用looper.loop方法,使当前线程循环读取MessageQueue中的message,并调用 msg.target.dispatchMessage方法,交由target处理(这里target是一个Handler实例)
- new 一个Handler,在建立Handler时,须要为他指定looper(若不指定则是当前线程的looper),Handler会取出looper中的MessageQueue也做为本身的MessageQueue。
- 调用Handler的sendMessage方法发送Message信息(这个方法将Message的target设置成当前handler,并把它加入到MessageQueue中)
- ANR是应用程序无响应,缘由是有事件在主线程运行时间过长形成新的事件没法处理,或者当前事件运行时间太长
- Looper.loop会循环处理到来的Message,当MessageQueue为空是,线程处于阻塞状态,释放cpu资源
都是进城间通讯的方式,AIDL使用Binder通讯,Messenger经过AIDL实现
Messenger原理:Handler内部持有一个IMessenger实例,IMssenger时一个aidl的接口,Messenger初始化时这个IMssenger实例也传给了Messenger
区别:1.Messenger是对AIDL的封装,使用简单;2.Messenger经过Handler处理传过来的Message,只能运行在一个线程中,咱们在AIDL中能够运行多个线程;3.Messenger客户端调取服务方法的结果只能异步回调给客户端,AIDL能够同步回调
瞎jj写点凑个数吧
- Binder是Android中的一种进程间通讯方式
- Binder通讯是经过驱动来实现的,原理是在内核空间中进行内存映射,觉得只有一次内存拷贝,因此速度较快
- Binder会验证权限,鉴定UID/PID来验证身份,保证了进程通讯的安全性
- 系统的Service会想ServiceManager注册,使用时向ServiceManager获取
- Service/Client同ServiceManager通讯的过程自己也是经过binder驱动实现的
- android中Service的bind通讯是经过ActivityManagerService实现的
- Binder中的代理模式:
- 客户端代理对象和服务端实现统一接口
- 客户端获取服务端的引用,若是位于同一进程,那么获取的是服务端自己,若是是不一样进程,获取到的是服务端的代理
- 经过代理请向服务端请求
- 服务端接收请求并处理,返回给客户端
- 全称:Android Interface Define Language(Android接口定义语言)
- 目的是为了进行进程间通讯
- 使用方式:定义aidl文件,编译器编译时会帮咱们生成对应的JAVA代码;经过调用生成的java代码,来进行进程间通讯
- 原理:经过Binder方式进行通讯
- Window 0. Window有三类:系统Window、应用Window、子Window
- Window是接口,具体实现类是PhoneWindow
- Window 是一个抽象概念,咱们并不能直接操做window
- Activity在建立的时候attach方法中会建立Window并使之与Activity关联
- Window中会建立Decorview,并经过ViewRootImpl与View交互
- WindowManager 0. 在Activity启动时,handleResumeActivity方法中启动activity的时候,会将主窗口加入到WindowManager中
- 咱们并不能直接操做window,而是经过WindowManager
- WindowManagerImpl是其实现类,他将view增删改的操做交给 WindowManagerGlobal处理
- WindowManagerGlobal 中会调用 ViewRootImpl的方法
- ViewRootImpl经过IWindowSession与WindowManagerService交互
- Window用于显示View和接收各类事件,Window有三种类型:应用Window(每一个Activity对应一个Window)、子Window(不能单独存在,附属于特定Window)、系统window(Toast和状态栏)
- Window分层级,应用Window在1-9九、子Window在1000-199九、系统Window在2000-2999.WindowManager提供了增删改View三个功能。
- Window是个抽象概念:每个Window对应着一个View和ViewRootImpl,Window经过ViewRootImpl来和View创建联系,View是Window存在的实体,只能经过WindowManager来访问Window。
- WindowManager的实现是WindowManagerImpl其再委托给WindowManagerGlobal来对Window进行操做,其中有四个List分别储存对应的View、ViewRootImpl、WindowManger.LayoutParams和正在被删除的View
- Window的实体是存在于远端的WindowMangerService中,因此增删改Window在本端是修改上面的几个List而后经过ViewRootImpl重绘View,经过WindowSession(每一个应用一个)在远端修改Window。
- Activity建立Window:Activity会在attach()中建立Window并设置其回调(onAttachedToWindow()、dispatchTouchEvent()),Activity的Window是由Policy类建立PhoneWindow实现的。而后经过Activity#setContentView()调用PhoneWindow的setContentView。
- ActivityThread作为APP的入口,会执行 handleLaunchActivity -> performLaunchActivity
- performLaunchActivity中经过ClassLoader建立Activity对象 -> 调用Activity.attach方法 -> callActivityOnCreate
- 在Activity.attach方法中会建立一个Window实例PhoneWindow,并将activity做为callback传递给window
- 在Activity的onCreate方法中,会调用setContentView,setContentView会调用PhoneWindow 的 setContentView
- PhoneWindow 的 setContentView会建立DecorView,并把咱们本身设置的ContentView和DecorView绑定
- performLaunchActivity至此走完,以后的performResumeActivity会调用handleResumeActivity方法
- 在handleResumeActivity中,会调用Activity的getWindowManager()获取一个WindowManager,接着调用WindowManager的addView方法
- addView实际执行的是WindowManagerGlobal的addView(),这里会建立一个ViewRootImpl,并调用ViewRootImpl的setView方法
- 在setView这个方法内部,会经过跨进程的方式向WMS(WindowManagerService)发起一个调用,从而将DecorView最终添加到Window上
- 解压文件到data/app目录下
- 资源管理器加载资源文件
- 解析解析AndroidManifest文件,并在/data/data/目录下建立对应的应用数据目录。
- 而后对dex文件进行优化,并保存在dalvik-cache目录下。
- 将AndroidManifest文件解析出的四大组件信息注册到PackageManagerService中。
- 安装完成后,发送广播。
- 调用当前类加载器的loadClass加载类
- loadClass中先调用findLoadedClass查看类是否已加载,已加载->over
- 尚未加载,调用父类加载器的loadClass,父类加载器加载完成 -> over
- 若是父类加载器没有加载,调用本类加载器的findClass,并在findClass中调用·defineClass方法加载
- Android中加载的是Dex文件
- ClassLoader 是个抽象类,其具体实现的子类有 BaseDexClassLoader 和SecureClassLoader,(SecureClassLoader 的子类是 URLClassLoader ,其只能用来加载 jar 文件,这在 Android 的 Dalvik/ART 上无法使用的)
- BaseDexClassLoader 的子类是 PathClassLoader 和 DexClassLoader 。
- PathClassLoader 在应用启动时建立,从 data/app/… 安装目录下加载 apk 文件。
- DexClassLoader 则没有此限制,能够从 SD 卡或网络加载包含 class.dex 的 .jar 和 .apk 文件,这也是插件化和热修复的基础
build过程即执行gradle task 打包生成apk的过程:
- 经过appt工具,将资源文件生成R.java文件;将aild文件转换成对应的java文件
- 编译java文件,生成.class文件
- 将.class文件转换成Android虚拟机支持的.dex文件
- 经过apkbuilder将dex文件和编译后的资源文件生成apk文件
- 对apk进行签名和对齐
调试模式容许咱们为优化代码而添加许多额外的功能,这些功能在Release时都应该去掉;Release包能够为了安全等作一些额外的优化,这些优化可能比较耗时,在Debug时是不须要的
- log日志只在debug时输入,release时应该关掉(为了安全)
- 签名/混淆/压缩等在debug编译时能够加入,减小打包时间
- 能够在debug包中加入一些额外的功能辅助咱们开发,如直接打印网络请求的控件,内存泄漏检测工具LeakCanary等
- 在打Release包时,除了混淆等操做,每每还须要加固操做,保证APP的安全
- Dalvik 是 Android 中使用的虚拟机,执行dex字节码
- Dalvik 与 JVM 相比
- JVM执行class字节码文件,Dalvik执行dex字节码文件,dex文件作了优化,说起更小
- Dalvik是基于寄存器的,VM基于栈
- Art是Dalvik虚拟机的升级版,Dalvik是解释型的,Art是翻译型的
- Dalvik虚拟机执行的是dex字节码,ART虚拟机执行的是本地机器码
- 相对于Dalvik,Art安装占用内存更大,安装时间更长,可是运行速度会有提高
- 代码混淆(原理是减少方法数)
- sdk裁减(原理是减少方法数)
- multi-dex(原理是打包成多个dex)
引入multi-dex后,在5.0如下手机上,第一次安装后启动速度可能变慢甚至anr,须要进行优化:如单独开一个线程;修改keep文件等
原理:先计算出hash值,再对hash值进行非对称加密
V1版本签名生成的APK中与签名有关的文件:
- MANIFEST.MF: jar 包的文件清单,在 apk 中咱们用来记录全部非目录文件的 数据指纹
- CERT.SF:根据MANIFEST.MF生成的文件
- CERT.RSA:这里会把以前生成的CERT.SF文件,用私钥计算出签名, 而后将签名以及包含公钥信息的数字证书一同写入CERT.RSA 中保存
V1版本存在安全漏洞,google推出了v2版
查看当前链接的设备:adb devices
结束adb链接: adb kill-server
安装apk: adb install test.apk
从手机获取文件和推送文件到手机:adb push <本地文件> <远程路径> ; adb pull <远程路径> <本地路径>
获取log信息:adb logcat > log.txt
启动Activity: adb shell am start -n 包名/包名+类名
显示系统Activity栈信息:adb shell dumpsys activity
发送广播:adb shell am broadcast -a "android.intent.action.AdupsFota.WriteCommandReceiver"
查看进程信息:adb shell ps <package_name|PID>
杀掉某个进程:adb shell kill pidNumber
查看内存占用:adb shell dumpsys meminfo <package_name|PID>
Jar包里面只有代码,aar里面不光有代码还包括代码还包括资源文件,好比 drawable 文件,xml 资源文件。对于一些不常变更的 Android Library,咱们能够直接引用 aar,加快编译速度
- cpu的架构有armeabi、armeabi-v7a、x86等
- 针对不一样的CPU,使用不一样的so包,可使性能最大化
- 若是a.so提供了armeabi、armeabi-v7a、x86格式,那么b.so也要提供这几个格式,不然可能崩溃
- 当没有对应cpu的so时,会选择其余so,但执行速度会变慢:当一个应用安装在设备上,只有该设备支持的CPU架构对应的.so文件会被安装。在x86设备上,libs/x86目录中若是存在.so文件的话,会被安装,若是不存在,则会选择armeabi-v7a中的.so文件,若是也不存在,则选择armeabi目录中的.so文件(由于x86设备也支持armeabi-v7a和armeabi)
- compileSdkVersion :编译所依赖的版本,它可让咱们在写代码时调用最新的api,告知咱们过期的api
- minSdkVersion:最小的可安装此App的版本,意味着咱们不用作低于此版本的兼容
- targetSdkVersion: 目标版本,可让咱们虽然运行在最新的手机上,可是行为和target版本一致,好比:若是targetSdkVersion小于Android 6.0,那么即便咱们的app运行在6.0系统上,也不须要运行时权限
- 使用@TargetApi 来标明api版本,这样编译器就不会报错了
- 在代码逻辑中判断版本,在低版本上调用替代api或本身实现的算法。
- 记录启动的activity
- 须要退出时调用存活activity的finish方法,并调用System.exit(0)方法
- java中能够用实现Runnable接口、实现Callable接口、继承Thread类三种方式
- Android中还能够用AsyncTask、HandlerThread、IntendService
- 长链接:App 与服务器创建一个生命周期很长的链接,服务器经过push向App推送消息
- 心跳包:App 每隔一段时间就会向服务器查询是否有新的消息
- 长链接可能由于各类缘由被打断,心跳包接收消息可能不及时,因此咱们能够采起长链接+心跳包的方式:经过Socket创建一个长链接,并经过心跳包检测这个长链接是否存活,长链接中断的话则从新创建
Androi中主要有DOM,SAX,PULL三种方式
DOM将文件都加载到内存中,比较消耗内存;SAX和PULL节省内存,PULL使用比SAX更简单
- 指定浏览器:intent.setClassName(“com.android.browser”,”com.android.browser.BrowserActivity”);
- 指定网址: Uri uriBrowsers = Uri.parse(“http://www.sina.com.cn”); intent.setData(uriBrowsers);
能够用JNI(java native interface java 本地接口)接口
- 下载NDK,配置环境变量,配置gradle文件
- JAVA中声明native 方法如private native String printJNI(String inputStr);
- 生成或写对应的头文件
- 编写对应文件实现代码
- 编译成so文件
- 使用
- 扩展:native调用java代码
- 获取你须要访问的Java对象的类
jclass cls = (*env)->GetObjectClass(env, obj); //使用GetObjectClass方法获取obj对应的jclass。
class cls = (*env)->FindClass(“android/util/log”) //直接搜索类名,须要是static修饰的类。
复制代码
- 获取MethodID:
methodID mid = (*env)->GetMethodID(env, cls, "callback", "(I)V"); //GetStaticMethodID(…),获取静态方法的ID使用GetMethdoID方法获取你要使用的方法的MethdoID
复制代码
- 调用:
(*env)->CallVoidMethod(env, obj, mid, depth);// CallStaticIntMethod(….) , 调用静态方法
复制代码
NDK 是Native Development Kit 的缩写,是一些列工具的集合,帮助开发者迅速的开发C/C++的动态库
- JVM是JAVA虚拟机,保证了java语言的跨平台性,是编译后的 Java 程序(.class文件)和硬件系统之间的接口
- JVM = 类加载器 classloader + 执行引擎 execution engine + 运行时数据区域 runtime data area
视频加密根据场景的不一样有不少种方式
- 如仅对地址加密,能够起到防盗链的目的,能够与其余方法一块儿使用
- 对整个文件加密,加解密时间长,不实用
- 对文件的头中尾加密,播放器能够直接跳过,破解简单,不实用
- 对视频流加密(基于苹果HLS协议的加密 基于RTPE协议)
- 关键帧加密
- 建立一个bitmap,拿到canvas
- 在canvas上绘制圆,绘制五角星,绘制文字,返回bitmap
- 阴影:shadow属性
- 描边:两个TextView叠加;或者重写onDraw方法
选取 DeviceId,AndroidId,Serial Number,Mac,蓝牙地址等中的一个或者几个做为种子,生成UUID。
- 第一次登陆时保存两个token,一个长效一个短效
- 短效token用于每次网络请求的用户标识
- 长效token用于当短效token失效时自动登陆,从新获取token
使用BitmapRegionDecoder
- 在使用Https时,咱们须要对SSL证书作验证以确保有效
- 证书须要验证证书有效性,时效,域名等
- Android中WebView能够重写onReceivedSslError方法来处理ssl证书不对时的状况
- OkHttp设置证书验证:
- 验证能够是双向的,也能够是单向的
- 单向:将服务器对应的Server.cer文件打包进Apk中,经过cer文件生成SSLSocketFactory,并将其设置给okHttpClient
- 双向:用Server.cer和Client.key生成SSLSocketFactory,并将其设置给okHttpClient
- 内存大小 = 100*100*像素点大小
- 像素点大小和编码方式有关:ARGB_8888占8+8+8+8=32bit;ARGB_4444占4+4+4+4 = 16bit;
- 建立一个实现InvocationHandler接口的类,它必须实现invoke方法
- 调用Proxy的静态方法newProxyInstance,建立一个代理类
- 倒计时类:用CountDownTimer
- 延迟类: 1. CountDownTimer,可巧妙的将countDownInterval设成和millisInFuture同样,这样就只会调用一次onTick和一次onFinish 2. handler.sendMessageDelayed,可参考CountDownTimer的内部实现,简化一下,我的比较推荐这个 3. TimerTask,代码写起来比较乱 4. Thread.sleep,感受这种不太好
- 定时类: 1. 参照延迟类的,本身计算好要延迟多少时间 2. handler.sendMessageAtTime 3. AlarmManager,适用于定时比较长远的时间,例如闹铃
有比较才会有优点,咱们一般将Json与Xml进行比较,Json更加轻量。我以为在某些程度上讲,这是一个仁者见仁智者见智的问题。例若有些人认为Json相比Xml更易读,有些人责认为否则,这里大体列举几条,仅供参考
- 结构简单,可读性更强,读写更加容易
- 格式是压缩的,占用带宽小
- 支持多种语言
- 由于JSON格式可以直接为服务器端代码使用,大大简化了服务器端和客户端的代码开发量
MVC是model,view,controller的缩写
- 模型(model)对象:是应用程序的主体部分,全部的业务逻辑都应该写在该层;
- 视图(view)对象(对应Android中的布局xml文件):是应用程序中负责生成用户界面的部分。也是在整个mvc架构中用户惟一能够看到的一层,接收用户的输入,显示处理结果;
- 控制器(control)对象(对应Android中的Activity):是根据用户的输入,控制用户界面数据显示及更新model对象状态的部分,控制器更重要的一种导航功能,响应用户出发的相关事件,交给m层处理。 扩展:和MVP最大的区别是View和Model能够直接交互
- 在Java开发中,IoC意 味着将你设计好的类交给系统去控制,而不是在你的类内部控制
- 是框架的重要特征
- Android中Activity 的生命周期都是框架控制的,是一种控制反转
- 控制反转:将对象的建立交给框架去作
- 经常使用的框架:ButterNife/Android Annotation
- 2中两个框架都是经过java的注释框架实现的,而且都是做用在编译期
- 运行时权限是APP启动后由Android虚拟机(如Dalvik)控制的
- 文件系统权限是Linux内核受权
- 面向切面编程是经过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
- 例如不少功能须要先登录,登录在这里就是一个切面。
- 在goole官方的demo中,经过一个Contract把将View和Presenter管理起来,强化其一一对应的关系,便于操做
- [可是也有人认为不该该将Presenter定义为接口]http://www.infoq.com/cn/articles/do-not-give-the-prensenter-in-mvp-interface),由于这并不会方便测试,还会增长复杂性
- 普通动画(视图动画、补间动画)
- 属性动画
- 帧动画
- 物理动画(Android 8.0)
- Apache公司提供的库
- 拥有丰富的API,但也由于这个缘由,在不破坏兼容性的前提下,其庞大的API也令人难以改进
- Android 6.0中抛弃了Http Client,替换成OkHttp
- Sun公司提供的库
- 功能比较简单,可拓展性强
- 直接支持GZIP压缩,而且在Android 4.0 以上支持cache缓存,提升了网络效率
- Intent是Android中的信使,能够启动Activity,Service等
- Intent能够设置的几项值:Action, Category, Data/Type,Component
- 当设定Component时,是显式调用,其他是隐式调用
- IntentFilter 在AndroidMainifest中注册,用来帮助系统选出用户定义的隐式Intent对应的Activity /Service等
- Android是经过Intent的action、data、category这三个属性来进行匹配判断的
- action:隐式启动须要给Intent设置Action,若是没有设置这条匹配则自动经过;必须给IntentFilter设置一个action
- data:若是Intent没有提供type,系统将从data中获得数据类型。同action相似,只要Intent的data只要与Intent Filter中的任一个data声明彻底相同,data方面就彻底匹配成功。
- category:Intent的Category能够有多个,每个都须要和IntentFilter匹配才能算匹配上,不设置Intent的category时是默认的(DEFAULT)
- 总结:只有一个Intent的action、data、category匹配上IntenFilter中的一组数据时,才算匹配成功。
- 基本类型及其数组
- 实现了Serializable或者Parcelable的类型及其数组
- manifest:根节点,描述了包名,版本号等。
- application:包含package中application级别组件声明的根节点。
- activity:Activity是用来与用户交互的主要工具。
- receiver:IntentReceiver能使的application得到数据的改变或者发生的操做,即便它当前不在运行。
- service:Service是能在后台运行任意时间的组件。
- provider:ContentProvider是用来管理持久化数据并发布给其余应用程序使用的组件。
- uses-permission:请求你的package正常运做所需赋予的安全许可。
- permission: 声明了安全许可来限制哪些程序能你package中的组件和功能。
- uses-feature:使用到的硬件信息,如nfc
- upports-screens:支持的屏幕类型
- meta-data:data数据
- instrumentation:声明了用来测试此package或其余package指令组件的代码。
- dp = dip(device independent pixels),是设备独立像素
- sp:scaled pixels(放大像素),主要用于字体显示。
- px(pixel):像素
- dpi(dot per inch)
- px = dp*像素密度/160
public static int dp2px(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
复制代码
- layout-w400dp:当屏幕相对宽度大于400dp时,来这里取layout(与横竖屏有关)
- layout-sw400dp:当屏幕绝对宽度大于400dp时,来这里取layout(与横竖屏无关)
- 样式(Styles):能够理解成是针对View或者窗口(Window)设置外观或者格式的一个属性集合
- 主题(Themes):主题相比单个视图而言,是应用到整个 Activity 或者 application 的样式
- 区别:
- Theme做用域是Activity或者Application,Stytle针对View或者窗口(Window)
- 某些主题样式不能够在View中使用,例如"@android:style/Theme.NoTitleBar" 等 扩展: 属性(Attributes):你也能够将单个属性应用到 Android 样式上,一般会在自定义View 的时候,自定义属性。
- Goole 下个Android版本的预览已经放出,代号p
- 支持wifi室内定位
- 适配刘海屏
- 通知栏改进:能够显示对话,附加照片和表情等
- 多摄像头API
- 神经网络API 1.1
热修复的原理是让咱们的新类替换掉原来类的加载,从而达到修复的目的,如下是一种思路:
- java中经过PathClassLoader和DexClassLoader来加载类,类加载的方式是双亲委派模式
- PathClassLoader和DexClassLoader都继承自BaseDexClassLoader
- BaseDexClassLoader中维护了一个dex的数组
- 咱们能够经过DexClassLoader加载类,而后经过反射的机制将加载进来的数组添加到path数组的前面
- 加载的时候找到咱们须要的class后,就再也不继续向后找了,因此能够达到修复的目的
- Bundle : 只支持四大组件
- 文件共享:不适合并发
- Messenger:封装了AIDL
- AIDL:经过binder实现
- ContentProvider:共享数据
- Socket:适用于网络等
- Android 7.0中私有目录访问会被限制,致使不能自动安装
- 可使用FileProvider来解决
- 经过在AndroidManifest中给Activity设置process属性开启新的进程
- 能够开启N个进程,例如给webview单独开启一个进程,但要处理多进程间通讯和屡次初始化Handler问题
先unbind,再stop
- 在Activity退出时调用unbind方法,service会销毁
- 若是不调用unbind方法,service也会销毁,可是会抛出leaked serviceConnection 异常 (参考2)
- 使用HandlerThread,新建Handler时经过调用HandlerThread 的 getLooper方法拿到looper
- 原理:HandlerThread在run时会为咱们生成一个looper,getLooper方法会阻塞等待直到 looper!=null 才返回。
- Junit:不须要依赖android环境,适合于逻辑测试
- Instrumentation:依赖android环境,能够启动Activity,模拟内存回收,获取组件等,模拟点击等。须要在AndroidManifest中进行配置,适合于更复杂的测试
经过反射拿到对应的指示器,设置LayoutParams
- 外部:经过adb shell 命令导出内存,借助工具分析
- 内部:经过将对象加入WeakReference,配合RefernceQueue观察对象是否被回收,被回收的对象会被加入到RefernceQueue中
思路:递归实现,分别打印每一圈
使用build tools 下的dx工具
class 和dex文件对比:
1. 都是二进制文件
2. class文件存在容与,dex文件将整个工程的类信息整合到了一块儿,去掉了冗余
复制代码
- 随便写点凑个数吧=-=
- 硬件加速的四个级别:Application/Activity/Window/View
- binder效率更高:socket是一个通用接口,效率低;管道和队列内存拷贝两次,效率低;共享内存控制复杂
- binder更加安全:binder能够创建私有通道,经过uid/pid验证身份
https://www.nowcoder.com/discuss/3043
http://weixin.niurenqushi.com/article/2017-03-17/4790406.html
https://blog.csdn.net/vfush/article/details/51481127
https://blog.csdn.net/vfush/article/details/51790079