Force Close
:关键字 Fatal
Application Not Response
:关键字 ANR
。
1、Singleton,单例模式:保证一个类只有一个实例,并提供一个访问它的全局访问点
2、Abstract Factory,抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们的具体类。
3、Factory Method,工厂方法:定义一个用于创建对象的接口,让子类决定实例化哪一个类,Factory Method使一个类的实例化延迟到了子类。
4、Observer,观察者模式:定义对象间一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知自动更新。
5、Visitor,访问者模式:表示一个作用于某对象结构中的各元素的操作,它使你可以在不改变各元素类的前提下定义作用于这个元素的新操作。
1、MVC是模型(model)-视图(view)-控制器(controller)的缩写,用一种业务逻辑、数据、界面显示分离的方法组织代码,将业务逻辑聚集到一个部件里面,在改进和个性化定制界面及用户交互的同时,不需要重新编写业务逻辑。
2、MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter(MVC中的Controller)来进行的,所有的交互都发生在Presenter内部,而在MVC中View会直接从Model中读取数据而不是通过Controller。
在MVC里,View是可以直接访问Model的!从而,View里会包含Model信息,不可避免的还要包括一些业务逻辑。 在MVC模型里View是依赖于Model的。因此有一些业务逻辑在View里实现了,业务逻辑是无法重用的。
3、MVVM是Model-View-ViewModel的简写,MVVM其实是对MVP的一种改进,他将Presenter替换成了ViewModel,并通过双向的数据绑定来实现视图和数据的交互。也就是说只需要将数据和视图绑定一次之后,那么之后当数据发生改变时就会自动的在UI上刷新而不需要自 己进行手动刷新。
内部类相对于外部类来说,前者是后者的方法,所以当前者是static修饰的时候,在前者中不能访问后者中的非static属性和方法;
相反,当前者是非static修饰的时候,前者可以访问后者中的任何成员。
静态内部类可以不依赖于外部类实例被实例化。而通常的内部类需要在外部类实例化后才能实例化。普通的内部类对象隐含地保存了一个引用指向创建它的外围类对象。 静态内部类的对象并不需要其外围类的对象。
泛型的本质便是类型的参数化,就是把类型像方法的参数那样传递。
编译器没法帮忙做类型检查,导致代码在运行时易于出现ClassCastException异常。因此,类型安全问题只能由程序员自己来把关了,泛型的使用避免了很多运行时的ClassCastException异常,程序员也无需记住各种对象的类型和担心类型匹配问题了,同时大部分情况下也不用做类型强制转换工作了。
有界类型是在类型参数部分指点extends或super,分别用上限或下限限制类型范围。
public void killAll(ArrayList
反射就是把java类中的各种成分映射成一个个的Java对象。反射机制是在运行状态中,对于任意一个类都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
反射获取类的三种方法:
(1)Class c1=Class.forName("类的全路径")。(最常用)
(2)Student s = new Student();
Class c2 = s.getClass();
(3)Class c3 = Student.class;
通过反射可以获取属性、方法、构造方法等。
(1)接口的方法默认是public,所有方法在接口中不能有实现,抽象类可以有非抽象的方法
(2)接口中的实例变量默认是final类型的,而抽象类中则不一定
(3)一个类可以实现多个接口,但最多只能实现一个抽象类
(4)一个类实现接口的话要实现接口的所有方法,而抽象类不一定
(5)接口不能用new实例化,但可以声明,但是必须引用一个实现该接口的对象
抽象是对类的抽象是一种模板设计,接口是行为的抽象是一种行为的规范。关注一个事物的本质的时候用抽象类;当你关注一个操作的时候用接口。
(1)LinearLayout:线性布局,垂直或水平依次排列。
(2)RelativeLayout:相对布局,通过相对定位方式。
(3)FrameLayout:没定位方式,控件都放左上角。
(4)TableLayout:表格方式排列。
(5)AbsoluteLayout,绝对位置布局。
(1)SharedPreferences存储:适合保存少量数据,适合键值对存储,存储如putString()。
(2)SQLite数据库:继承SQLiteOpenHelper抽象类,2个方法onCreate()和onUpgrade()分别创建和升级数据库,还有2个方法getReadableDatabase()和getWritableDatabase()用来创建或打开数据库并返回一个可对数据库读写的对象。
SQLiteOpenHelper构造方法有4个参数,一是context,二是数据库名,三是查询数据返回一个自定义的Cursor一般传null,四是当前数据库版本号。
CURD操作:添加用insert(),更新用update()如db.update("Book",values,"name=?",new String[]{"song"}),删除用delete()。查询最复杂用query()有7个参数,1表名,2查哪几列,3和4约束查某一行或几行,5是group by,6是group by后的过滤,7是查询结果排 序, query()会返回一个Cursor对象,可用moveToFirst(),getColumnIndex()和isAfterLast()操作Cursor。也可直接用sql操作数据库,db.rawQuery("select * from book",null),db.execSQL("insert into Book(name,price) values(?,?),new String[] {"song","15.96"}")。除查询用rawQuery()都用execSQL()。SQLite可以解析大部分标准SQL语句,例如select * from 表名 where 条件子句 group by 分组字句 having ... order by 排序子句。Sql的limit指定查询哪些数据,count指定返回匹配的行数。having称为分组滤过条件,也就是说是分组需要的条件,所以必须与groupby联用,聚合函数计算的结果可以当条件来使用,因为它无法放在where里只能通过having这种方式来解决。
(3)用io流文件存储:调用Environment的getExternalStorageState()方法判断手机上是否插了sd卡,调用Environment.getExternalStorageDirectory()方法来获取外部存储器也就是SD卡的目录,或者使用"/mnt/sdcard/"目录。(4)ContentProvider
(5)网络存储
1、Activity
Activity生命周期:onCreate()第一次创建,onStart()不可见变可见,onResume()获得用户焦点时用,onPause()被遮挡时用,onStop()完全不可见时用,onDestroy()销毁之前用,onRestart()重启时用.
解决活动被回收时数据得不到保存用onSaveInstanceState(),此方法携带参数Bundle,可用bundle.putString(),再用onCreate(Bundle b)里b.getString()取出,也可把数据存Bundle里再存入Intent里.
返回数据给上一个活动:活动1用startActivityForResult(intent,1)启动活动2,并用onActivityResult()接受活动2返回的数据,活动2用setResult(RESULT_OK,intent)向上一个活动返回数据,再用finish()销毁当前活动。
碎片(Fragment):是一种可嵌入活动中的UI片段。写一个类继承Fragment通过LayoutInflater的inflater()方法动态加载布局,再在
一个fragment必须总是绑定到一个activity中,虽然fragment有自己的生命周期,但同时也被它的宿主activity的生命周期直接影响。当一个fragment作为activity的布局文件的一部分添加进去的时候,它就在activity的视图层次中的ViewGroup中。
任务栈(Activity栈):
任务其实就是activity的栈,由一个或多个Activity组成,栈底的是启动整个任务的Activity,栈顶的是当前运行的用户可以交互的Activity,任务中的所有activity是作为一个整体进行移动的。整个的任务(即activity栈)可以移到前台,或退至后台。
Activity的LaunchMode:
多次启动同一个Activity时,系统会创建多个实例把他们放入任务栈,点击back会一一回退直到栈空系统回收栈,是一种后进先出结构。
(1)standard标准默认模式,每启动一个Activity都会创建一个实例不管是否已存在。
(2)singleTop栈顶复用模式,当新Activity已位于栈顶不会创建新的,否则会创建。
(3)singleTask栈内复用模式,只要Activity在栈中存在都不会创建新实例。
(4)singleInstance单例模式,加强版的singleTask,除了singleTask所有特性外此模式的Activity只能单独位于一个任务栈中。
Activity A跳转到Activity B再返回A的生命周期:Activity A(onCreate、onstart、onResume、onPause),Activity B(onCreate、onstart、onResume、),Activity A onStop,Activity B onPause,Activity A(onRestart、onstart、onResume),Activity B(onStop、onDestory)。
Fragment:生命周期:创建时:onAttach()、onCreate()、onCreateView()、onActivityCreated()
对用户可见时:onStart()、onResume(),进入后台时:onPause()、onStop(),被销毁时(或者持有它的activity被销毁):onPause()、onStop()、onDestroyView()、onDestroy() 、onDetach()
Fragments的大部分状态都和activity很相似,但fragment有一些新的状态:
onAttached() 当fragment被加入到activity时调用(在这个方法中可以获得所在的activity)。
onCreateView() 当activity要得到fragment的layout时,调用此方法,fragment在其中创建自己的layout(界面)。
onActivityCreated() 当activity的onCreated()方法返回后调用此方法
onDestroyView() 当fragment中的视图被移除的时候,调用这个方法。
onDetach() 当fragment和activity分离的时候,调用这个方法。
一旦activity进入resumed状态(也就是running状态),你就可以自由地添加和删除fragment了。因此,只有当activity在resumed状态时,fragment的生命周期才能独立的运转,其它时候是依赖于activity的生命周期变化的。
2、Service
使用方法:继承Service类,在Manifest中注册。在已绑定Service时,继承Binder类实现自定义IBinder对象通过onBind()所返回的IBinder对象传给ServiceConnection对象里onServiceConnected(ComponentName name,IBinder service)方法的sercice参数。
生命周期:onCreate(创建)、onStartCommand(启动)、onDestroy(关闭)、onBind(返回IBinder对象)、onUnbind(与绑定的所有客户端都断开连接)。其中startService方式是onStartCommand(),bindService方式是onBind()、onUnbind()。
startService与bindService区别:
(a)startedService:服务的onStartCommand()方法被调用。当服务是started状态时,其生命周期与启动它的组件无关,并且可以在后台无限期运行,即使启动服务的组件已经被销毁。所以需要调用stopSelf()或者stopService()方法停止。
(b)使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
详细可参考另一篇文章《AIDL、Binder、进程间通信方法》。
Service 与 IntentService 的区别:IntentService是继承于Service 并处理异步请求的一个类,IntentService会起一个新线程来处理耗时操作,启动IntentService的方式和启动传统Service一样,同时,当任务执行完后IntentService会自动停止不需要手动控制。另外,可以启动IntentService多次,每一个耗时操作会以工作队列的方式在IntentService的onHandleIntent回调方法中执行,并且每次只会执行一个工作线程,执行完第一个再执行第二个。所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。
3、Content Provider
CountentProvider的底层实现用的是Binder,用于在不同程序之间实现数据共享(如需访问通讯录、相册),以一个或多个表(Sqlite数据库)的形式将数据呈现给外部应用,ContentResolver用于从ContentProvider中获取或存入数据。
使用方法:创建类继承ContentProvider重写Query()、insert()、update()、delete()等方法供外部间接调用,并在Manifest中注册(authorities属性为该ContentProvider的唯一标识)。外部类用getContentResolver()获取ContentResolver并通过ContentProvider的Uri(需以content://开头)做CURD操作,实际上就是调用了ContentProvider的CURD方法。
为了知道外界要访问哪个表,需定义单独的Uri和Uri_Code,用UriMatcher.addURI()关联起来。
ContentProvider的生命周期只有onCreate()一个方法,当被ContentResolver第一次访问时调用,只会调用一次。
ContentObserver:应用程序需要实时监听ContentProvider所共享数据的改变时需要用到(继承ContentObserver类重写onChange())。方法contentResolver.notifyChanges(uri,null)可通知所有注册在该uri的监听着数据发生了改变。
防止sql注入:使用参数化查询,如SQLiteDatabase对象自带的防SQL注入的方法比如query(),insert(),update等,别直接写sql。
权限控制:ContentProvider的创建者和使用者都需要在AndroidManifest.xml
中使用
静态指定,还可以单独声明读或写权限比如
4、Broadcast Receiver
使用方法:继承BroadcastReceiver重写onReceive()方法,action匹配的Intent发送时就会被触发,发送广播用sendBroadcast()。
两种方式注册在代码中registerReceiver(receiver,intentFilter),在Manifest的
BroadcastReceiver.abortBroadcast()截断广播不会再传递。
分为标准广播和有序广播,标准广播:异步执行,所有广播接收器同一时间收到。 有序广播:同步执行,同一时刻只有一个能收到,结束后才会继续传播,前面的广播接收器可截断正在传递的广播。
(1)你的应用可以使用它对外部事件进行过滤,只对感兴趣的外部事件(如当电话呼入时,或者数据网络可用时)进行接收并做出响应。广播接收器没有用户界面。然而,它们可以启动一个activity或serice来响应它们收到的信息,或者用NotificationManager来通知用户。通知可以用很多种方式来吸引用户的注意力,例如闪动背灯、震动、播放声音等。一般来说是在状态栏上放一个持久的图标,用户可以打开它并获取消息。
(2)动态注册广播接收器特点是当用来注册的Activity关掉后,广播也就失效了。静态注册无需担忧广播接收器是否被关闭,只要设备是开启状态,广播接收器也是打开着的。也就是说哪怕app本身未启动,该app订阅的广播在触发时也会对它起作用。
Android 8.0开始对于静态广播的发送需要加入如下:
intent.putExtra("com.cns.android.intent.extra.command", "aaaaaaa");
ComponentName componentName = new ComponentName(ReceiverActivity.this,
"com.papaya.android.test.mqttreceiver.MyReceiver");
intent.setComponent(componentName);
sendBroadcast(intent);
Intent(意图)的概念:表示我们要执行的某个操作,一般用于启动活动,服务或发送广播。
Intent可以传输4种类型数据Serializable、charsequence、Parcelable、Bundle。
Intent通过下面的属性来描述以上的某个意图:
1、Action(动作):用来表示意图的动作。
2、category(类别):用来表示动作的类别。
3、data(数据):表示与动作要操作的数据。
4、type(数据类型):对data类型的描述。
5、extras(附件信息):附件信息。
6、component(目标组件):目标组件。
1、调用者生成Intent对象,并设置相关属性。生成的Intent分为以下两种:
显示Intent:对于明确指出了目标组件名称的Intent(调用setComponent或setClass来指定)。如Intent in = new Intent(OneActivity.this,TwoActivity.class).
隐式Intent:对于没有明确指出目标组件名称的Intent。于隐式Intent,由于没有明确的目标组件名称,所以必须包含足够的属性信息,他们是:Action,Data,Category。再由Android系统帮助应用程序寻找与Intent请求意图最匹配的组件。如Intent in = new Intent("com.example.test.Action_START0"),传入的值要和配置文件里的
2、向Android提交Intent请求:
根据要调用的组件不同,请求方式不同:startActivity(Intent),startServer(Intent),sendBroadcast(Intent)。
3、注册目标组件,配置IntentFilter。
是目标组件在Android-Manifest.xml中声明自己所含组件的过滤器(即可以匹配哪些Intent请求)。一个没有声明Intent-Filter的组件只能响应指明自己名字的显式Intent请求,而无法响应隐式Intent请求。而一个声明了IntentFilter的组件既可以响应显式 Intent请求,也可以响应隐式Intent请求。在通过和IntentFilter比较来解析隐式Intent请求时,Android将以下三个因素作为选择的参考标准。Action,Data,Category。而Extra和Flag在解析收到Intent时是并不起作用的。
应用程序的组件为了告诉Android自己能响应、处理哪些隐式Intent请求,可以声明一个甚至多个IntentFilter。每个IntentFilter描述该组件所能响应Intent请求的能力——组件希望接收什么类型的请求行为,什么类型的请求数据。比如之前请求网页浏览器这个 例子中,网页浏览器程序的IntentFilter就应该声明它所希望接收的Intent Action是WEB_SEARCH_ACTION,以及与之相关的请求数据是网页地址URI格式。隐式Intent和IntentFilter进行比较时的三要素是Intent的Action、Data以及Category。实际上,一个隐式Intent请求要能够传递给目标组件,必要通过这三个方面的检查。如果任何一方面不匹配,Android都不会将该隐式Intent传递给目标组件。
是一种与开发语言无关的、轻量级的数据格式,Json类型的数据一般是 JSONObject(对象),JSONArray(数组)。
是一种通用的数据交换格式,XML的解析方式分为四种:1、DOM解析;2、SAX解析;3、JDOM解析;4、DOM4J解析。DOM4J性能最好。解析就是取根节点然后遍历出每个子节点。
强引用是编程过程中使用的最简单的引用,如代码String s=”abc”中变量s就是字符串对象”abc”的一个强引用,任何被强引用指向的对象都不能被垃圾回收器回收。
弱引用通过WeakReference类实现,对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存(立即回收的方式)。
(1)在请求结果回调处理数据前用isFinishing()和isDestroy()判断。
(2)在presenter中对activity对象实行弱引用,也就是不直接持有activity对象的引用,当activity被销毁后我们的presenter所持有的弱引用也能够被内存回收。
专门用于服务其他的线程,如果其他的线程(即用户自定义线程)都执行完毕,连main线程也执行完毕,那么jvm就会退出(即停止运行)——此时,连jvm都停止运行了,守护线程当然也就停止执行了。GC即是守护线程。
1.最常见的解决方案就是提供多套图和多套布局,另外布局不要使用绝对布局使用相对布局和百分比布局。用dp来进行适配而不用px,因为dp适配是根据“最小宽度(Smallest-width)限定符”来找的,80%的手机的最小宽度dp值(widthPixels/density)都为360dp,这样就大大减少了dimens.xml文件。
2. 使用自动拉伸位图(.9.png)
3.使用相对布局(RelativeLayout),禁用绝对布局(AbsoluteLayout).
4.使用"wrap_content"、"match_parent"和"weight“来控制视图组件的宽度和高度.
Android的消息机制主要是指Handler的运行机制。
Message里面可以包含消息处理对象和处理数据等等,由MessageQueue进行统一的排列,然后交由Handler进行处理.
MessageQueue主要有插入和读取两个操作。
Looper在这套消息机制里面可以称为一个轮询器,它会不断的从MessageQueue中轮询查看是否有新消息,如果有新消息这个轮询器就会处理,创建一个Looper的方法为Looper.prepare()
Handler是消息的处理者,它的工作主要是消息的发送和接收以及处理。
Handler的作用:在非UI线程中完成耗时操作,在UI线程中去更新UI。
Android中为什么设计为只能在UI线程中去更新UI:解决多线程并发问题。
子线程耗时操作往主线程发送消息更新ui:在子线程中创建Message消息,sendmessage(message)发给主线程,在主线程创建Handler用handleMessage()中获得Message消息,进而处理更新UI界面。
主线程往子线程发送消息:子线程中需要先调用Looper.prepare()才能再创建Handler对象和handleMessage(),最后调用Looper.loop()取出消息对象。
非UI线程真的不能更新UI吗 ? 是可以的,源码中checkThread()检查是否是主线程有办法可以绕开。
HandlerThread:本质上是一个建立了Looper的Thread,将loop转到子线程中处理,就是分担MainLooper的工作量降低了主线程的压力,拥有自己的消息队列不会干扰或阻塞UI线程,它只有一个线程。
动画可以分为两类,传统动画和Android3.0之后出现的属性动画。传统动画又包括帧动画(FrameAnimation)和补间动画(Tweened Animation)。
帧动画是最容易实现的一种动画,这种动画更多的依赖于完善的UI资源,他的原理就是将一张张单独的图片连贯的进行播放。
补间动画又可以分为四种形式,分别是alpha(淡入淡出),translate(位移),scale(缩放大小),rotate(旋转)。
属性动画是对于对象属性的动画。因此所有补间动画的内容,都可以通过属性动画实现。属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。ValueAnimator还负责管理动画的播放次数、播放模式、以及对动画设置监听器等。
View是Android中所有控件的基类,ViewGroup也继承了View。
构造函数、onAttachedToWindow(如果需要保存为全局的引用)、Measure、onMeasure、layout、onLayout(分配大小和位置给它的每一个子View)、dispatchDraw、draw、onDraw。
事件分发是当用户触摸屏幕时(View或ViewGroup派生的控件)将产生点击事件,Touch事件的相关细节(发生触摸的位置、时间等)被封装成MotionEvent对象,系统需把这个事件传递给一个具体的View去处理。事件分发的本质是将点击事件(MotionEvent)传递到某个具体的View处理的整个过程。事件在Activity、ViewGroup、View之间进行传递。事件分发过程由dispatchTouchEvent() 、onInterceptTouchEvent()和onTouchEvent()这些方法协作完成。
ViewGroup相较View多出了onInterceptTouchEvent()方法,这个方法的作用类似于堵住水管。点击事件产生后,首先传递给根ViewGroup它的dispatchTouchEvent(),如果它的onInterceptTouchEvent()返回true表示拦截事件,这个事件就会让他处理它的onTouchEvent()被调用,而且后续事件将不会传递给onInterceptTouchEvent()直接传递给onTouchEvent()而目标view将接收不到任何事件。如果这个ViewGroup的onInterceptTouchEvent()返回false表示不拦截,事件就会传递给它的子元素然后子元素的dispatchTouchEvent() 被调用,如此反复直到被最终处理。
1.ViewRootImpl类的requestLayout()方法来调度一次完成的绘制流程
2.measure: 判断是否需要重新计算View的大小,需要的话则计算;
3.layout: 判断是否需要重新计算View的位置,需要的话则计算;
4.draw: 判断是否需要重新绘制View,需要的话则重绘制。
滑动冲突场景:界面中内外两层同时可以滑动就会产生滑动冲突。场景有三种,(1)外部滑动方向和内部滑动方向不一致(2)外部滑动方向和内部滑动方向一致(3)上面两种情况的嵌套
滑动冲突解决方式:推荐使用外部拦截法,即点击事件都先经过父容器的拦截处理,父容器根据需要选择是否拦截事件就可以解决。
举例:在onInterceptTouchEvent(MotionEvent event)中做switch判断,当event.getAction()等于MotionEvent.ACTION_MOVE时做是否拦截的判断。
onMeasure()负责对当前View的尺寸进行测量处理大小(宽度和高度),invalidate()用来重绘View,最终要设置setMeasuredDimension(int width, int height),onDraw()负责把当前这个View绘制出来,Canvas用于绘制对象Paint用于造型(颜色等)。
ViewGroup是个View容器,它装纳child View并且负责把child View放入指定的位置。
1.首先得知道各个子View的大小,才知道当前的ViewGroup该设置为多大去容纳它们。
2.ViewGroup和子View的大小算出来了之后,接下来就是去摆放了,这得根据你定制的需求去摆放,比如你想让子View按照垂直顺序一个挨着一个放,或者是按照先后顺序一个叠一个去放。
3.已经知道怎么去摆放还不行,决定了怎么摆放就是相当于把已有的空间”分割”成大大小小的空间,每个空间对应一个子View,接下来就是把子View对号入座了把它们放进它们该放的地方去。
在res/values/中创建一个attrs.xml,在根节点resources内添加declare-styleable节点,declare-styleable节点只有一个name属性且必填name可以自由定义。attr节点有两个属性name和format,其中name为必填可自由定义,format用于规定自定义属性的值的类型,举例如下:
如要使用系统定义好的属性,则:
布局文件里的命名空间表示下面使用的属性应该去哪找,如:xmlns:openxu="http://schemas.android.com/apk/com.example.openxu.myview"
,而com.example.openxu.myview
为自己的应用程序包名。
通过重写onTouchEvent()判断点击坐标是否在范围内,如:圆形自定义view的点击坐标到圆心距离大于圆形半径则在外部,不响应点击事件。
View.TouchDelegate:当控件太小导致无法准确点击,TouchDelegate类可以让某个控件处理比它实际占用空间更大的触摸范围。
例子:String url = "http://img1.dzwww.com:8080/tupian_pl/20150813/16/78407436.jpg";
ImageView imageView = (ImageView) findViewById(R.id.imageView);
Glide.with(context) .load(url) .into(imageView);
原理:Glide原理的核心是为bitmap维护一个对象池。对象池的主要目的是通过减少大对象内存的分配以重用来提高性能。图片的加载任务会与activity或者Fragment的生命周期绑定,当界面执行onStop的使用自动暂定,而当执行onStart的时候又会自动重新开启,同时Glide会对网络状态做监听,当网络状态发生改变时,会重启失败的任务,预览图的使用为加快加载速度提高体验。
图片缓存:
三级缓存的原理首次加载的时候通过网络加载获取图片,然后保存到内存和SD卡中,之后运行APP时优先访问内存中图片缓存没有则加载本地SD卡中的图片再没有才网络加载,Glide框架默认开启了内存缓存的。
图片高效加载(图片压缩):核心思想是用BitmapFactory.Options来加载所需尺寸的图片。大部分情况下图片原始尺寸比ImageView要大,可通过BitmapFactory.Options加载缩小后的图片,避免OOM提高性能。
BitmapFactory.Options缩放图片用它的inSampleSize参数即采样率,Options.inSampleSize大于1的整数才有缩小效果,如为2采样后的图片宽/高均为原图的二分之一。
缓存策略:三级缓存
是一个处理网络请求的开源项目,是安卓端最火热的轻量级框架。
使用OkHttp3发送Http请求并获得响应的过程大体为:
(1)创建OkHttpClient对象。OkHttpClient为网络请求执行的一个中心,它会管理连接池,缓存,SocketFactory,代理,各种超时时间,DNS,请求执行结果的分发等许多内容。
(2)创建Request对象。Request用于描述一个HTTP请求,比如请求的方法是"GET"还是"POST",请求的URL,请求的header,请求的body,请求的缓存策略等。
(3)利用前面创建的OkHttpClient对象和Request对象创建Call对象。Call是一次HTTP请求的Task,它会执行网络请求以获得响应。OkHttp中的网络请求执行Call既可以同步进行,也可以异步进行。调用call.execute()将直接执行网络请求,阻塞直到获得响应。而调用call.enqueue()传入回调,则会将Call放入一个异步执行队列,由ExecutorService在后台执行。
(4)执行网络请求并获取响应。
内存泄漏:对象在内存heap堆中中分配的空间,当不再使用或没有引用指向的情况下,仍不能被GC正常回收的情况。多数出现在不合理的编码情况下,通常情况下,大量的内存泄漏会造成 OOM。
OOM:即OutOfMemoery,就是指内存溢出了。内存溢出是指APP向系统申请超过最大阀值的内存请求系统不会再分配多余的空间。在我们Android平台下,多数情况是出现在图片不当处理加载的时候。内存管理之道,无非就是先理解并找出内存泄漏的原因。
1、static是个好东西,声明赋值调用就是那么的简单方便,但是伴随而来的还有性能问题。由于static声明变量的生命周期其实是和APP的生命周期一样的,有点类似与Application。如果大量的使用的话,就会占据内存空间不释放,积少成多也会造成内存的不断开销直 至挂掉。static的合理 使用一般用来修饰基本数据类型或者轻量级对象,尽量避免修复集合或者大对象,常用作修饰全局配置项、工具类方法、内部类。
2、在处理异步操作的时候,handler + thread是个不错的选择。但是handler运行于主线程,不断处理来自MessageQueue的消息,如果handler还有消息需要处理但是Activity页面已经结束的情况下,Activity的引用其实并不会被回收,这就造成了内存泄漏。解决方案,一是在Activity的onDestroy方法中调用handler.removeCallbacksAndMessages(null);取消所有的消息的处理,包括待处理的消息;二是声明handler的内部类为static。
3、在查询SQLite数据库时,会返回一个Cursor,当查询完毕后,及时关闭,这样就可以把查询的结果集及时给回收掉。I/O流操作完毕,读写结束,记得关闭。
4、BroadCastReceiver、Service绑定广播和服务,一定要记得在不需要的时候给解绑,在Activity的onPause方法中调用unregisterReceiver()方法。
5、线程不再需要继续执行的时候要记得及时关闭,开启线程数量不易过多,一般和自己机器内核数一样最好,推荐开启线程的时候,使用线程池减少创建和销毁线程的次数,每个工作线程都可以被重复利用(线程池ExecutorService)。
6、String/StringBuffer当有较多的字符创需要拼接的时候,推荐使用StringBuffer。
switch case语句不写break:会执行跳转到的case xxx以及后面的case,直到遇到有break为止。如果都没有break就会执行所有case语句。
取消edittext默认自动弹出软键盘:在xml中edittext的父控件中(如LinearLayout)添加两个属性,android:focusable="true"
android:focusableInTouchMode="true"。