面试必背
会舍弃、总结概括——根据我这些年面试和看面试题搜集过来的知识点汇总而来
建议根据我的写的面试应对思路中的策略,面试之前全面过一遍题库,加深印象:https://www.jianshu.com/p/af089080904e
一、ANR的情况和避免
由Activity Manager和WindowManager监视,下列情况:
1、主线程5s内没有影响输入事件
2、BroadCastReceiver在10s内没有执行完毕
3、Service主线程耗时操作超过15s
解决方案案:网络通信、数据库和高耗时的计算放在子线程中,用handler.sendMessage、runOnUIThread和AsyncTask更新UI。
二、View绘制
2.1、绘制流程
1、onMeasure测量视图大小,MeasureSpec.makeMeasureSpec三种方式:AT_MOST最大不能超过,EXACTLY精确,UNSPECIFIED不限定size|mode
2、onLayout确定view的位置,实际应用:浏览器小红点的位置
3、onDraw绘制View。
2.2、draw的绘制步骤
1、画background
2、canvas.save
3、View自身
4、子View
5、canvas.restore
6、绘制装饰如滚动条
2.3、canvas.save和canvas.restore
a、canvas.save、restore确保对坐标系的改动只发生在save、restore之间的代码
也就是save可以调用canvas的旋转rotate、缩放,平移,剪裁和错切,restore恢复canvas上述的改动,避免对后续绘制进行的影响
例如:canvas.rotate(45)画一个矩形,这个不用save、restore罩住。那么如果以后再画的矩形也是45旋转的
b、save和restore配对使用,restore只能比save少,不能多。多了引发error
c、save将当前canvas状态入栈,restore把最后一个状态弹出栈
三、自定义View如何考虑机型适配
ldpi、mdpi、hdpi、xhdpi、xxhdpi和xxxhdpi的dpi
dp和px:px =dp*(dpi/160)
2、a、多使用wrap_content、match_parent和dp
b、不要用具体的像素值
c、不用AbsoluteLayout,使用RelativeLayout
d、不同分辨率不同大小的图片
e、manifest中声明支持
3、横屏竖屏layout
三、View、SurfaceView、GLSurfaceView的区别
3.1、View必须在UI主线程更新,速度慢
A、如果更新耗时,UI主线程阻塞,造成ANR
B、SurfaceView存在事件同步问题
3.2、SurfaceView是View的子类,采用双缓存机制,在新的工作线程刷新界面,速度比View快
A、SurfaceView是View子类
B、SurfaceView自身有GLSurfaceView和VideoSurfaceView
C、GL、视频播放、Camera摄像头都采用SurfaceView
3.3、GLSurfaceView是SurfaceView的子类,openGL专用
四、Android图片占用内存计算公式
1、常用的ARGB_8888存储时:
占用内存 = 图片长 * 图片宽 * 4字节
图片长 = 图片原始长 * (设备dpi/图片所在资源文件夹dpi)
图片宽 =图片原始长 * (设备dpi/图片所在资源文件夹dpi)
例如:红米dpi为320,图片200*230放在hdpi文件夹中,hdpi文件夹对应dpi为240,那么图片长 = 200
* (320/240)= 266.67。同理,xxhdpi文件夹对应480,代入公式图片长为133.33
2、RGB_565存储时,2字节;ALPHA_8为1字节,ARGB_4444为2字节
四、自定义View的事件分发机制
1、事件传递为Activity->PhoneWindow->DecorView->ViewGroup->View
2、不拦截就用requestDisallowInterceptTouchEvent
3、
五、View和ViewGroup事件分发的函数
5.1、View有a、dispatchTouchEvent->setOnTouchListener的onTouch->OnTouchEvent->onClick
c、setOnTouchListener优先级高于OnTouchEvent
5.2、ViewGroup有dispatchTouchEvent->onInterceptTouchEvent->onTouchEvent
5.3、所有事件都不接收就消耗在Activity的onTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
if(getWindow().superDispatchTouchEvent(ev)) {
return true;
}
returnonTouchEvent(ev);
}
5.4、onInterceptTouchEvent返回true,交给OnTouchEvent,OnTouchEvent又返回false,则该事件传递上一层view的onTouchEvent处理,都是false则传递到Activity的onTouc;如果返回true,那么整个Touch过程事件都交由整个onTouchEvent处理。
5.5、View的dispatchTouchEvent调用顺序
a、在dispatchTouchEvent中优先用setOnTouchLister的onTouch
b、再onTouchEvent
c、最后是onTouchEvent的ACTION_UP中performClick条件执行onClick。
5.6、ViewGroup的dispatchTouchEvent中伪代码
publicboolean dispatchTouchEvent(MotionEvent ev) {
boolean result =false;
if(!onInterceptTouchEvent(ev)) {
result =child.dispatchTouchEvent(ev);//如果没有拦截就交给子View
}
//拦截了或者子View都没被消费
if(!result) {
result = onTouchEvent(ev);
}
return result;
}
六、自定义View如何提供获取View属性的接口
a、attrs.xml定义declare-styelable
b、构造方法中用TypedArray获取值
c、xml使用,加apk/res-auto
七、art和Dalvik对比(绿色表示面试出现概率很大)
7.1、即时编译技术(Just
In Time JIT)与预编译技术(Ahead Of Time AOT)
1、JIT(Just In Time)即时编译技术
a、解释执行逐条编译成机器码,执行速度仍比直接执行机器码慢
b、JIT技术在运行时将热方法编译成机器码,存储到代码缓存中,以后不再解释执行
2、AOT(Ahead Of
Time)预编译技术
a、在安装时就直接编译成机器码,运行时直接运行机器码
7.1、对机器码的处理
1、dalvik下,每次运行时字节码通过JIT转换为机器码,效率低
2、ART下,第一次安装时通过AOT编译成机器码。启动和执行都会很快
3、ART下,机器码储存空间大,安装应用的时间变长
7.2、两者的联系
1、android 4.4用ART(Android
Runtime)替代Dalvik,开发者模式中“选择运行环境”
八、Dalvik虚拟机
8.1、Dalvik的特点
1、每个程序都有一个Dalvik
2、Dalvik是基于寄存器的,数据访问通过寄存器间直接传递;jvm基于栈,从栈上读写数据需要很多的指令指派和内存访问次数
3、Dalvik处理dex文件,执行的是Dalvik字节码。 Dalvik字节码由java字节码转换而来,并被打包到dex文件中,Dalvik解释dex文件执行Dalvik字节码
8.2、Android SDK中有个dx工具将Java字节码转换为Dalvik字节码
1、dx工具对java字节码重排序
2、将java类文件中的常量池分解,消除冗余信息,重新组成一个常量池,所有类文件共享一个常量池,相同字符串、常量在Dalvik中只出现一次,减少文件体积
八、Dalvik与jvm的区别
1、Dalvik基于寄存器,处理的是dex文件,Dalvik字节码,支持JIT
2、jvm基于栈,
3、java处理java字节码。
4、jvm本质上是软件,将Java字节码翻译为机器码
九、滑动冲突解决
1、onInterceptTouchEvent中ACTION_DOWN做标记,不拦截;在ACTION_MOVE中作条件判断,拦截。然后交给onTouchEvent事件
2、内部拦截,parent.requestDisallowInterceptTouchEvent
十、四大组件的生命周期
10.1、Activity的生命周期
1、正常启动:oncreate->onStart->onResume
2、back键退出:onPause->onStop->onDestory
2、透明Activity、对话框或者锁屏:onPause->onResume
3、非透明Activity、完全覆盖或者Home件回主屏:onPause->onSaveInstanceState->onStop->onRestart->onStart->onRestoreInstanceState->onResume
4、下拉通知栏,对生命周期时没有影响的
5、不走onStop的情景,在onCreate中调用finish
6、从A的Activity中点击button进入B Activity。再back键finish掉B Activity。A和B的完整生命周期,也就是A的onStop要在B的onResume之后同理finish掉B:
A.onCreate->A.onStart->A.oResume->A.onPause->B.onCreate->B.onStart->B.onResume->A.onStop->B.onPause->A.onCreate->A.onStart->A.onResume->B.onStop->B.onDestroy
10.2、Activity几个函数的调用
1、onSaveInstanceState、onRestoreInstanceState调用时机
a、透明Activity、对话框或者锁屏的时候因系统资源不足杀死
b、屏幕方向改变
c、非透明Activity、完全覆盖或者Home件回主屏
d、长期待在后台资源不足时
PS:被回收,Fragment重叠就是这样造成的
ps:Activity的onSaveInstanceState时,收集实现了保存/恢复方法View的状态;在onRestoreInstanceState方法中,Activity将收集的状态发回View结构树具有相同id的View(做搜索的时候,EditText出现的问题)。
10.3、Activity的四种启动模式和应用场景
1、四种启动模式,在AndroidManifest的android:launchMode属性
a、standard:默认,每次都创建一个Activity实例放在任务栈
b、singleTop:如果这个Activity实例在任务栈顶,onNewIntent重用该实例;否则创建一个新实例放在任务栈顶
c、singleTask:任务栈中有这个Activity实例,onNewIntent重用该实例;并且这个实例之上的任务全部出栈
d、singleInstance:一个独立的任务栈存在这个Activity实例
2、应用场景
a、singleTop:消息推送要启动的Activity;登录成功要跳转的Activity;耗时操作返回的Activity
b、singleTask:浏览器、微博的入口主Activity;
c、singleInstance:呼叫来电界面;闹钟提醒;不可用于中间跳转页
3、什么是任务栈
1、每一个程序都有一个任务栈,这个程序的所有Activity都放在这个任务栈中
2、栈顶activity才可以和用户交互
3、退出程序时,任务栈的所有activity出栈,任务栈被销毁。
4、另外一种形式的启动模式,Intent标志
a、FLAG_ACTIVITY_NEW_TASK对应singleTask
b、FLAG_ACTIVITY_SINGLE_TOP对应singleTop
c、FLAG_ACTIVITY_CLEAR_TOP对应清除栈中Activity和其上面的Activity,重新创建这个Activity
5、不同进程的Activity可以放在同一个任务栈,如android:process=":remote"
6、用taskAffinity可以在一个程序中指定不同的栈,也可不同进程同一个栈
10.4、Service的生命周期
1、startService->onCreate(Service已创建跳过)
->onStartCommand->stopService/stopSelf->onDestroy
2、bindService->onCreate->onBind->unbindService->onUnbind->onDestroy
PS:做过的项目中,NetTrafficService采用startService和bindService同时使用。在bindService时使用不同Action,而在onBind中对不同的Action返回不同用途的接口。bindService和unbindService配对使用。
10.5、bindService几个点
1、indService的onBind执行一次和多个Client即组件(如Activity)绑定
2、bindService发生的事情
a、Client执行bindService
b、Service不存在,Service执行onCreate
c、没有执行过onBind,Service执行onBind
d、执行过onBind,直接获取上次已经获取到的IBinder实例。
e、onBind一次执行创建的IBinder实例,Anroid会将该IBinder存起来,对所有client共享
f、client的ServiceConnection执行onServiceConnected
3、Client销毁,比如Activity执行onDestroy,该Client就会自动解除与Service的绑定
4、UnBindService发生的事情
a、Client执行unbindService
b、Client与Service解除绑定状态
d、Service检测是否还有其他Client与其绑定
c、如果没有其他Client绑定,Service执行onUnbind、onDestroy
十一、Fragment的生命周期
11.1、生命周期
1、onAttach->Activity的onAttachFragment->onCreate->onCreateView->onActivityCreate->onStart->onResume
2、onPause->onStop->onDestroyView->onDestory->onDetach
11.2、Fragment的状态保存和恢复
1、Fragment的自身状态保存
1.1、Activity中对View已经有保存和恢复,在Fragment中就应该不再有。Fragment只保存和保存自身的成员变量
11.3、Fragment的传递数据
1、使用setArgument,在onCreate中getArgument取出
a、好处:内存重启之前,这样做可以让系统保存数据。同Activity的intent恢复机制
11.4、Fragment中存在的七大问题
11.5、getActivity空指针问题
1、原因:
a、onDetach造成
b、onSaveInstanceState保存Fragment造成:Activity回收Fragment不会回收,而是在onSaveInstanceState中保存在Bundle中,在Activity的onCreate中取出来恢复。恢复的Fragment使用getActivity会是null
2、解决方案:在onAttach中用mActivity=getActivity保存Activity的引用
11.6、Fragment重叠问题
1、原因:onSaveInstanceState时,保存了Fragment,而回收了Activity。onCreate取出了Fragment,又add一个Fragment
2、出现情况:
A、当使用add、show和hide组合方式操作,重新add一个Fragment
B、使用replace不会出现这个问题,每次replace都会执行Fragment的生命周期。在这些生命周期里如果拉取了数据,就会造成多次重复拉取
C、add是将一个Fragment添加到container;replace先remove掉相同id的Fragment,再add,其中remove会执行那个Fragment的onDetach。而add在只有第一次add的时候会,在Activity退出的时候执行onPause->onStop->onDestroyView->onDestroy->onDetch
D、根本原因是在Fragment的hide之后,Activity的onSaveInstanceState中的FragmentState没有对应的Fragment的mHidden状态
2、解决方案
a、重写Activity的onSaveInstanceState,调用super.onSavedInstanceState之前使用FragmentTransaction.remove删除所有的Fragment,FragmentTransaction.commitAllowingStateLoss。FragmentTransaction通过getFragmentManager().getFragmentTransaction
b、重写Activity的onAttachFragment,让新的Fragment指向原来未原来未销毁的Fragment
c、重写Activity的OnSaveInstanceState保存hidden状态,在重建之前Fragment的onCreate的判断saveInstanceState是否为null,调用FragmentTransaction.hide并FragmentTransaction.commit
d、在Activity的onCreate中重建Fragment之前调用savedInstanceState.remove(TAG)
e、重写onSaveInstanceState,不调用super.onSaveInstanceState
十二、Fragment与Activity间的通信
12.1、Fragment和Activity相互获得
1、Fragment获得Activity:getActivity
2、Activity获得Fragment:getFragmentManager.findFragmentById([fragment在Activity中的布局id])
十二、Fragment与Fragment间通信
[if !supportLists]1、 [endif]通过Activity中转:getActivity().getFragmentManager().findFragmentById
[if !supportLists]2、 [endif]使用接口
[if !supportLists]3、 [endif]使用广播
十二、ContentProvider
12.1、概念
1、Android四大组件,提供增删查改功能,必须在AndroidManifest中注册
2、将自己的数据通过URI共享出去
3、以数据库表的方式将数据暴露出去,外界通过URI方法
4、系统中联系人信息、短信信息、图片库和音频库都是通过ContentProvider展现
12.3、URI
1、URI由Scheme、authority、path、query和fragment组成
2、UriMacth类对Uri进行验证
12.2、ContentProvider、ContentResolver、ContentObsever的关系
1、ContentProvider对外提供数据
2、ContentObserver监听数据的改变状态
3、ContentResolver获取ContentProvider提供的数据,ContentResolver.registerContentObserver注册ContentObserver、ContentResolver.notifyChange发出通知ContentProvider
十二、Android数据存储方式
12.1、五大存储方法
1、File
2、SharedPreference
3、ContentProvider
4、SQLiteDataBase
5、网络
十二、ThreadLocal
12.1、四大方法:set、get、remove和initialValue。
1、initialValue在第一次调用get或set时执行,只执行一次,初始化内部类Values中Oject数组。
2、JDK5.0开始支持泛型
3、内部ThreadLocal.ThreadLocalMap用来存储key键ThreadLocal和Value值变量副本
12.2、如何为每个线程维护一个变量副本:
a、ThreadLocal有个静态类ThreadLocalMap,键key为当前ThreadLocal对象、值value为对应线程的变量副本。一个线程可能含有多个ThreadLocal
b、ThreadLocal为不同的线程创建对应的ThreadLocalMap。在initialValue中createMap(t, value)
c、使用ThreadLocal作为Key的原因,ThreadLocal相比Thread要少,提供性能
12.3、与线程同步机制比较
1、同步机制仅提供有一个变量,不同线程排队访问;ThreadLocal为每个线程提供一个变量副本,可以同时访问。
2、ThreadLocal不是解决多线程变量问题,
3、ThreadLocal一般声明为static变量
12.4、存在的内存泄漏问题
1、使用线程池,线程结束后不会销毁而会被再次使用,就可能出现内存泄漏
2、如果ThreadLocal置为null,则存在ThreadLocalMap
Object>,内存泄漏。线程被gc,线程中的ThreadMap也被销毁,不会内存泄漏
12.5、适用场景
数据库链接、Session管理。多线程需要获得初始状态
十二、Android版ThreadLocal
1、ThreadLocal的内部类Values中Oject数组偶数位存ThreadLocal的弱引用,下一位存值
2、采用斐波拉契散列寻址
3、Thread自身有个ThreadLocal.Values成员变量,在ThreadLocal中初始化
6、典型应用
一个线程只允许一个Looper。为保证只有一个Looper采用ThreadLocal原理,在Looper的prepare函数中
if sThreadLocal.get() != null
throw一个线程只能有一个Looper
sThreadLocal.set(new Looper(quitAllowed));
十四、内存泄漏
1、静态变量(比如单例类)持有Activity的引用,例如Context或者接口。
解决方案:接口在OnDestroy时remove掉,Context传递ApplicationContext
2、a、Activity内部类Handler、Runnable和AsyncTask对Activity的隐式引用,Activity销毁之后,任务没完成。
解决方案:a、在onDestroy中用Handler.removeAllCallbackAndMessage
b、Handler定义为静态内部类,静态内部类不会持有外部类的引用。配合使用WeakReference解决
c、典型的:Volley在Activity中使用,Response回调Listener采用静态内部类和WeakReference使用Activity
3、资源未关闭:BroadcastReceiver、ContentObserver、File、Cursor、Stream和Bitmap;Bitmap.recylce()并置为null
4、静态内部类中引用外部类变量(如Context、View)时采用弱引用
5、不需要的对象,赋值为null
6、持久化Drawable,定义成static,4.0之前持有View,View持有Activity
十五、AsyncTask
15.1、使用准则:
1、必须在UI线程创建,execute必须在UI线程中执行
2、不能手动调用onPreExecute、doInBackground、onPostExecute、onProgressUpdate
3、只能执行一次;因为多个子线程同时运行造成线程不同步,状态不一致
15.2、存在的问题
1、AsyncTask所在的Activity终止,AsyncTask不会终止;
2、在没有不可中断操作比如BitmapFactory.decodeStream的,重写onCancel对socket、file进行关闭。并在Activity的onDestroy可调用AsyncTask.cancel终止
3、AsyncTask如果是非静态内部类,依照非静态内部类会持有外部类的引用,由于声明周期问题,可能会造成泄漏;
4、1.6之前串行;1.6到2.3并行;2.3以后execute串行,executeOnExecutor执行并行自定义的执行器
5、AsyncTask内部用弱引用持有Activity
6、横竖屏切换,Activity重建,Activity引用失效,onPostExecute刷新界面会失效。解决方法:在Activity的onRetainNonConfigurationInstance中重新传一个对象给AsyncTask
15.3、Android 3.0之后
1、execute只有一个线程执行排队任务,对应线程池Executors.newSingleThreadPool。也就是AsyncTask预定义的SERIAL_EXECUTOR
2、executeOnExecutor可以自定义线程池执行任务。执行无数个就用java线程池的Executors.newCachedThreadPool。AsyncTask默认定义了THREAD_POOL_EXECUTOR,就是Executors.newFixedThreadPool(5),最多只能5个线程。
十四、性能优化
14.1、布局优化
1、使用ViewStub、merge和include
2、overdraw,GPU选项观察overdraw的情况
3、ondraw不要new对象,不能耗时,60fps, 16ms绘制帧,GPU加速
4、避免内存泄漏
5、ListView:a、使用Holder;b、分页加载;c、滑动停止后在加载图片、
6、Bitmap加载:用LRUBitmap,内存加硬盘缓存
7、必要的时候使用SurfaceView
14.2、性能优化
1、上述布局优化
2、使用ArrayMap和SparseArray替代HashMap
3、少用枚举,多用static访问块
十四、ListView异步加载图片错位原理与解决方法
[if !supportLists]14.1、 [endif]异步加载图片错位原因
[if !supportLists]1、 [endif]同步加载不会出现图片错位
[if !supportLists]2、 [endif]异步加载图片出现错位
ListView一整屏显示7个item,当向下滑动时,显示出item8.由于RecycleBin机制,item8重用item1。如果异步网络请求item8的图片比item1慢,item8会显示item1的图片,当item8图片下载完成,此时向上滑动显示item1,item1会显示item8的图片。
[if !supportLists]14.2、 [endif]解决方案:对ImageView设置tag为图片URL,并设置默认图。等异步请求加载完比较Image的tag与请求的URL是否匹配
十四、ListView和RecyclerView
14.1、使用的对比
1、ListView使用时:
a、要继承重写BaseAdapter
b、自定义类ViewHolder,配合convertView一起完成复用优化
2、RecyclerView使用时
a、要继承RecyclerView.Adapter
b、继承RecyclerView.ViewHolder
c、设置布局管理器
LinearLayoutManager linearLayoutManager= new LinearLayout(activity);
mRecyclerView.setLayoutManager(linearLayoutManager);
14.2、局部刷新
1、ListView调用Adapter.notifyDataSetChanged会重绘每个item
2、RecyclerView.Adapter提供notifyItemChanged(int
postion)刷新单个item
14.3、HeaderView和FooterView对position的影响
1、ListView会将HeaderView和FooterView计算到position中,对setOnItemClickListener会有影响
2、RecyclerView只有addOnItemTouchListener
十五、OOM情景和解决方案
1、原因:a、加载对象过大;b、相应资源过多,来不及释放;c、Adapter没有使用缓存的convertView。e、Android虚拟机Dalvik,最大堆大小为16M,有的机器为24M。
2、解决方案:关闭资源;引用方面处理;加载图片预压缩;堆内存自定义大小和优化Dalvik虚拟机对内存分配
PS:Dalvik虚拟机堆内存分配优化setTargetHeapUtilization;自定义堆内存大小setMinimumHeapSize
十五、性能分析新方法
利用Looper的setMessageLogging方法,Looper.getMainLooper
十六、AndroidManifest中对Android:process属性设置的两种形式:
1、以冒号开头,android:process
= ":remote",该进程是私有进程,其他应用的组件不可以和它跑在同一个进程中。
2、以.或者以包名.remote开头,全局进程,其他应用可以设置相同的ShareUID和它泡在同一个进程
十七、多进程面临4个问题
a、Application多次重建;b、静态成员失效;c、文件共享问题;d、断点调试问题。
十八、java序列化
18.1、序列化:将对象转换为字节流序列,目的:a、永久保存到硬盘上;b、网络上传送
18.2、序列化的要求:实现Serializable或Externalizable接口的对象;
18.3、用到的api:ObjectOutputStream、ObjecInputStream;writeObject、readObject;
18.4、序列化的使用原则:对象的序列化
a、序列化不会保存静态变量,因为序列化保存的是对象的状态,静态变量属于类的状态
b、父类没有实现序列化接口Serializable,子类实现了。序列化时基类对象不会序列化,基类的属性不会序列化,反序列化时通过无参构造函数构建基类对象
c、父类实现序列化,子类没有。基类改变,序列化ID不会改变
e、外部类不序列化,非静态内部类、匿名内部类、本地类不能序列化,因为这些内部类中有个变量,这个变量指向外部类对象this,序列化要求对象中所有的对象属性也要序列化。
f、外部类序列化,内部类必须序列化
g、静态内部类不管外部类有没有实现序列化,都可以序列化
h、List、Map容器中的泛型类型必须实现序列化接口Serializable,否则会报NotSerializableException错误
18.2、关键字Transient、接口Externalizable
a、Transient声明变量,该变量不会序列化到文件中,反序列化是变量置为0或者null
b、Externalizable可以用writeExternal和readExternal指定序列化哪些属性
18.2、嵌套类
1、匿名内部类
a、没有名字,只能使用一次
Person p = new Person() {
//实现Person接口或者抽象类Person的方法
}
b、必须继承一个接口或者父类
2、本地类
a、定义在方法内部
3、非静态内部类
4、静态内部类
5、非静态内部间接实现了闭包
1、闭包是指一个可调用的对象,记录了创建它的作用域这个信息。所有外部类元素对于非静态内部类是可见的,有权操作
18.3、序列化代理类
1、Serializable接口存在的问题
a、readObject的方法是一个参数为字节流的构造函数,如果篡改字节流,readObject可能得到一个状态错误的对象
b、重写readObject、writeObject都不能解决,使用序列化代理模式可以解决
c、readObject对序列化类的引用类型做保护性拷贝能避免篡改字节流的问题,但是这样序列化类的引用型成员变量就不能声明为final类型的
d、使用序列化代理,静态内部类序列化,其中的readResolve构造一个全新的外部类对象,对外部类引用类型进行保护性拷贝。交给构造函数
publicvoid ExtenalClass implements Serializable {
private static final long serialVersionUID =1L;
private void readObject(ObjectInputStramois) throw InvalidObjectException {
throw new InvalidObjectException("Proxyrequired");
}
//JVM处理
private Object writereplace() {
retrun new SerializabtionProxy(this);
}
private static class SerializabtionProxyimplements Serializable {
private static final long serialVersionID= 1L;
//jvm会调用这个方法
private Object readResolve() {
//做保护性拷贝
return new ExtenalClass();
}
}
}
2、序列化代理类的基本思路
十八、Parcelable和Serializable的区别
1、Parcelable比Serialzable效率高,内存开销小;Parcelable通过IBinder通信的消息载体,只存在内存中;
2、实现方式上:Parcelable需要实现writeToParcel、describeContents函数和静态变量CREATEOR;Serializable提供一个序列化版本id(serialVersionUID)
3、Parcelable不能保存在磁盘上,因为在外界变化时不能保证数据的持续性
十八、java泛型
18.0、泛型类的静态方法和静态变量不能使用泛型类所声明的泛型类型参数T,但是可以使用方法前声明的泛型类型参数T
1、对于泛型类 public
class Test2不能public static T one或者public static T show(T one) {}
2、但是静态泛型方法自身声明的T可以使用
public class Test2可以定义publicstatic T show(T one) {}
18.1、类型擦除
1、泛型定义的时候,所有的T或?都是作为Object处理
2、使用时,List和List都作为List处理,在编译时先检查类型,再进行类型擦除
3、泛型参数不能替换基本类型,因为都当做Object处理,而Object类型不能存储double、int、boolean值,只能是Double、Integer和Boolean这些对象类
18.2、T和?的区别
1、T可以创建类的对象,?不可以
2、T是确定类型,?是未知类型
可以
class CollectionGenFoo {
private T mT;
public CollectionGenFoo(T xT) {
mT = xT;
}
}
CollectionGenFoolistFoo = new CollectionGenFoo(new ArrayList);
这里不能用CollectionGenFoo
extends Collection>会报错
18.3、擦除带来的无法具体类型信息和解决
1、Instanceof、new都是错误,只能用Class.isInstance()和class.newInstance()
class Erasure {
private Class mClass;
public Erasure(Class kind) {
mClass = kind;
}
public void f() {
if(mClass.isInstance(arg)) {}
T t =mClass.newInstance();
}
}
3、用Array.newInstance和反射构造泛型数组
public static T[] minMax(T[] a) {
T[] tArray =(T[])Array.newInstance(a.getClass().getComponentType(), 2);
}
18.4、泛型类型擦除在异常Exception中使用问题
1、类型擦除使得不能定义
public class Problem extendsException {}
try{} catch(Problem e1) {}catch(Problem e2){}类型擦除之后
try{}catch(Probleme1){}catcj(Problem e2);这个编译是错误的
2、不能catch(T e)
可以这样使用
publicstatic void doWork(T tObj) thows T {
try {
...
} catch(Throwable t) {
tObj.initCause(realCause);
throw tObj;
}
}
18.5、instanceof的用法
1、只能是if(arrayList instanceof ArrayList)
18.6、通配符的使用
1、无限定通配符
public static voidprintCollection(Collection collection) {
for(Object obj : collection) {
System.out.println(obj)
}
}
printCollection(new ArrayList());
用Object替换?就会出错
2、
extends Number>,上边界限定通配符
3、
extends Number>,下边界限定通配符
18.7、只有java语言由类型擦除
1、jdk1.5才出现泛型,为了兼容之前的代码
十八、重载与覆盖
18.1、区别
1、覆盖是子类和父类,重载是同一个类
2、覆盖要求参数列表相同,重载要求参数列表不同
3、覆盖是根据对象类型,子类还是父类的实例对象确定使用哪个方法;重载根据实参表和形参表来决定使用哪个方法
4、覆盖override是运行时的多态,重载overload是编译时的多态
18.2、覆盖
1、覆盖方法抛出的异常必须与父类抛出的异常一致,可以是父类抛出异常的子类
2、不能是private
18.3、重载
1、只可以通过参数类型、参数个数、参数顺序三不同
2、不能通过访问权限、返回类型、抛出异常不同进行重载,但是不同的重载函数返回值可以不同
3、抛出异常的类型和数目不同不会对重载进行影响
十八、隐藏
1、概念:子类的函数屏蔽了与其同名的父类函数
2、
十八、Binder
18.1、Binder的机制和底层实现
1、四个概念:Server、Client、ServiceManager和Binder驱动。基于Client-Server通信模式
2、Server与Binder驱动通信,Binder驱动做了两件事:a、Binder驱动可以访问内核空间中Server数据包;b、在内核空间创建Binder节点,隶属于Server进程;c、为Binder节点分配大于0的句柄,并将句柄和名字发送给ServiceManager
3、Client与Binder驱动通信:a、Client将Server名字和句柄为0的值作为数据包,发给Binder驱动;b、Binder驱动发现句柄为0,将数据包发给ServiceManager;c、ServiceManager根据Server名字找到该Server的句柄发送回来;d、Binder驱动将句柄发回Client;e、Client将服务和句柄,再发送给Binder驱动,Binder驱动找到对应的Binder节点。
4、Binder驱动:运行在内核空间的代码,通过/dev/binder文件在内核空间和用户空间来回搬数据。
5、BINDER_SET_CONTEXT_MGR命令:注册为ServiceManager
6、Client和Server是存在于用户空间;Client与Server通信的实现,是由Binder驱动在内核空间实现;SM作为守护进程,处理客户端请求,管理所有服务项
18.2、句柄的理解
a、句柄是区分不容服务的标识,只有跨进程的通信才会用句柄,即远程访问;b、同一个进程空间用引用,即同进程本地访问
18.3、IPC通信方式
1、linux中消息队列、共享内存、信号量、socket支持IPC
2、socket传输效率低,开销大,用于网络和进程间低俗通信。两次拷贝
3、消息队列、信号量采用存储-转发方式,两次拷贝
4、共享内存控制复杂,0次拷贝
5、消息队列、信号量、共享内存、socket公开管道名称,systemV,socket的ip地址、文件名。无法建立私有通道
4、Android基于Binder通信,ContentProvider、Intent、aidl都是基于Binder
18.4、Binder内存管理
1、Binder最大只能传1M的数据,因为Binder驱动只预留了一段1M大小的虚拟地址。
2、mmap中定义BINDER_VM_SIZE为1M,Binder数据需要跨进程传递,需要在内核上开辟空间,所以允许在Binder上传递的数据不是无限大的
十八、进程间通信
18.1、进程间通信五大方式
1、访问其他应用的Activity;2、ContentProvider;3、BroadcastReceiver;4、AIDL;5、Messager;前3个为四大组件的3个
2、Messenger和AIDL:
a、在Service端,Messenger处理Client端的请求是单线程的,而AIDL是多线程的。Messenger利用Handler形式实现IPC通信,数据放在Message中。一次只能处理一次请求,效率较低。
b、Messenger封装AIDL,开发的时候不用写.aidl文件。Messenger底层采用AIDL,对AIDL进行封装;
c、Client的方法使用AIDL获取返回值是同步,使用Messenger则是异步的。
Messenger用send发送Message没有返回值。使用msg.replyTo其实就是一个Messenger对象传递返回参数,Service端处理完调用该对象的send发送Message将返回值传递到Client端
Client端:
msg.replyTo =mGetReplyMessenger;
Service端:
Messenger client =msg.replyTo;
client.send(replyMessage);
d、AIDL方式不是线程安全的,支持并发处理。Messenger是线程安全的,不支持并发处理;
e、Messenger不适用于大量并发请求;只是为了传递消息,不适用与跨进程调用服务器端的方法
18.2、AIDL
1、AIDL接口定义语言,采用RPC方式实现
2、步骤:
a、aidl文件,编译器或ADT自动生成java接口文件
b、Service类
c、实现aidl的stub的子类,在onBind中返回该子类的实例
d、在绑定类中调用bindService(newIntent, serviceConnection, Context.BIND_AUTO_CREATE)
十八、BroadcastReceiver和Broadcast
18.1、BroadcastReceiver
1、四大组件之一
2、通过Binder机制实现
18.2、分类
1、无序广播sendBroadcast
a、异步的,效率高
b、无法终止广播,不能将处理结果交给下一个接收者
c、动态注册的BroadcastReceiver执行顺序不固定,由Android进程调度决定
d、静态注册的BroadcaReceiver按照设置的优先级顺序执行
e、判断有序还是无序,isOrderedBroadcast
2、有序广播sendOrderedBroadCast
a、按优先级顺序传播
b、都有权终止广播abortBroadcast
c、都可处理,并将处理结果交给下一个接收者
d、BroadcastReceiver优先级的设置:Android的Manifest中的android:priority设置,范围2的32次方-1,数值越大优先级越高,优先级相同执行顺序不确定。
e、sendOrderedBroadcast可指定最终广播接收者,执行两次onReceive
18.3、BroadcastReceiver的注册
1、静态注册:AndroidManifest中,由PackageManagerService负责,mReceivers队列中按优先级排列。进程不在,也能收到广播
2、动态注册:registerReceiver,由ActivityManagerService负责。
18.4、BroadcastReceiver生命周期
1、onReceive执行时,BroadcastReceiver是活跃的;onReceive执行完成时,变成不活跃
2、活跃的BroadcastReceiver受系统保护,不活跃后就会被系统随时杀死
3、10s超时,不要做耗时,也不要开启子线程做耗时操作,最好启动Service或者Handler发送到Service
18.4、自己的广播只让指定的app接收
1、步骤:在本应用中创建权限,在接收的应用中使用权限
a、指定权限
b、sendBroadcast(intent,
"广播名")
c、接收app的Manifest中声明
android:name="广播名">
18.5、supportv4中LocalBroadcastManager发广播
1、与BroadcastManager发广播相比
a、只在本应用内广播本应用内接收,比全局广播高效
十九、注册JNI的两种方式
19.1、静态注册
a、创建java类声明Native方法,编译成.class文件
b、用javah命令生成c的头文件
c、根据头文件创建源文件
ps:弊端:a、每个生成的class文件都得用javah生成头文件;b、JNI层函数名特长;c、初次调用native函数要与JNI层函数建立关联,影响效率
19.2、动态注册
a、使用JNINativeMethod的结构体数组保存对应关系
例:
#defineJNIREG_CLASS "com/test/JavaHello"
staticJNINativeMethod gMethods[] = {{"hello","()Ljava/lang/String", (void*)native_hello}};
二十、NDK
20.1、什么时NDK
a、集成了交叉编译器,用mk文件隔了平台差异,只需修改mk文件就可以创建出so
b、自动将so和java应用一起打包
20.1、Android的Native调用java
1、通过JNIEnv类获取jclass
jclass JNIEnv.FindClass:通过完整类名获得jclass类
jclass JNIEnv.GetObjectClass:根据jobject对象获得类
jclass JNIEnv.GetSuperClass:获取jclass的父类
2、通过JNIEnv类获取属性FieldID和方法jmethodID
非静态方法:GetFieldID/GetMethodID
静态方法:GetStaticFieldID/GetStaticMethodID
env->GetMethodID(env,jclass, "", "()V")
二十一、项目中用到的设计模式
21.1、单例模式
1、4种实现方式
a、饿汉模式:声明时就实例化
b、懒汉模式:每次getInstance要同步,同步开销
c、DCL(双重检查锁定模式):每次getInstance不需要同步
pulic static Singleton getInstance(){
if(sIntance== null) {
synchronized(Singleton.class){
if(sInstance == null) {
sInstance= new Singleton();
}
}
}
}
d、静态内部类单例模式:利用加载外部类不会初始化静态内部类,类初始化不会并行的特点,将sInstance声明并初始化在静态内部类中,getInstance直接返回静态内部类.sInstance
private static class SingletonHolder {
private static finalSingleton sInstance = new Singleton();
}
public static Singleton getInstance() {
returnSingletonHolder.sInstance;
}
2、适用场景
a、某类型对象只应该有且只有一个:播放器
b、创建新对象消耗资源过多,如要访问IO或者数据库
3、项目中用到的场景
a、缓存文件保存和读取:KFileCacheManager,保存为CMLauncher/.data和CMLauncher/.image,数据文件则用ObjectOutputStream、writeUnShared保存,图片用FileOutputStream、compress保存。
b、登录管理:登录接口ThirdLoginInterface;登录事件回调接口EventListener,Activity继承,Activity创建时添加到HashSet中,销毁时remove;单例类LoginInManager实现对外业务逻辑接口
c、数据库管理:已订阅的新闻或者听书专辑保存;继承SQLiteOpenHelper的StoryTellDbHelper,实现数据库创建,增删查改(beginTransaction,setTransactionSuccessful,endTransaction)。单丽丽StoryTellDbManager对DBHelper封装,提供业务逻辑上的增删查改接口。
3、DCL失效原因和解决方案
a、DCL两次null判断,第一次是避免不必要的同步;第二次在null下创建实例;
b、编译器指令重排序:获得锁的线程正在执行构造函数时,其他线程进行第一次null判断,返回了一个没有构造完全的对象。
c、解决方案:1、1.5之后用volatile,1.5之前将成员变量声明为final;2、用静态内部类
21.2、工厂模式和抽象工厂模式
21.3、策略模式
1、适用场景
a、
21.4、观察者模式
3、项目用到,一般配合单例模式用:
a、登录模块的HashSet集Listener
b、壁纸更换:KWallpaperManager单例类;观察者WallpaperChangedLisenter;观察者集合CopyOnWriteArrayList(写时复制,线程安全);
21.5、原型模式
3、项目用到
a、对HashSet的EventListener的操作:同步锁定mEventListeners;浅拷贝mEventListeners.clone获得拷贝listenerSet;调用listenerSet的接口
21.6、策略模式
二十二、Android动画
22.1、三大分类
1、三大类:View动画、帧动画和属性动画
2、各类动画概述:
a、View动画:平移、缩放、旋转和透明度四种效果,可用AnimationSet结核起来使用;
b、帧动画:不断切换图片实现
c、属性动画:改变View的属性值实现动画
3、各动画的特点
a、view动画可自定义:继承Animation类,重写initialize和applyTransformation
b、帧动画:注意每帧中图片的大小
c、属性动画:Android3.0开始支持;用translationX/Y,scaleX/Y和rotationX/Y实现
二十三、引用、GC和内存模型
23.1、四大引用
1、分类:强、软、弱、虚
2、什么时候GC
a、强引用:jvm宁愿抛出OOM也不会回收
b、软引用:内存不足时jvm回收
c、弱引用:jvm垃圾回收时就回收
d、虚引用:任何时候都可能会被垃圾回收器回收
3、用法和使用场景
a、软引用
4、GC Roots包括
a、虚拟机栈中引用的
b、方法区中类静态变量引用的
c、方法区中常量引用的
d、本地方法中中jni引用的
另一说:a、赋为null;b、重新new分配堆空间
5、如何判断一个对象为垃圾
a、引用计算算法:引用计数器为0的对象;难易解决对象之间循环引用
b、可达性分析算法:以GC
Roots为起始点,搜索走过的路径为引用链;没有一个引用链到达GC Roots的对象为垃圾对象
6、GC判断为垃圾的对象一定会回收吗?
a、不会,需要两次标记
b、第一次标记并筛选,筛选出对象是否要执行finalize
c、如果对象没有重写finalize方法,或者finalize已被虚拟机调用过,就是没有必要执行,直接回收
d、如果不是上述情况,则有必要执行finalize,则GC将对象放在F-Queue对象,由Finializer线程执行finalize
e、GC对F-Queue再次标记。这时对象的finalize中重新和引用链关联了,就不回收。
23.2、GC算法
1、引用计数算法
a、原理:有一个引用加1;删除该引用减1;计数为0的对象可以回收
b、缺点:无法处理循环引用问题;
2、复制算法
a、原理:把内存分2个区。垃圾回收时遍历一个区,将该区中正在使用的对象复制到另外一个区
b、优点:不会有碎片问题
c、缺点:1、要暂停整个应用;2、需要2倍的内存空间
3、标记-清扫算法
a、原理:标记阶段:从GC roots遍历其所有引用,标记活的对象;清扫阶段:对堆遍历,清扫未标记对象。
b、优点:解决了循环引用问题
c、缺点:1、要暂停整个应用;2、有碎片问题
d、sun早期版本
4、标记-压缩算法
a、原理:标记对象;压缩阶段:把标记的对象压缩后,按顺序放到堆的一块中
b、优点:1、避免停止复制的空间问题;2、避免碎片问题
5、分代算法
a、原理:对象生命周期不同对应不同的算法
b、对象生命周期:新生代、老年代、持久代
c、新生代使用效率高的复制算法,老年代使用标记-压缩算法
二十四、Handler机制
24.1、四个概念
1、Hanlder:负责Message的发送sendMessage、处理dispatchMessage,handleMessage
2、Message:消息对象;由链表实现,最大长度50。用于缓存避免消息对象的多次创建;配合obtainMessage方法使用
3、MessageQueue:Message队列
4、Looper:MessageQueue队列的处理
24.2、四个概念的关系
1、创建Handler的时候需要一个Looper
2、Looper创建MessageQueue,一个线程只有一个Looper,Looper.loop方法循环地从MessageQueue中取出Message
3、Looper从MessageQueue中取出Message后调用Hanlder的dispatchMessage分发Message
4、dispatchMessage按优先级交给Message.callback(callback.run),初始化回调mCallback(mCallback.handleMessage)和Handler的handleMessage处理
24.3、线程安全
1、Handler是线程安全的
24.4、Hanlder和Looper的关系
1、主线程中
a、在主线程中new
Hanlder(),默认就是Looper.getMainLooper
b、也可以new
Handler(Looper.getMainLooper()),初始化传个Looper
2、子线程中
a、HandlerThread:mHandlerThread= new HandleThread("Test");
mHandlerThread.start(); new Handler(mHandlerThread.getLooper());
在onDestroy中:mHandlerThread.quit;
b、Thread:Looper.prepare();mHandler = new Handler();hanlder.sendEmptyMessage(2);
Looper.loop()
在onDestroy中mHander.getLooper().quit();
24.5、Thread和HandlerThread
1、HandlerThread继承Thread,区别是再内部创建了Looper
24.6、Handler
Handler在Activity中声明时要用静态内部类,一定要用Activity的话就用弱引用WeakReference
24.7、利用Looper.loop进行内存分析
1、Looper自身有个Printer
2、Looper.loop中分发消息dispatchMesssage时,Printer的println打印>>>>Dispatching to
3、dispatchMesaage结束,Printer的println打印>>>>>Finished to
4、利用Looper.setMessageLoging(new
LogPrinter(){})重写println函数,startWith判断
5、创建一个子线程Handler,startMonitor中postDelayed打印调用栈的Runnable,在Finished to中removeCallbacks这个Runnable
6、打印调用栈
StackTraceElement[] stackTrace =Looper.getMainLooper().getThread().getStackTrace();
for(StackTraceElement trace : stackTrace) {
sb.append(trace.toString + "\n");
}
二十五、Android应用启动流程
25.1、第一阶段:到ActivityManagerService类
1、Launcher就是个Activity,处理点击事件,startActivity
1、startActivity或者Launcher中startActivity的startActivitySafely调用startActivityForResult
2、startActivityForResult调用Instrumentation类的execStartActivity,该方法为hide方法
3、Instrumentation.execStartActivity通过ActivityManagerNative.getDefualt获得IActivityManager的startActivity
4、ActivityManagerNative中内部类ActivityManagerProxy实现IActivityManager接口,实现了startActivity方法
5、ActivityManagerProxy.startActivty。其实IActivityManger、ActivityManagerProxy、ActivityManagerNative和ActivityManagerService就是AIDL机制,只不过对aidl生成类ActivityManagerNative加了一个内部远程代理类ActivityManagerProxy
IActivityManager是AIDL定义的接口,ActivityManagerNative对应stub继承Binder,实现IActivityManager;ActivityManagerProxy对应继承实现IActivityManager接口;ActivityManagerNative就是IActivityManager这个AIDL文件的生成类,ActivityManagerProxy为ActivityManagerNative的内部Binder类
ActivityManagerManagerService继承ActivityManagerNative,返回ActivityManager为IBinder
25.2、九个概念
1、ActivityManagerService:服务器对象,负责所有Activity的生命周期,称作AMS
2、ActivityThread:App真正的入口,主线程。与AMS一起管理Activity
a、app的入口为ActivityThread.main()
b、ActivityThread.main中创建Looper,Looper.prepareMainLooper,Looper.loop。
c、再次强调,其他线程使用Handler,要用Looper.prepare和Looper.loop对。一般用HandlerThread这个封装好的线程类
d、Application在ActivityThread.attach创建,通过Instrumentation.newApplication实例化一个Application
3、ApplicationThread:AMS通过ApplicationThread的代理与ActivityThread通讯进行Activity的管理
4、ApplicationThreadProxy:AMS通过Proxy与ActivityThread通讯
5、Instrumentation:ActivityThread通过Instrumentation创建或暂停某个Activity
a、每个应用程序只有一个Instrumentation,程序中的所有Activity都有一个Instrumentation对象的引用。
6、ActivityStack:记录已启动Activity的先后关系、状态信息的栈。AMS通过ActivityStack决定是否启动新进程
7、ActivityRecord:记录Activity的状态。AMS服务器端Activity的映像,每个Activity对应一个ActivityRecord。封装了Activity所在的TaskRecord
8、TaskRecord:记录ActivityRecord的栈,AMS用TaskRecord确定Activity的启动和退出顺序。Activity的4中launchMode就是基于TaskRecord
9、Zygote:
a、Android系统fork第一个zygote开启新进程,目的为了资源共用和快速启动
b、SystemServer是个进程也是由第一个zygote进程fork出来
c、SystemServer中开启AMS、PackageManagerService、WindowManagerService等重要服务
d、也就是zygote进程fork出SystemServer进程,AMS是属于SystemServer的Service组件
25.2、APP、AMS和zygote进程的通信
1、App与AMS通过Binder进行IPC通信
2、AMS所在的SystemServer与zygote通过socket进行IPC通信
3、startActivity打开app,需要AMS通知zygote进程,由zygote进程fork开启进程。Activity的开启、暂停和关闭都是AMS控制的
4、也就是app、SystemServer和zygote三个进程,利用app的ActivityThread、SystemServer的AMS通过AIDL方式Binder进行IPC通信,AMS与zygote通过socket进行通信
25.3、Instrumentation和ActivityThread
1、Activity操作进程中的Instrumentation
2、startActivity实际调用的Instrumentation.execStartActivity(),这个
Instrumentation.execStartActivity通过ActivityManagerNative.getDefault获得AIDL接口AMS的ActivityManagerProxy
3、ActivityManagerProxy.startActivity,调用Binder.transact,封装参数为Parcel对象,发送给AMS进行Binder通信
25.4、AMS与ActivityThread通讯
1、AMS通过ApplicationThreadProxy,调用Binder.transact,封装参数为Parcal对象,发送给ApplicationThread进行通信
25.5、AMS收到ActivityManagerProxy的指令如何startActivity
1、调用ActivityStack的startActivity(ActivityRecord
r,...),其中的参数ActivityRecord,从TaskRecord这个栈中取的
25.6、ActivityThread对Activity的生命周期的管理
1、Launcher中调用ActivityThread.handlePauseActivity()
2、1中在调用ActivityThread.performPauseActivity暂停Activity
3、用ActivityManagerProxy.activityPaused(token)通知AMS
25.7、用自己的话描述
1、Zygote进程fork出SystemServer进程。SystemServer开启AMS;
2、startActivity其实调用Instrumentation.execStartActivity,Instrumentation.execStartActivity通过ActivityManagerNative.getDefualt获得AMS的AIDL接口ActivityManagerProxy,ActivityManagerProxy.startActivity符合AIDL机制,通过Binder.transact将Parcel对象发送给AMS。也就是Laucher或其他app启动另外一个app的时候,通过Binder通信AIDL和AMS通信
3、AMS接到ActivityManagerProxy指令(启动Activity,Broadcast、Service和ContentProvider),ProcessRecord不存在,通过socket通信
a、具体为AMS中zygoteSendArgsAndGetPid()作为SocketClient执行write,Zygote中runSelectLoopMode()执行SocketServer执行read
4、从TaskRecord取出ActivityRecord作为参数,调用ActivityStack.startActivity
5、ActivityThread.main为Application的真正入口,在ActivityThread.attach中创建Application,
调用的是Instrumentation.newApplication;
二十六、Application
26.1、Application的生命周期等于这个程序的生命周期
26.2、生命周期五大函数
1、onCreate创建
2、onTerminate在模拟环境下可执行,在真机下永远不会执行。且终止对象时不保证一定被调用
3、onLowMemory内存较低的时候执行
4、onConfigurationChanged配置改变的时候被调用
5、onTrimMemory系统在内存清理时执行,4.0以后的API。触发频繁,每次计算进程优先级时,只要满足条件都会触发
二十七、Android系统架构
27.1、四层结构含五大模块
1、Application应用程序层
a、含有拨号、短信发送、图片浏览、浏览器等应用程序
2、Application
Framework层十大模块
a、包括七种Manager:Activity Manager、Window
Manager、Package Manager、Resource Manager、Notification Manager、Location Manager、Telephony Manager。Activity、窗口、安装包、资源包、通知栏、位置、电话的Manager
b、一个系统:View System即视图系统
c、一个组件:ContentProvider
d、一个服务:XMPP Service
3、Libraries系统库和Android
Runtime 系统运行时
4、Libraries:含有9大模块
a、一个Manager:Surface Manager,1、显示与存取操作间的互动;2、2D汇通与3D绘图的显示合成
b、4个引擎:webkit浏览器引擎、SGL 2D图形渲染引擎(skia着色系统和SurfaceFlinger
Surface控制系统)、OpenGL ES 3D图形引擎、SQLite数据库引擎
c、4个库:FreeType点阵字与向量字的库,Media
Framwork多媒体库,SSL安全套接层库,Libc嵌入式Linux设备定制c库
5、Android
Runtime两大核心
a、core Libraries核心库:java语言API库,一些Android核心API库
b、Dalvik虚拟机:每个程序都有一个Dalvik虚拟机
6、Linux kernel
Linux内核层
1、各种驱动Driver:Display、Camera、Bluetooth、USB、WIFI、Power、Aduio、Keypad、Flash Mem等驱动Driver,Binder驱动也在这个
二十八、Toast、Dialog都需要Activity的Context
二十九、JSON、XML和GSON
29.1、XML概念
1、XML:扩展标记语言,Extensible Markup Language。
2、使用DTD(Document
Type Definition)文档类型定义数据
3、跨平台,适合web传输
29.2、JSON概念
1、JSON:JavaScriptObject Notation
2、基于JavaScriptProgramming Language
3、轻量级跨平台
29.3、XML优缺点
1、XML传输占用带宽大
2、服务器端和客户端都要解析XML,耗时,且不同客户端解析方式也不一致
3、解析有DOM、SAX和Pull,DOM读入到内存,解析要考虑子节点、父节点,而JSON没有,JSON解析难度小
4、SAX和Pull解析的不同:两者都是逐步解析,但SAX不能控制处理事件,Pull可以控制。ANdroid中常用XmlPullParser
29.4、JSON优缺点
1、文件体积小,占用带宽小
2、易于解析
3、JSON和序列化想对应,GSON所做的。
4、XML提供大规模数据的逐步解析(SAX),JSON只有整体解析方案,适合较少数据
5、Android采用XML,使界面与代码分开,方便机型适配和App国际化
29.5、GSON
1、Google提供用于转化java对象和JSON对象
UML图、用到库的原理
进程和线程
12.1、区别
1、单位:进程是资源分配的最小单位,线程是程序执行的最小单位。
2、数据和空间:进程有独立的地址空间,系统建立数据表来维护代码段、堆栈段和数据段;线程共享进程中的数据,使用相同的地址空间
3、通信:进程间通信以IPC通信、消息队列,共享内存;线程共享全局变量、静态变量,Android中用Handler通信;线程拥有独立的堆栈
12.2、死锁产生的原因和四个必要条件
1、死锁产生的原因
a、系统资源不足
b、进程运行推进的顺序不合适
c、资源分配不当
2、四个必要条件
a、互斥条件:一个资源每次只能被一个进程使用
b、请求与保持条件:一个进程请求资源而阻塞时,对已获得的资源保持不放
c、不剥夺条件:进程已获得的资源,在未使用完之前不能强行剥夺
d、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系
3、处理死锁
a、检测死锁并恢复
b、动态分配资源
12.3、进程间通信
18.3、IPC通信方式
1、linux中消息队列、共享内存、信号量、socket支持IPC
2、socket传输效率低,开销大,用于网络和进程间低俗通信。两次拷贝
3、消息队列、信号量采用存储-转发方式,两次拷贝
4、共享内存控制复杂,0次拷贝
5、消息队列、信号量、共享内存、socket公开管道名称,systemV,socket的ip地址、文件名。无法建立私有通道
[if !supportLists]12.4、 [endif]线程间通信
a、线程间通信除了Handler,还有共享变量;文件;数据库;wait、notify和notifyAll,消费者和生产者
细碎的:
技术层面、业务逻辑
MVC和MVP
区别:
1、MVC的缺点:很难区分Activity或Fragment到底是View还是Controller的角色
2、MVP:View层和Model层完全解耦,View层发出事件传递到Presenter层,Presenter层去操作Model层将数据返回View层。
3、MVP:Model层和Presenter层解耦:用接口,Activity、Fragment实现接口,Presenter通过接口调用。
4、MVP:便于测试,编写测试用的View,模拟用户各种操作对Presenter进行测试
MVP缺点:
1、逻辑很复杂的页面,Model层和Presenter层的接口会很多
2、解决:利用基类和派生类减少接口
项目:插件化、壁纸问题的解决、单任务调度模式(解析短信、3D效果预览、浏览器弹窗)、性能优化(工具的使用和解决方案)、搜索页面动画、悬浮停靠
线程、内存模型;四大引用和gc;网络;反射;排序、算法
理论和应用实例,工程化的操作模式
反射
1、概念:在运行状态中,对于任意一个类,都能够知道这类的所有属性和方法;对于任意一个对象,都能调用他的任意一个方法。
2、反射时Class实例三种获取方式
a、利用对象调用getClass获取
b、Class类的静态方法forName获得
c、运用类名.class获得,基本数据类型用.TYPE获得,比如.TYPE
3、适用场景:Android系统中未开放的类
步骤:
a、用forName获得隐藏类Class实例:ClassstatusBarManagerClass =Class.forName("android.app.StatusBarManager");
b、通过Class实例getMethod获得隐藏类的方法实例:Methodmethod = statusBarManagerClass.getMethod("expandNotificationsPanel");
c、通过方法实例invoke执行该方法:method.invoke
4、getMethod和getDeclaredMethod
a、getMethod获得public方法,含父类和实现的接口的public方法;getDeclaredMethod所有的成员方法,但不包含父类和实现的接口的。
5、创建类实例
Constructor classObjConstructor =classInstance.getConstructor(null);
Object classObj =classObjConstructor.newInstance();
6、属性的获得
a、成员变量
Field field =classInstance.getField(fieldName);
Object proerty = field.get(classObj);
b、静态成员变量
Object proerty = field.get(classInstance);
c、成员变量设值
field.setAccessible(true);
field.set(classObj, "值")
7、方法的执行
a、成员方法
method.invoke(classObj, args)
b、静态成员方法
method.invoke(null, args)
TCP与UDP
1、三次握手:建立一个TCP连接请求需要三次握手
a、第一次握手:客户端发syn(x)包到服务器,进入SYN_SEND
b、第二次握手:服务器发送确认ACK(x+1)和SYN(y)包,进入SYN_RECV
c、第三次握手:客户端发确认ACK(y+1)包。服务器和客服电话进入ESTAVLISHED状态,完成三次握手
2、为什么三次握手,两次握手可以吗?
a、三次握手主要是防止已经失效的连接请求报文段有传送到服务器而产生错误。
b、连接请求又有网络节点延迟到达服务器,而在此期间已经完成了一次3次握手和数据传输了。这时,靠3次握手确认该连接请求是失效的,如果两次握手,服务器就会任务该连接请求有效,而一直等待客服端发数据,造成了服务器端的资源浪费。
3、四次挥手:断开一个TCP连接需要四次挥手
a、第一次挥手:主动关闭方发送一个FIN,关闭主动到被动的数据传送
b、第二次挥手:被动关闭方收到FIN发送ACK(FIN+1)确认
c、第三次挥手:被动关闭方发送关闭被动到主动数据传送的FIN
d、第四次挥手:主动关闭方收到FIN发送ACK(FIN+1)确认
4、TCP和UDP的区别
4.1、理论上区别:
a、TCP全双工可靠信道;UDP不可靠信道
b、TCP是面向连接,点对点;UDP是无连接,一对一、一对多、多对一和多对多的
c、TCP面向字节流;UDP面向报文
d、TCP提供可靠的服务,有各种数据控制机制,传输数据无差错、不丢失、不重复、按顺序到达;UDP是尽最大努力交付,没有数据控制机制
e、TCP首部20字节;UDP首部8字节,开销小
4.2、具体编程操作上也有区别。主要是TCP多了服务器段的监听listen、接收accept;客户端多了连接服务器connect。
HTTP协议
1.1、 组成HTTP请求的三部分
A、 请求行
B、 消息报头
C、 请求正文
1.2、 概念
A、 基于TCP连接
B、 应用层协议,无状态的,基于请求与响应模式的
1.3、 常用的六大方法
A、 GET、POST方法
B、 PUT、DELETE方法:PUT传输文件;DELETE删除文件
C、HEAD:报文首部
D、 OPTIONS:查询相应URI
1.4、 Get和POST区别
A、Get传输数据量小,URL只能128位,效率高。Post传输数据量大,上传文件只能用POSt
B、 Get不安全,URL可见。
C、 Get只支持ASCII码,传递中文可能乱码。POST可以
1.5、 HTTP请求报文三部分
A、请求行:HTTP版本、请求方法、URI
[if !supportLists]B、 [endif]请求首部字段
[if !supportLists]C、 [endif]请求内容实体
[if !supportLists]1.6、 [endif]HTTP响应报文三部分
[if !supportLists]A、 [endif]状态行:HTTP版本、状态码,状态原因短语
[if !supportLists]B、 [endif]响应的首部字段
[if !supportLists]C、 [endif]响应的内容实体
[if !supportLists]1.7、 [endif]HTTP响应的状态码:200,404
1、2XX:表示成功;200:请求成功;204:请求被手里但没有资源能返回;206:请求了部分资源;用Content-Range可以指定资源的范围
2、4XX:表示客户端错误;400请求报文语法错误;401请求要认证;403请求的资源禁止访问;404无法找到资源
3、5XX:服务器端错误;500服务器内部错误;503服务器忙
4、3XX:表示重定向;301永久重定向;302临时重定向;303临时重定向,只不过是通过GET方法重定向。307临时重定向,通过Post方法重定向
5、1XX:请求已接收或继续处理的只是
[if !supportLists]1.8、 [endif]HTTP常见的首部字段
1、通用的首部字段
a、Date:报文创建时间
b、Transfer-Encoding:报文传输编码方式
c、Cache-Control:缓存方面
d、Connection:管理连接
2、请求的首部字段
a、Host:请求的服务器
b、Accept:资源类型
c、Accept-Charset:字符集类型
d、Accept-Encoding:内容编码方式
e、Accept-Language:语言类型
3、响应的首部字段
a、Server:服务器自身信息
b、Location:重定向的URI
c、Accept-Ranges:字节范围
4、实体的首部字段(内容实体的首部字段)
a、Allow:支持的HTTP方法
b、Content-Encoding:编码方式
c、Content-Language:语言类型
d、Content-Range:位置范围
e、Content-Length:字节数
[if !supportLists]1.9、 [endif]HTTP1.1新特性
1、持久连接,可进行多次HTTP请求
2、管线化并发处理HTTP请求
3、采用断电续传
http和https的区别
[if !supportLists]1、 [endif]https比http多了一层安全套接字ssl,所使用的证书是自签名的;签名机构不在信任证书列表里,就会连接失败。
HTTPS原理
1、HTTP+SSL/TLS,HTTP上加了加密模块SSL(安全套接层)或TLS。http是超文本传输协议,明文传输
2、SSL/TLS使用RSA非对称加密、DES对称加密和HASH算法
3、只在第一次握手交换RSA加密密钥,之后使用DES对称加密。也就是验证身份采用RSA,传输数据采用DES。RSA大数运算较复杂,比较慢
4、HTTP采用端口80、HTTPS采用端口443
5、HTTPS是安全套接层(Secure
Sockets Layer, SSL),位于TCP/IP和HTTP两个协议层之间。在HTTP报文传输前对其加密,到达时加密
6、综上,HTTPS采用443端口,报文加密+验证身份+验证报文完整性
HTTP相比HTTPS存在的问题
1、明文不加密导致内容被窃听
2、不验证身份可能被伪装
3、不验证报文完整性可能被篡改
排序:
1、平均时间复杂度都是O(n
log n),为什么快速排序比堆排序要快
a、随着数据规模扩大,快速排序的时间复杂度线性增长;堆排序的时间是线性*对数级别,数据量增长10倍,比较的时间开销是10log10倍。因为存在大量近乎无效的比较
b、堆排序需要有效的随机存取,大规模数据中对数组指针寻址需要一定的时间;而快速排序只需将数组指针移动到相邻的区域。
ConcurrenthashMap
1、原理
a、HashTable的synchronized针对整张hash表上锁;ConcurrentHashMap用多个锁分段(Segment)对hash表上锁。
b、跨段操作时如size、containsValue,按顺序锁定所有的段,操作完毕按顺序是否所有段的锁。按顺序很重要,是为了避免死锁
volatile与synchronized区别
1、volatile适用于变量,从主存中读取内存,不会造成线程阻塞,保证每次都拿到最新值,但是不保证正确性;synchronized适用于变量和方法,内存操作,阻塞线程,
2、volatile轻量级,不执行互斥访问,每次看到的最新值,但不保证正确性。synchronized同一时刻只有一个线程执行。性能开销大
3、volatile变量只能成员变量
a、volatile特殊修饰符,只有成员变量才能使用。
2、volatile成员变量能保证下一个操作在前一个操作之后
wait和sleep的区别:
a、sleep线程暂停,sleep时间到了就会自动恢复运行
b、wait线程放弃对象的锁,进入等待此对象的等待线程池;notify后,才进入对象锁定池准备。
c、sleep是Thread类的静态方法Thread.sleep,wait是Object类的非静态方法
d、wait、notify和notifyAll必须在同步方法或者同步块中使用,因为同步锁住了,才能wait、notify释放锁。sleep不受限制。
e、sleep必须捕获异常,wait、notify和notifyAll不需要
f、sleep被其他对象调用它的interrupt产生InterruptException异常、wait也会
h、obj.wait会释放该线程所持有的所有锁,不仅仅是obj
j、obj.wait(long)或obj.wait(long,
int)设置long时间,如果在这个时间内没有被notify唤醒,则自动唤醒,带int是更精确
notify和notifyAll
a、notify通知等待该对象锁的其他线程。如果多个线程等待,则由线程规划器随机挑选出一个等待状态的线程
b、notify方法后当前线程不会马上释放该对象锁,要等到退出同步代码块
c、notify仅通知一个线程,将等待队列的随机一个等待状态的线程移出同步队列,notifyAll通知所有等待状态的线程,就是将所有等待状态的线程移出同步队列。
d、notifyAll虽然所有的等待状态线程都被通知,但是这些线程仍会竞争,且只有一个线程成功获取到锁。但是这个获取锁的线程运行完了,不用再notify或者notifyAll通知没有抢到锁的线程,因为他们已经移出等待线程的同步队列,获取锁的线程运行完,其他没有获取锁的线程会再次竞争。
使用wait、notifyAll的经典范式
1、等待方
a、获取对象锁
b、如果条件不满足,则wait,被通知后仍要检查用while不用if
c、条件满足则从wait返回,并继续
synchronized(对象) {
while(条件不满足) {
对象.wait();
}
}
2、通知方
a、获得对象锁
b、改变条件
c、通知所有等待wait对象的线程
synchronized(对象){
改变条件
对象.notifyAll();
}
3、用while不用if的原因:if在条件不满足,线程也可能错误的唤醒;while在线程睡眠先后都会检查条件是否满足,不满足就会继续wait
4、多线程间共享的对象必须使用wait
锁
[if !supportLists]1、 [endif]分类
[if !supportLists]a、 [endif]可重入锁:避免死锁;
[if !supportLists]b、 [endif]不可重入锁:
[if !supportLists]2、 [endif]
synchronized和ReentrantLock(一般lock)
[if !supportLists]1、 [endif]用法的区别
ReentrantLock:在锁定代码段的时候,用lock加锁和unlock解锁,finally中unlock防死锁
[if !supportLists]2、 [endif]方式
a、Synchronized由jvm执行,出现异常,jvm自动释放;ReentrantLock由java代码实现,要保证锁一定被释放,必须将unlock放在finally中
[if !supportLists]3、 [endif]可重入锁
[if !supportLists]a、 [endif]synchronized和ReentrantLock都是可重入锁
[if !supportLists]b、 [endif]同一线程外层函数获得锁之后,内层递归函数仍然可有获得这个锁,也就是两个synchronized嵌套,或者lock.lock,lock.unlock嵌套
[if !supportLists]c、 [endif]最大的作用避免死锁
[if !supportLists]4、 [endif]ReentrantLock提供锁投票、定时锁等待、中断锁等待
临界资源和临界区
1.1、临界资源
1、概念:每次仅允许一个进程访问的资源,进程间互斥访问
2、分类
a、属于临界资源的硬件:打印机、磁带机
b、属于临界资源的软件:消息缓冲队列、缓冲区、数组、变量
1.2、临界区
1、概念:每个进程中访问临界资源的代码。互斥访问决定每个进程在进入临界区之前,要检查临界资源是否占用
线程的状态
[if !supportLists]1、 [endif]五大状态
[if !supportLists]a、 [endif]新建状态:newThread(r)
[if !supportLists]b、 [endif]就绪状态:start():创建线程的资源并run
[if !supportLists]c、 [endif]运行状态:
[if !supportLists]d、 [endif]阻塞状态:sleep;I/O阻塞;试图得到的锁被其他线程持有;等待某个触发的条件;暂时让出了cpu,其他就绪状态的线程可获得CPU
[if !supportLists]e、 [endif]结束状态
[if !supportLists]f、 [endif]
多线程编程的好处
1、cpu不会因某个线程需要等待资源而空闲
2、多个线程共享堆内存,比多个进程更好
线程调度器和时间分片
1、线程调度器是操作系统的一个服务
线程池
1、概念:基于对象池的思想,开辟内存空间存放众多未结束的线程,池中的线程有池管理器来处理。有线程任务的时候,从池中取出一个,执行完后线程对象归池
2、优点:
a、避免线程创建和销毁的性能开销
b、避免抢占资源而导致阻塞
d、对线程进行管理并提供定时执行、间隔执行的功能
java中的线程池
1、顶级接口类Executor,继承Executor接口类ExecutorService,实现ExecutorService的类ThreadPoolExecutor
2、提供四种线程池
a、newSingleThreadExecutor:单个线程的线程池,池中每次只有一个线程工作,串行执行
b、newFixedThreadPool:固定数量线程的线程池,达到线程的最大数量后,后面的线程排队等待
c、newCacheThreadPool:可缓存线程的线程池,线程池中的数量超过处理任务所需的线程数量,就会回收空闲线程(60s无执行)。
d、newScheduleThreadPool:无数量限制的线程池,支持定时和周期性执行线程
2、四种线程池与BlockingQueue,命名为workQueue
a、newSingleThreadExecutor将corePoolSize和maximumPoolSize都置为1,使用LinkedBlockingQueue
b、newFixedThreadPool将corePoolSize和maximumPoolSize都置为n,使用LinkedBlockingQueue。
c、newCachedThreadPool将corePoolSize置为0,maximumPoolSize置为Integer.MAX_VALUE,使用SynchronousQueue
3、ArrayBlockingQueue、LinkedBlockingQueue和SynchronousQueue
a、ArrayBlockingQueue:数组结构的有界队列,FIFO。构造函数必须传一个int参数指明ArrayBlockingQueue的大小
b、LinkedBlockingQueue:链表结构的无界队列,FIFO,newFixedThreadPool。可传int参数指明大小,反之Integer.MAX_VALUE。LinkedBlockingQueuemList = new LinkedBlockingQueue(100);
c、SynchronousQueue:不存储元素的阻塞队列,每次插入必须等待另一个线程调用移除操作,newCachedThreadPool使用。线程安全,阻塞的,不允许使用null。put一个元素之后,一直wait知道其他Thread把这个element取走
d、PriorityBlockingQueue:类似LinkedBlockingQueue,但不是FIFO,而是自然排序或者Comparator参数决定
LruCache
3、线程池如何实现线程的复用
将Thread.start屏蔽起来,用自己的Runnable.run(),循环跑;循环跑的过程中用getTask检查新的任务Runnable,有则执行
4、线程池有四个概念
a、线程管理器(ThreadPool):创建管理线程
b、工作线程(PoolWork):线程管理器创建的线程,循环跑执行Task
c、任务(Task):就是Runnable
d、任务队列(TaskQueue):存放Task
1、LruCache原理
a、刚刚使用的移至头部,使用最少的移至队尾;
b、空间满了就移除队尾;空间大小可指定
2、使用例子
mLru = new LruCache(Runtime.getRuntime().maxMemery / 1024 * 8) {
@override
protected int sizeOf(String key, Bitmap bitmap) {
if(bitmap != null) {
returnbitmap.getByteCount();
}
return 0;
}
}
3、适用场景:内存缓存
DiskLruCache
1、
图片加载的3级缓存
1、3级的概念:内存->文件->网络
服务保活
1、Service设为前台,startForground;保活程度:会被force stop杀死
2、Service的onStartCommand返回START_STICK;保活程度:有次数和时间限制;会被force stop杀死
3、AndroidManifest设置android:persistent="true";需要系统签名;保活程度:会被force stop杀死
4、Service的onDestroy中再次启动该服务;保活程度:很弱;适用于“正在运行”和DDMS 中杀死的服务
5、监听一系列系统静态广播:
a、在AndroidManifest中静态注册这一系列广播的监听器。例如开机广播、文件挂载等等
b、保活强度:4.0以上,没有启动的应用或者force stop杀死的应用接受不到静态广播
6、监听第三方应用的静态广播:同5
7、定时器唤醒:用AlarmManager定时唤醒Service;保活程度:会被force
stop杀死,清除AlarmManager
8、利用账号同步,定时唤醒
a、原理:android中的账户系统,系统定期为唤醒账号更新服务
b保活强度:force stop杀死的也可拉活;Android
N不再支持
9、1像素锁屏窗、程序间
10、心跳唤醒
a、微信保活技术,长连接网络回包机制
b、保活强度:不敌force stop;需要网络,SDK 23以上doze会关闭所有网络
11、Native进程拉起
a、原理:Android当前进程fork开启Native子进程,定时发intent
b、保活程度:不敌force stop;5.0以上无效
用自己的语言回答范例
ArrayList和LinkedList的大致区别:
1.ArrayList
是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
2.
对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.
对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
4. ArrayList使用一个内置的数组来存储元素,已满,会扩展为数组length的1.5倍+1,然后用array.copy的方法,将原数组拷贝到新的数组中
5.默认0.75倍的时候就满,会扩展为数组length的1.5倍+1,然后用array.copy的方法,将原数组拷贝到新的数组中
6、非线程安全的
7、Vector比ArrayList多了synchronized同步,线程安全,Vector扩容是增加100%,ArrayList扩容增加原来的50%
a、ConcurrentHashMap是无条件线程安全的类
b、Vector、HashTable、StringBuffer是有条件线程安全的类,多线程在遍历时添加或删除元素是会产生ArrayIndexOutOfBoundException
8、LinkedList继承Queue
a、peek返回队头但不删除,poll返回队头但删除。remove删除会抛弃异常,element返回队头不删除,会抛出异常
b、add加入队尾,超出队列界限抛出unchecked异常;offer加入队尾,直接返回false
c、
Array,ArrayList,List
[if !supportLists]1.1、 [endif]Array
[if !supportLists]a、 [endif]内存中是连续的,速度快,操作简单
[if !supportLists]b、 [endif]定义时要定义其长度,不灵活
[if !supportLists]1.2、 [endif] ArrayList
[if !supportLists]A、 [endif]属于集合System.Collections,继承Ilist接口,不需要指定长度
[if !supportLists]1.3、 [endif]List
A、泛型,继承Ilist接口。避免拆箱和装箱
CopyOnwrite系列
1、写时复制;
2、适用于读操作多于写操作;
3、每次修改(即写入)的时候都要复制,内存占用;
4、不能保证数据实时一致性,保证数据最终一致性
线程访问List
[if !supportLists]1、 [endif]使用synchronized或者Collections.synchronized同步,增删操作时同步锁阻塞遍历
[if !supportLists]2、 [endif]使用ConcurrentHashMap或者CopyOnWriteArrayList
HashMap
1、“你用过HashMap吗?”“什么是HashMap?你为什么用到它?”
HashMap可以接受null键值和值,而Hashtable则不能;HashMap是非synchronized;HashMap很快;以及HashMap储存的是键值对。HashTable使用Enumeration,HahMap使用Iterator
2、“你知道HashMap的工作原理吗?”“你知道HashMap的get()方法的工作原理吗?”
HashMap是基于hashing的原理,我们使用put(key,
value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键对象和值时,我们先对键对象调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象。Entry是链表的节点,成员变量key、value、next
3、“当两个对象的hashcode相同会发生什么?”
因为hashcode相同,所以它们的bucket位置相同,‘碰撞’会发生。再比对键对象的equals不同的情况下,才会发生碰撞。因为HashMap使用链表存储对象,这个Entry(包含有键值对的Map.Entry对象)会存储在链表中。也就是java中采用的链地址法
4、当填满到bucket数组的0.75倍时,会创建两倍容量的新bucket数组,并将原来的对象复制到新的bucket数组中,而链表中元素次序是反的。调整的时候在多线程下回产生条件竞争:两个线程同时发现该调整大小了,就同时试着调整了。链表元素次序反了造成条件竞争。
5、HashCode和equals针对的是键对象
5、时间复杂度:平均是O(1),最好是O(1),最坏是O(n)
6、概念:键对象,值Entry
HashMap、SparseArray、ArrayMap和ArraySet
1、SparseArray
a、两个数组int[] mkeys存键key,hashing后的值;一个Object[]
mValues存值value
b、键key只能是int型
c、在put和get的时候使用二分法查找
d、key数组是排序的,通过key二分查找很快
e、value数组的二分法查找比HashMap遍历链表要快
f、适用场景:添加、查找和删除都要先进行一次二分法查找,数据量大的时候性能会下降50%;所以在数据量不大、千级以内;key为int型的时候使用
2、ArrayMap:从内存优化考虑设计的
a、两个数组,一个key的hashCode值数组;一个Value值数组;
b、添加、查找、删除都用二分法
c、应用场景和SparseArray一样
3、HashMap的缺点
a、满足加载因子(默认0.75)后,2倍容量创建新的数组
b、新数组还有hash运算
c、通过遍历Enty[]数组来查找,比较慢
LRUCache的原理LinkedHashMap
[if !supportLists]1、 [endif]继承自HashMap,链表使用双向链表
[if !supportLists]2、 [endif]重写put的addEntry,双向链表插入
[if !supportLists]3、 [endif]重写get,当accessOrder为true时,最新访问的元素添加到双向链表表头,原来的位置删除
[if !supportLists]4、 [endif]LRUCache就是基于LinkedHashMap实现的
[if !supportLists]5、 [endif]LRUCache必须删除某个cache值时,重写其entryRemoved。
[if !supportLists]6、 [endif]LRUCache初始化内存推荐为可用内存的八分之一
Set
1、特点:无放入顺序,位置由该元素的hashcode决定。元素不可重复
2、实现类:HashSet(底层由HashMap实现),LinkedHashSet(由LinkedHashMap实现,继承HashSet,HashSet的不公开构造函数包装)
STL的Map和HashMap
1、区别
a、STL的map底层用红黑树存储,查找时间复杂度为log n级别
b、STL的HashMap是用hash表存储的,查询时间复杂度为常数级别
c、常数级别不一定比log n要小。HashMap的hash函数、解决地址冲突都要耗时,内存消耗大。
d、元素达到一定数量级考虑HashMap。内存要求严格的时候不要使用HashMap。主要权衡查找速度、数据量和内存使用
e、TreeMap按键值key的升序排列,父节点大于等于左孩子,小于等于右孩子。顺序插入和要求内存小的时候使用。TreeMap的键为实体类,该实体类实现Comparable接口,重写compareTo方法。线程不同步
2、红黑树
2.1、特征
1、根节点为黑色,只为空叶子节点为红色
2、父节点为红色,子节点必须为黑色
3、从一个节点到该节点的子孙节点的所有路径上都黑节点的个数相同
String、StringBuffer、StringBuilder
1、在单线程环境下用StringBuilder,性能更好。避免加锁带来的性能损耗。StringBuffer每个方法前加Synchronized进行同步
2、String的内部实现为final
char[],所以每次对现有的字符串修改的话都会新实例化一个String
3、StringBuffer和StringBuilder是char[]数组实现
3、频繁的字符串修改和拼接要用StringBuffer
EventBus原理
1、原理:EventBus采用观察者模式,首先需要在onCreate中注册,然后Publisher
Post一个事件,最后Subcriber事件订阅者接收特定的事件信息。根据事件指定操作更新UI或者传递信息。
2、与BroadcastReceiver的区别:
java中使用string作为switch参数
1、java 7之前不支持,7之后支持
start与run区别:
1、start启动线程,并调用run方法;2、run方法可多次调用,单start只能调用一次
项目
悬浮停靠、壁纸问题、短信拦截优化
上滑停靠组件是个容器,类为SlipUpScrollLinearLayout。这个类就是对ScrollView的改动,而且继承LinearLayout,基于view的canScrollVertically原理。
一、canScrollVertically的原理
1.1、view的canScrollVertically基于三个函数计算得来
1、如图,对于一个view可分为3部分:
2、view中提供三个函数来计算上图中的offset、Range、Extent
a、computeVerticalScrollOffset计算Offset,Offset大于0则可以下拉,也就是canScrollVetically(DOWN)为true;Offset等于0的时候不可以下拉。也就是canScrollVetically(DOWN)为false
b、computeVerticalScrollRange计算Range、computeExtent计算Extent
b、当Range-offset-Extent大于0,则可以上滑,canScrollVertically(UP)为true;小于0则不可以下滑,canScrollVertically(UP)为false
二、这里主要基于canScrollVertically对ScrollView的scrollBy方法做改动
2.1、首先设计接口SlipUpScrollable,UP、DOWN两个常量用于判断是否可上滑和下拉。要停靠的下半部分视图实现这个接口。该接口含有方法:
是否可竖直滑动canScrollVertically(int
direction);控制滑动scrollBy(int dy);上滑到顶部scrollToTop();监听滑动事件onScrollStateChanged
2.2、scrollBy的两个参数x,y;只对y做事情;
1、y > 0就是上滑
computeVerticalScrollOffset计算当前可见区域居0坐标的距离;computeVerticalScrollRange为0到可滑动位置的距离;computeVerticalScrollExtent计算View可见区域大小。range-extent-offset=还可滑动的范围canScrollDelta;
若canScrollDelta大于0,判断是否大于等于y,大于等于y说明自身还能滑动,则调用自身super.scrollBy(x,y);
若canScrollDelta小于等于0,说明SlipUpScrollLinearLayout已经不可滑动;这时判断SlipUpScrollable.canScrollVertically(UP)是否可上滑,如果可以则直接调用SlipUpScrollLinearLayout.scrollBy(y);如果SlipUpScrollable不可以上滑,那么如果mScroller.isFinished没完成,调用mScroller.abortAnimation结束滑动。
2、y < 0就是下拉
首先mScrollable.canScrollVertically(SlipUpScrollable.Down)可以下拉,就mScollable.scrollBy(y);滑动Scrollable;如果不能看offset,如果offset + y
>= 0;还可下拉,那就super.scrollBy(x, y);否则super.scrollBy(x, -offset);同时停止Scroller计算mScroller.abortAnimation
2、原理性的东西
Scroller只是计算器,为滑动提供计算,scrollTo或者ScrollBy时获取Scroller中插值器的值,产生动画效果;computeScroll计算ViewGroup如何滑动
computeScroll中根据Scroller计算结果来scrollTo、ScrollBy滑动ViewGroup
3、onScrollStateChanged的处理
a、三类滚动状态:没有滑动SCROLL_STATE_IDLE;手指在滚动SCROLL_STATE_TOUCH_SCROLL;惯性滑动中SCROLL_STATE_FLING
b、在computeScroll中mScroller滑动完成,且不在BeingDragged状态
c、onTouch、fling、smoothScrollBy中添加
4、对上滑距离的控制
延迟加载
1、定义HashMap
LazyLoadModule>,LazyLoadManager
2、LazyLoadModule:构造函数TAG、Delay和初始化后的回调;
3、start:判断、并注册初始化后界面;LinkedList
4、
逆向
键盘监听退出
onKeyPreIme(KEYEvent)
KeyEvent.KEYCODE_BACK== event.getKeyCode()
退出
dispatchTouchEvent中对键盘隐藏事件
Java内存管理
1、对象的分配和释放问题
2、所有对象通过new子啊队中分配空间
3、对象的释放由GC绝对和执行
4、分类:方法区、程序计数器、栈和堆
a、方法区:存储常量、静态变量、即时编译器编译后的代码
b、程序计数器:当前线程执行的字节码的行号指示器
c、栈:每个线程都拥有一个栈,新方法调用,增加一个存储单元帧,保存该方法的参数(基本类型变量和对象引用)、局部变量和返回地址
又分本地方法栈、虚拟机方法栈
d、堆:垃圾回收主要回收堆的空间
java运行时栈帧结构
1、栈帧是jvm方法调用和方法执行用到的数据结构
2、只有处于栈顶的栈帧才有效,是当前栈帧;关联的方法是当前方法
3、栈帧由局部变量表、操作数栈、动态链接、返回地址组成
java内存模型
1.1、java内存模型定义8个操作
1、完成变量从主内存拷贝到工作内存,从工作内存再同步到主内存的过程
1.2、八大操作
1、lock、unlock、read、load、use、assign、store、write
2、lock、unlock、read、write作用为主内存的变量
3、load、assign、use、store作用于工作内存的变量
4、store将工作内存变量传送主内存中,write将传递到主内存的值赋予主内存变量中
5、assign将执行的值复制到工作内存中
1.3、java内存模型对八大操作的规定
1、read和load要配对,store和write要配对
2、不允许一个线程丢弃它的assign操作,变量在工作内存改变后必须同步到主内存中
3、不允许一个线程没有任何assign就把数据从工作内存同步到主内存
4、变量只能在主内存中产生,也就是use之前先执行load和store之前要先执行assign
5、lock和unlock成对使用
编译器和处理机重排序、内存屏障指令
1.1三种重排序
1、编译器优化的重排序:编译器不改变单线程语义,对语句执行顺序进行重排序
2、指令级并行的重排序:由于多条指令重叠执行的指令级并行技术,在不存在数据依赖的情况下,对机器指令执行顺序进行重排序
3、内存系统的重排序:
1.2、内存屏障指令
1、分为:LoadLoad、LoadStore、StroeLoad、StoreStore
happens-before规则
1.1、happens-before规则不是一个操作必须在后一个操作之后,仅仅要求前一个操作对后一个操作可见,且前一个操作按顺序排在第二个操作之前
1.1、四大规则
1、程序顺序规则:一个线程的每个操作happens-before后续操作
2、监视器锁规则:一个监视器锁的解锁happens-before随后对其的加锁
3、volatile变量规则:对volatile变量的写happens-before后续的读
4、传递性规则:A
happens-before B,B happens-before C,那么A happens-before C
java虚拟机的方法调用
1.1、概念
1、方法调用过程是指调用哪个方法的过程
2、符号引用解析成直接引用的过程:编译过程不包含连接步骤,class文件存储的只是符号引用,而不是实际运行时内存布局入口地址
1.2、分为解析调用和分派调用两大类
1.3、解析调用--在类解析阶段
1、解析阶段,将一部分符号引用解析成直接引用
2、解析阶段要求方法在运行之前是个可确定的版本,在运行期间不可改变
3、解析阶段适用于静态方法和私有方法
1.3、分派调用
1、overload重载的方法,为静态分派,属于多分派类型
2、override覆盖重写的方法,为动态分派,属于单分派类型
1.4、jvm的5条方法调用指令
invokestatic
invokespecial
incokevirtual:虚方法
invokeinterface:接口方法
invokedynamic:动态解析出
1.5、动态分配
a、虚方法表或接口方法表存放着各个方法实际入口地址,子类重写方法,将用子类方法表中的地址指向这个重写方法的入口;如果没有重写,子类方法表的地址指向父类方法的入口
b、方法表
抽象类和接口
1、接口只能定义公共静态常量,所有变量都是final型
2、抽象方法要被实现,不能是静态的也不能是私有的。
3、接口可以继承接口:典型的是java线程池中Executor总接口,ExecutorService接口继承这个接口
静态方法和静态变量能否被继承
a、能被继承,但是重写后被隐藏;也就是子类重写了父类的静态方法,用父类引用指向该子类,调用静态方法实际调用的是父类的静态方法,而不是子类重写的静态方法。
b、可以通过类名调用,也可通过实例名来调用,但是不能用this来调用;因为是没有this的方法
c、new一个对象时,先将静态方法、静态变量加载到方法区然后在堆内存中创建对象;静态方法和变量随类的加载而被加载。this关键字指向该对象;
d、非静态成员不存在的时候静态成员已经存在了,静态访问非静态肯定会出错
public、protected、defult与private
1、public:当前类、同package、子孙类、其他Package
2、protected:当前类、同package、子孙类;通过子类内或子类实例访问,如果子父类不在同一个包内,在子类中不能用父类实例访问protected,但可以同子类的实例访问。
3、default:当前类、同package
4、private:当前类
5、类声明时只能用public和default
final
[if !supportLists]1、 [endif]可以局部变量、成员变量、方法和类
[if !supportLists]2、 [endif]final方法:
a、子类不能重写父类的final方法,private方法都是隐式的final类型,但是更严格
b、final方法在编译的时候就静态绑定,内联调用,不需要运行时再动态绑定,效率较高
[if !supportLists]3、 [endif]final类
a、final类不能被继承
[if !supportLists]4、 [endif]final变量
a、对象变量,地址不能变,值可以变
b、final的局部变量,必须声明时赋值。final的成员变量可在构造函数中赋值
[if !supportLists]5、 [endif]final的优点
a、提高性能,jvm和java都会缓存final变量;JVM对方法、类和变量进行优化
b、多线程下安全地共享,不需要同步而带来性能开销
c、匿名类中所有变量都是final
[if !supportLists]6、 [endif]final实现不可变类
a、类是final声明,不可继承,所有的变量、方法都是final型
b、所有的变量方法都是private型
c、没有set方法
d、变量不是基本类型或者不可变类对象,也就是普通类对象,必须构造函数中new,在get的时候clone;保证不可变
Java中的析构函数
1、Java中只有通知垃圾回收器回收对象的方法
2、finalize通知jvm当前对象可以回收,典型用法:JNI调用native方法的时候,在finalize中调用native类的析构函数
3、System.gc显式通知垃圾回收器回收,但垃圾回收器不一定会立即执行
4、Runtime.getRunTime().gc():同System.gc
synchronized修饰静态和非静态方法
1、非静态方法
a、实际上synchronized(this),this就是对象本身
b、一个对象在两个线程中访问两个同步synchronized非静态方法,会互斥,因为this相同
c、不同对象在两个线程中方法两个同步synchronized非静态方法,不会互斥,因为this不同
2、静态方法
a、实际上synchronized(.class)
b、类名直接在两个线程中访问两个synchronized静态方法,会互斥
c、类的静态对象在两个线程中调用两个synchronized静态方法,会互斥
插件化的实现
1、寄主端:
a、利用DexClassLoader
b、进程锁:锁文件。
b.1、获得进程锁,500次每10s尝试一次
mFileLock= mFileChannel.tryLock();获得锁
//若不能获得
Thread.sleep(10,0);
//10ms为间隔,尝试500次
c、加载Dex
c.1、PackageManager.getPackageArchiveInfo(报名)获得pi
c.2、PackageManager.getResourcesForApplication(pi)获得resource
c.3、DexClassLoader获得classLoader
d、初始化插件(其实就是反射)
d.1、mClassLoader加载Entry类获得Class对象
d.2、然后getDeclaredMethod获得方法createObject获得Plugin对象,并将IModuleManager传给插件apk
d.3、利用getModule获得类名具体类实体,类继承于IPluginModule
2、库(3个接口)
IModuleManager:
IPlugin:IPluginModule getModule()
IPluginModule:空接口
预先定义一个具体的接口IEffect实现具体某项功能
IEffectModule实现插件的入口类,继承IPluginModule
3、插件端:
Entry:实现createObject
Plugin:继承IPlugin;实现getModule返回EffectModule
jar与aar的区别
1、jar只包含class与清单文件,不包含资源文件
2、aar包含class和资源文件
3、UI库只能使用arr
JVM虚拟机知识
类加载
1.1、7大阶段
加载、验证;准备、解析;初始化、使用;卸载
1.2、加载
a、通过类全名获得定义这个类的二进制字节流
b、将二进制字节流的静态存储结构转换为方法区的数据结构
c、从堆中new一个类名java.lang.Class的对象,作为访问方法区的接口
1.3、解析
a、类、接口的解析
b、成员变量、成员函数解析
c、接口方法解析
1.4、五种类必须初始化的情况,又称主动引用
1、实例化对象时,方法类的静态字段、静态方法时
2、反射调用时
3、初始化子类时,必须先初始化父类
4、jvm启动时,必须先初始化含有main方法的类
5、jdk1.7动态语言支持时,MethodHandle解析三种方法句柄对应的类必须初始化。这三种方法句柄为REF_getStatic、REF_putStatic、REF_invokeStatic
1.5、被动引用
1、子类引用父类静态字段,只会初始化父类
2、类的数组,A[] as = new
A[10];不会初始化A,而初始化LA
3、引用常量,不会初始化定义常量的类。而是直接存入调用类的常量池
1.6、接口的记载
1、子接口初始化,不会初始化父接口;但子类初始化,一定会初始化父类
类加载器ClassLoader
1.1、概念
1、ClassLoader负责将java字节代码.class文件转换为java.lang.Class类。也就是上面的“加载”阶段
1.2、类加载器ClassLoader的分类
1、系统类加载器ClassLoader
a、Bootstrap ClassLoader:引导类加载器,加载核心库,由C代码实现。唯一不继承java.lang.ClassLoader的类加载器
b、Extensions ClassLoader:扩展类加载器,加载扩展库,位于/lib/ext
c、System ClassLoader(Application
ClassLoader):系统类加载器,加载CLASSPATH的类库,通过ClassLoader.getSystemClassLoader()获得这个加载器
2、自定义类加载器ClassLoader
a、自定义java.lang.ClassLoader的子类,实现自定义ClassLoader
1.3、类加载采用双亲委托模型(Parent
Delegation模型)
1、JDK 1.2,Java类加载采用双亲委托模型
2、双亲委托模型
a、概念:一个类加载器收到类加载请求,先将这个请求委托给自己的父加载器,父加载器再委托它的父加载器,最终将这个请求传递到最上层类加载器Bootstrap ClassLoader,只有所有的父加载器反馈自己无法加载,该类加载器才会尝试自己加载
1.4、双亲委托模型加载类的过程
1、加载过程
a、自定义ClassLoader向父加载器Application
ClassLoader传递加载请求
b、Application
ClassLoader继续向父加载器Extension ClassLoader传递
c、Extension
ClassLoader继续向父加载器Bootstrap ClassLoader传递
d、Bootstrap
ClassLoader为最上层类加载器,尝试再自己路径下查找要加载的类,找到了就加载。否则找不到,向下层Extension ClassLoader反馈自己无法加载
e、Extension ClassLoader从自己的路径中查找要加载的类,找到了就加载。否则,向下反馈
f、Application
ClassLoader从自己的路径中查找要加载的类,找到了就加载,否则,向下反馈给自定义ClassLoader
j、自定义ClassLoader从自己的路径中查找要加载的类,找到则加载,找不到就抛出ClassNotFoundException
2、双亲委托模型加载类的时序图
3、双亲委托模型的优点
a、能够解决类加载过程中的安全性问题
b、安全性问题体现在:
开发者自己编写了java.lang.Object类,要用自定义的ClassLoader加载自己编写的java.lang.Object类,以此欺骗jvm。双亲委托模型不会让他成功,因为JVM会优先再Bootstrap ClassLoader路径下找到java.lang.Object类并加载。
4、jvm的类加载也可以不遵循双亲委托模型
a、双亲委托模型只是JDK系统类加载器Bootstrap
ClassLoader、Extension ClassLoader和Application Class Loader遵循的类加载机制
b、可以自定义ClassLoader,重写父类的loadClass方法打破双亲委托模型
1.5、Dalvik虚拟机类加载机制
1、Android无法通过自定义ClassLoader,通过defineClass方法加载Class
2、Android系统提供了两个派生类:DexClassLoader和PathClassLoader
3、DexClassLoader和PathClassLoader重载了ClassLoader的findClass方法。而不再用difineClass方法
4、DexClassLoader和PathClassLoader都是符合双亲委托模型的类加载器
5、DexClassLoader和PathClassLoader都是通过DexFile实现类加载
1.6、Dalvik的DexClassLoader和PathClassLoader的区别
1、DexClassLoader通过它的静态方法loadDex获得DexFile对象,PathClassLoader通过DexFile的构造函数new
DexFile获得DexFile
2、DexClassLoader支持apk、jar和dex文件,并且会在指定的outpath中释放dex文件
3、PathClassLoader只支持dex文件
4、DexClassLoader调用DexFile的loadClass;PathClassLoader使用DexFile的loadClassBinaryName
ActivityLifecycleCallbacks类
1、对Activity生命周期各个事件进行集中处理,除了onRestoreInstanceState
2、在Application的onCreate方法中
Application.registerActivityLifecycleCallbacks(newActivityLifecycleCallbacks(){})
3、Android 4.0以上,4.0以下要用代理系统框架中的Instrumentation类
4、适用场景
a、对Activity声明周期做日志LeakCanary
b、Http请求框架,在Activity销毁时cancel队列中请求;
c、EventBus、Xutils框架中在onCreate、onDestroy中注册和remove。避免显示调用
ActivityLifecycleCallbacks的应用:LeakCanary原理
1、在Application中LeakCanary.install(this),实际调用Application 。registerActivityLifecycleCallbacks
res/raw和assets区别
1、相同点:都原封不懂地打在apk包,不会编译成二进制
2、raw会被映射到R.java文件,直接用ID访问,R.id.filename;assets需要用AssetManager类来访问
3、assets下可以再新建文件夹
4、文件:raw是通过getResources().openRawResource(R.id.filename)
assets是通过getResource().getAssets().open(fileName)
5、AssertManager不能超过1MB,raw没有这个限制
Android资源查找流程
1、三个概念:Resources类、AssetManager类和resources.arsc
2、resources.arsc放在apk包中,解压可看到。有AAPT打包生成,是资源索引表,维护折资源ID、Name、Path或value的对应关系
3、Resources类通过ID找到资源,如果是字符串等值的话,直接取。如果是layout、drawable交给AssetManager打开文件
resources.arsc和apktool编译后的public.xml关系
1、resource.arsc的格式描述对应数据结构frameworks的ResourceTypes.h中
2、public.xml由resources.arsc解析出来
Activity的intent-filter元素
1、一个Activity可以拥有多个intent-filter,自身属性android:label设置外部调用时的名称
2、action子元素
a、任何一条intent-filter都至少包含一个action,否则任何Intent请求都不能和这个
b、如果intent请求中没有设定action类型,只要intent-filter有一个action类型,intent将顺利通过intent-filter
3、category子元素
a、intent请求中与其中intent-filter的category匹配,即通过
b、没有指定任何category的intent-filter只能匹配没有设置类别的intent
4、data子元素
a、指定希望接收的Intent数据的URI的android:schema、android:authority和android:path;数据类型android:type。
b、通过Intent的setData(URI.parse())设定的Schema和数据类型必须与Intent-filter的data子元素设置的一致。
c、path也可用pathPattern代替,pathPattern为模糊匹配
d、mineType
5、intent设置
a、Intent.setAction;intent.setCategory;Intent.setData(URI.Parse())
AndroidManifest中Activity、Service、Receiver的meta-data子元素
使用
ai.metaData.getInt("[android:name]")
Bitmap中内存复用和采样
1、Bitmap.Options中inSamleSize设置采样率,超过内存后加载
2、Bitmap.Options中inBitmap进行Bitmap复用,4.4以下只能复用相同大小,4.4以上复用该图片大小以下的图片
具体为:
options.inMutable= true;
BitmapnewBitmap = cach.getBitmapFromReusacleSet(options);
if(newBitmap!= null) {
options.inBitmap = newBitmap;
}
一.SQLite中常用的sql语句
1. SQLite数据库如何查询表table1的第20条到第30条记录?
select * from table1 limit 19, 11;
ps:SQLite与MySQL一样,select语句也支持limit字句。在使用limit字句时,要注意记录从0开始,20条到30条记录数为11。
扩充知识:SQLite的LIMIT子句用于限制由SELECT语句返回的数据数量(通俗地讲,用于查询数据库后,按照限制返回数据记录条数)
(1)带有LIMIT子句的SELECT语句的基本语法如下:
SELECT column1,column2,column3….columnN (*) FROM table_name LIMIT [no of rows]
即:select [数据列] from [表名] limit [限制返回记录数目]
举例:如有一个表COMPANY,其字段和已有记录如下:
A.若要求只从表中提取6条记录——
select * from COMPANY
limit 6;
查询结果为:
B.若要求从一个特定的便宜开始查询,例如:从第3位开始提取3个记录-----
select * from COMPANY
limit 3 offset 2;
查询结果为:
(2)即LIMIT子句与OFFSET子句一起使用时的语法为:
SELECT column1,column2…columnNFROM table_nameLIMIT [no
of raws] OFFSET [row
num]
当然,也可以省却OFFSET关键字,例如上面的例子可写为:
SELECT * FROM COMPANY
ORDER BY ID LIMIT 2,3;
2. Sqlite不存在某条记录就插入,存在就更新,只用一条sql语句实现?
replace into table1 (id,
name) VALUES (1,’bill’);
或者replace into table1(id,name) value(1, ’bill’);
为什么不用insert语句(本题直接用insert语句肯定不行,insert语句遇到约束冲突后就会抛出异常,在SQLite中提供了replace语句,可使用replace语句来替换insert,这样,当id主键重复时则相当于使用update语句来更新name字段值)
(1) 表不存在则创建
create table if not
exists table1_student ( _id Integerprimary key autoincrement, name Text, age
Integer);
(2)表中的数据不存在时插入数据
Insert into
table1_student(name, age) select ‘bill’ , 25where not exists (select * from table1_student where name=’bill’ and age=25 ); ---重复执行多次,仍只有一条数据
(3)当表中的数据不存在则插入,存在则更新(使用replace语句)
这时候创建表的sql语句为:create table if not exists table1_student(_id Integer primary
key,name Text, age Integer)
不存在插入,存在更新sql语句为replace into table1_student(_id, name, age) VALUES (1, ’bill’, 25);-----重复执行多次,仍只有1条数据
(4)将年龄改为35,发现并没有插入新数据-----修改一条记录中非主键字段,只更新对应数据
replace into
table1_student(_id, name, age) VALUES (1,’bill’, 35);
(5)将学生id(主键)改为2,则发现插入了一条新数据--------修改主键,相当于新插入一条记录
replace into
table1_student(_id, name, age) VALUES (2,’bill’,35);
资料来源:http://blog.csdn.net/findsafety/article/details/50519790
3.如何才能将table1的表结构和数据复制到表table2上?
复制表的方法很多,最简单的方法就是使用create
table 语句复制表的结构和数据
create table table2 as
select * from table1;
(1)复制表结构+数据
create table
table_name_new as select * fromtable_name_old;
(2)只复制表结构
create table table_name_new
as select * from table_name_oldwhere 1=2;
或者
create table
table_name_new like table_name_old;
(3)只复制表数据
若两个表结构一样:
Insert into
table_name_new select * from table_name_old;
若两个表结构不一样:
Inset into
table_name_new(column1, column2….) selectcolumn1,column2….from table_name_old;
SELECT INTO也有这样的功能:
如:SELECT * INTO
table_name-new FROM table_name_old;
SELECT *INTO
table_name_new FROM table_name_old WHERE 1=2;
4.请写出创建视图的SQL语句?(创建视图即查询table1表的所有记录)
create view if not exits
view1 as select * from table1;
网上查到的都是:create view
view_name as select * from table_name where conditions;
或CREATE VIEW
view_name AS SELECT column_name(s) FROM table_name WHEREcondition;
http://www.w3school.com.cn/sql/sql_view.asp
http://www.yiibai.com/sqlite/sqlite_views.html
二.SQLite中的函数
1. SQLite中的核心函数
(1)abs(X)函数:
例:请说出abs(X)函数的返回值有哪几种情况?
由于abs(X)函数会自动尝试将当前字段值转换为整型,再取绝对值,因此,只要当前字段值可以成功地转换为数值型的值,abs(X)函数就可以成功返回正确的结果。因此,该函数的返回值会根据字段X的值类型不同而不同;此外,还要考虑abs(X)函数抛异常的情况。
总结起来,abs(X)函数的返回值共有如下几种情况:
1)若X为正值和0,则abs(X)函数返回X本身;
2)若X为负值,则abs(X)函数返回X的绝对值,也就是-X;
3)若X为NULL,则abs(X)函数也返回NULL;
4)若X为字符串、Blob等不能转换为数值的类型,则abs(X)函数返回0;不过,若非数值类型可以转换为数值abs(X)函数仍会取该值的绝对值,如X=字符串”-34”,则abs(“-34”)=34
5)若X的值超出64位这个数值范围,abs(X)函数则会抛出一个溢出错误。
(2)changes()函数:获取最近一次影响的记录行数;
例:如何获取最近一次执行SQL语句所影响的记录条数,并写出相应的SQL语句?
insert into tables
values(1,”bill”);//最近一次执行的是这条insert语句
//返回insert语句影响的记录条数
select changes();
(3)coalesce()函数:返回第一个不为null的字段值
如何只用一条sql语句从图(1)所示表中查询出图(2)所示结果?
分析:对比两个图可知,本题的实质就是查询第一个不为null的字段值的问题。当使用selcet语句查询表table1的id和name字段时,要求显示第一个不为null的字段值。
select coalesce(name,id)
as value fromtable1;
此外,该题用ifnull函数也可以实现。
select ifnull(name,id)
as value fromtable1;
ps: coalesce()函数和ifnull()函数的区别:
两者功能相同,只是ifnull()函数只有2个参数,相当于有2个参数的coalesce函数
(4)length()函数:返回字符串的长度
例:请说出length(X)函数再以下几种情况下返回怎样的值?
1) X为字符串类型---------------------返回字符串中包含的字符数
2) X为Blob类型的值-----------------返回二进制数据中包含的字节数
3) X为NULL-----------------------------返回NULL
4) X为数值型---------------------------length会将X作为字符串处理,例如length(123)的值为3
(5)like函数和like子句----使用通配符对字符串进行匹配
例:请分别使用like子句和like函数查询table1中name字段值包含bill的记录?
like子句:select
*from table1 where name like ‘%bill%’;
like函数:select
*from table1 where like (‘%bill%’, name);
ps: like(X,Y)函数与”Y like
X”子句的功能完全相同,like函数与”Y like X”子句都可以使用通配符对字符串进行匹配(例如,“%”表示0个或任意多个字符串;“_”表示任意的单个字符),但两者在X和Y的位置正好相反,此处X表示含通配符的字符串,Y表示待匹配的字符串。
like(含通配符包含某个子串的字符串, 待匹配的字段值)
“待匹配的字段值” like “含通配符包含某个字串的字符串”
扩充知识:like(X,Y,Z):有3个参数的like函数,第三个参数X为转意符号
使用场景:当想要查询的字段值中包含了通配符,就需要指定转意字符,SQLite中提供的一个escapte子句用于指定转意符,与之对应的like函数可以通过第三个参数指定这个转意符。例如,匹配以百分号%开头的字符串,可用如下SQL语句:
select like (‘a%%’, ‘%abcd’ , ‘a’);------------------------------执行后,结果返回1(true)
分析:此处’a’被第三个参数设置为转意符,因此’a%%’中第一个%不会被看作通配符,而会被当作普通字符处理,第二个%才是通配符。所以,此条SQL语句匹配的是所有以”%”开头的字符串,”%abcd”符合匹配条件。
(6)nullif(X,Y)函数:
例:请阐述nullif(X,Y)函数的用法,并举例说明?
当X和Y不同时,nullif函数返回X;当X和Y相同时,nullif函数返回NULL,例如:
--输出20
select nullif(20,30);
--输出NULL
select nullif(20,20);
(7)substr()函数:截取字符串的子串
例:请阐述substr函数的用法,并举例说明?
substr()函数用于截取字符串的子串,substr有substr(X,Y)和substr(X,Y,Z)两种形式,X为原字符差,Y为要截取的子串的第一个字符在原字符串的位置(原字符串的起始位置为1),Z为截取的子字符串的长度。
若不指定Z,substr函数会截取Y以后的所有字符串;
若Y为正值,表示的起始位置从原字符串左侧开始
若Y为负值,表示的起始位置凑个原字符串的右侧开始
若Z为负数,会取Z的绝对值
举例如下:
--从”abcdefg”的第2个位置截取后面所有的字符串,结果是”bcdefg”
select substr(‘abcdefg’,2);
--从”abcdefg”的第2个位置截取长度为3的字符串,结果为”bcd”
select substr(‘abcdefg’,2,3);
--从”abcdefg”右侧第2个位置截取所有的字符串,结果为”fg”
select substr(‘abcdefg’,-2);
--从”abcdefg”右侧第4个位置截取长度为2的字符串,结果为”de”
select substr(‘abcdefg’,-4,2);
(8)typeof()函数:获取当前字段的数据类型
例:如何知道当前字段是什么数据类型?
select typeof(field1)
from table1
事务(Transaction)是并发控制的基本单位。所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。例如,银行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。所以,应该把它们看成一个事务。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。
针对上面的描述可以看出,事务的提出主要是为了解决并发情况下保持数据一致性的问题。
事务具有以下4个基本特征。
● Atomic(原子性):事务中包含的操作被看做一个逻辑单元,这个逻辑单元中的操作要么全部成功,要么全部失败。
● Consistency(一致性):只有合法的数据可以被写入数据库,否则事务应该将其回滚到最初状态。
● Isolation(隔离性):事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。
● Durability(持久性):事务结束后,事务处理的结果必须能够得到固化。
2.事务的语句 开始事物:BEGIN TRANSACTION
提交事物:COMMIT TRANSACTION
回滚事务:ROLLBACK TRANSACTION
3.
事务的4个属性 ①原子性(Atomicity):事务中的所有元素作为一个整体提交或回滚,事务的个元素是不可分的,事务是一个完整操作。②一致性(Consistemcy):事物完成时,数据必须是一致的,也就是说,和事物开始之前,数据存储中的数据处于一致状态。保证数据的无损。③隔离性(Isolation):对数据进行修改的多个事务是彼此隔离的。这表明事务必须是独立的,不应该以任何方式以来于或影响其他事务。④持久性(Durability):事务完成之后,它对于系统的影响是永久的,该修改即使出现系统故障也将一直保留,真实的修改了数据库
4.
事务的保存点 SAVE
TRANSACTION 保存点名称 --自定义保存点的名称和位置
ROLLBACK TRANSACTION
保存点名称 --回滚到自定义的保存点
网友评论
https://www.jianshu.com/p/191d1e21f7ed