Handler是用来结合线程的消息队列来发送、处理“Message对象”和“Runnable对象”的工具。每个Handler实例以后会关联一个线程和该线程的消息队列。当你建立一个Handler的时候,从这时开始,它就会自动关联到所在的线程/消息队列,而后它就会陆续把Message/Runnalbe分发到消息队列,并在它们出队的时候处理掉。
Looper:
每个线程只有一个Looper,每一个线程在初始化Looper以后,而后Looper会维护好该线程的消息队列,用来存放Handler发送的Message,并处理消息队列出队的Message。它的特色是它跟它的线程是绑定的,处理消息也是在Looper所在的线程去处理,因此当咱们在主线程建立Handler时,它就会跟主线程惟一的Looper绑定,从而咱们使用Handler在子线程发消息时,最终也是在主线程处理,达到了异步的效果。
MessageQueue:
MessageQueue是一个消息队列,用来存放Handler发送的消息。每一个线程最多只有一个MessageQueue。MessageQueue一般都是由Looper来管理,而主线程建立时,会建立一个默认的Looper对象,而Looper对象的建立,将自动建立一个MessageQueue。其余非主线程,不会自动建立Looper。
Message: 消息对象,就是MessageQueue里面存放的对象,一个MessageQueu能够包括多个Message。当咱们须要发送一个Message时,咱们通常不建议使用new Message()的形式来建立,更推荐使用Message.obtain()来获取Message实例,由于在Message类里面定义了一个消息池,当消息池里存在未使用的消息时,便返回,若是没有未使用的消息,则经过new的方式建立返回,因此使用Message.obtain()的方式来获取实例能够大大减小当有大量Message对象而产生的垃圾回收问题。android
缘由:
(1)非静态内部类是会隐式持有外部类的引用,因此当其余线程持有了该Handler,线程没有被销毁,则意味着Activity会一直被Handler持有引用而没法致使回收。
(2)MessageQueue中若是存在未处理完的Message,Message的target也是对Activity等的持有引用,也会形成内存泄漏。
解决办法:
(1)使用静态内部类+弱引用的方式
(2)在外部类对象被销毁时,将MessageQueue中的消息清空。例如,在Activity的onDestroy时将消息清空算法
当上一个消息存在耗时任务的时候,会占用延时任务执行的时机,此时是不许确的。因为Loop.loop里面消息是串行取出并发给handler.dispatchMessage的,那么轮处处理第二个延时runnable的时候,MessageQueue类的next方法再执行到if(now < msg.when)的时候,就马上return了该msg,而后由handler.dispatchMessage处理,执行到该runnable的run方法编程
//ActivityThread.main()
public static void main(String[] args) {
....
//建立Looper和MessageQueue对象,用于处理主线程的消息
Looper.prepareMainLooper();
//建立ActivityThread对象
ActivityThread thread = new ActivityThread();
//创建Binder通道 (建立新线程)
thread.attach(false);
Looper.loop(); //消息循环运行
throw new RuntimeException("Main thread loop unexpectedly exited");
}
复制代码
ActivityThread的main方法主要就是作消息循环,一旦退出消息循环,那么你的应用也就退出了。Activity的生命周期都是依靠主线程的Looper.loop,当收到不一样Message时则采用相应措施。咱们的代码其实就是在这个循环里面去执行的,固然不会阻塞了。并且主线程Looper从消息队列读取消息,当读完全部消息时,主线程阻塞。子线程往消息队列发送消息,而且往管道文件写数据,主线程即被唤醒,从管道文件读取数据,主线程被唤醒只是为了读取消息,当消息读取完毕,再次睡眠。所以loop的循环并不会对CPU性能有过多的消耗。缓存
当使用内部类(包括匿名类)来建立Handler的时候,Handler对象会隐式地持有一个外部类对象(一般是一个Activity)的引用(否则你怎么可能经过Handler来操做Activity中的View?)。而Handler一般会伴随着一个耗时的后台线程(例如从网络拉取图片)一块儿出现,这个后台线程在任务执行完毕(例如图片下载完毕)以后,经过消息机制通知Handler,而后Handler把图片更新到界面。然而,若是用户在网络请求过程当中关闭了Activity,正常状况下,Activity再也不被使用,它就有可能在GC检查时被回收掉,但因为这时线程还没有执行完,而该线程持有Handler的引用(否则它怎么发消息给Handler?),这个Handler又持有Activity的引用,就致使该Activity没法被回收(即内存泄露),直到网络请求结束(例如图片下载完毕)。另外,若是你执行了Handler的postDelayed()方法,该方法会将你的Handler装入一个Message,并把这条Message推到MessageQueue中,那么在你设定的delay到达以前,会有一条MessageQueue -> Message -> Handler -> Activity的链,致使你的Activity被持有引用而没法被回收。处理方法:静态内部类+弱引用,还有一个就是handler.removeCallbacksAndMessages(null);,就是移除全部的消息和回调,简单一句话就是清空了消息队列。安全
LRU(Least Recently Used)是近期最少使用的算法,它的核心思想是当缓存满时,会优先淘汰那些近期最少使用的缓存对象。LruCache中维护了一个集合LinkedHashMap,该LinkedHashMap是以访问顺序排序的。当调用put()方法时,就会在结合中添加元素,并调用trimToSize()判断缓存是否已满,若是满了就用LinkedHashMap的迭代器删除队尾元素,即近期最少访问的元素。当调用get()方法访问缓存对象时,就会调用LinkedHashMap的get()方法得到对应集合元素,同时会更新该元素到队头。bash
内存泄露服务器
图片Bitmap相关网络
内存抖动 大量临时小对象频繁建立会致使内存碎片,程序频繁分配内存&垃圾收集器频繁回收内存。致使卡顿甚至内存溢出。数据结构
代码质量 & 数量
代码自己的质量(如 数据结构、数据类型等) & 数量(代码量的大小)可能会致使大量的内存问题,如占用内存大、内存利用率低等 平常不正确使用多线程
避免复杂的View层级。布局越复杂就越臃肿,就越容易出现性能问题,寻找最节省资源的方式去展现嵌套的内容;
尽可能避免在视图层级的顶层使用相对布局 RelativeLayout 。相对布局 RelativeLayout 比较耗资源,由于一个相对布局 RelativeLayout 须要两次度量来确保本身处理了全部的布局关系
布局层级同样的状况建议使用线性布局 LinearLayout 代替相对布局 RelativeLayout,由于线性布局 LinearLayout 性能要更高一些
不要使用绝对布局 AbsoluteLayout
将可重复使用的组件抽取出来并用include标签进行重用。若是应用多个地方的 UI用到某个布局,就将其写成一个布局部件,便于各个 UI 重用
使用 merge 标签减小布局的嵌套层次
帮助include标签排除多余的一层ViewGroup容器,减小view hierarchy的结构,提高UI渲染的性能
使用 ViewStub 标签来加载一些不经常使用的布局
ViewStub也能够用来加载布局文件,但与include标签彻底不一样。ViewStub是一个不可见的View类,用于在运行时按需懒加载资源,只有在代码中调用了viewStub.inflate()或者viewStub.setVisible(View.visible)方法时才内容才变得可见。
优化应用的启动速度。当应用启动一个应用时,界面的尽快反馈显示能够给用户一个良好的体验。为了启动更快,能够延迟加载一些 UI 以及避免在应用 Application 层级初始化代码。
使用include标签提升布局重用性
将一些通用的视图提取到一个单独的layout文件中,而后使用标签在须要使用的其余layout布局文件中加载进来,好比咱们本身App导航栏等。这样,便于对相同视图内容进行统一的控制管理,提升布局重用性。
*标签当中,能够重写全部layout属性的,如上面include中指定的layout属性将会覆盖掉titlebar中指定的layout属性。 而非layout属性则没法在标签当中进行覆写。另外须要注意的是,若是咱们想要在标签当中覆写layout属性, 必需要将layout_width和layout_height这两个属性也进行覆写,不然覆写效果将不会生效
外部拦截法 重写父容器的onInterceptTouchEvent方法
内部拦截法
//重写这个方法,而且在方法里面请求全部的父控件都不要拦截他的事件
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
getParent().requestDisallowInterceptTouchEvent(true);
return super.dispatchTouchEvent(ev);
}
复制代码
SurfaceView TexttureView
层级 单独 普通view
可否动画 否 能
硬件加速 必须
可否覆盖 否
消耗内存 少 多
使用版本 4.0之后
复制代码
onTouchListener的onTouch方法优先级比onTouchEvent高,会先触发。假如onTouch方法返回false会接着触发onTouchEvent,反之onTouchEvent方法不会被调用。只要view的CLICKABLE和LONG_CLICKABLE有一个为true,那么他就会消耗这个事件,即onTouchEvent方法返回true,无论它是否是DISABLE状态。而后就是当ACTION_UP事件发生时,会出发performClick方法,若是View设置了OnClickListener,那么performClick方法内部会调用它的onClick方法。总结:onTouch—–>onTouchEvent—>onclick
关于VSYNC
Vertical Synchronization,就是所谓的“垂直同步”。咱们也能够把它理解为“帧同步”。就是为了保证 CPU、GPU 生成帧的速度和 Display 刷新的速度保持一致。在 VSYNC 开始发出信号时,CPU和GPU已经就开始准备下一帧的数据了,赶在下个 VSYNC 信号到来时,GPU 渲染完成,及时传送数据给屏幕,Display 绘制显示完成。
双缓冲机制
双缓冲技术一直贯穿整个 Android 系统。由于实际上帧的数据就是保存在两个 Buffer 缓冲区中,A 缓冲用来显示当前帧,那么 B 缓冲就用来缓存下一帧的数据,同理,B显示时,A就用来缓冲!这样就能够作到一边显示一边处理下一帧的数据。
丢帧
因为某些缘由,好比咱们应用代码上逻辑处理过于负责或者过于复杂的布局,过分绘制(Overdraw),UI线程的复杂运算,频繁的GC等,致使下一帧绘制的时间超过了16ms,用户很明显感知到了卡顿的出现,也就是所谓的丢帧状况。
一、当 Display 显示第 0 帧数据时,此时 CPU 和 GPU 已经开始渲染第 1 帧画面,并将数据缓存在缓冲 B 中。可是因为某些缘由,就好像上面说的,致使系统处理该帧数据耗时过长或者未能及时处理该帧数据。
二、当 VSYNC 信号来时,Display 向 B 缓冲要数据,由于缓冲 B 的数据还没准备好,B缓冲区这时候是被锁定的,Display 表示你没准备好,只能继续显示以前缓冲 A 的那一帧,此时缓冲 A 的数据也不能被清空和交换数据。这种状况就是所谓的“丢帧”,也被称做“废帧”;当第 1 帧数据(即缓冲 B 数据)准备完成后,它并不会立刻被显示,而是要等待下一个 VSYNC,Display 刷新后,这时用户才看到画面的更新。
三、当某一处丢帧后,大几率会影响后面的绘制也出现丢帧,最后给用户感受就是卡顿了。最严重的直接形成ANR。
卡顿处理
在up事件里相应判断
public class RotateYAnimation extends Animation {
int centerX, centerY;
Camera camera = new Camera();
/**
* 获取坐标,定义动画时间
* @param width
* @param height
* @param parentWidth
* @param parentHeight
*/
@Override
public void initialize(int width, int height, int parentWidth, int parentHeight) {
super.initialize(width, height, parentWidth, parentHeight);
//得到中心点坐标
centerX = width / 2;
centerY = width / 2;
//动画执行时间 自行定义
setInterpolator(new OvershootInterpolator());
}
/**
* 旋转的角度设置
* @param interpolatedTime
* @param t
*/
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
final Matrix matrix = t.getMatrix();
camera.save();
//设置camera的位置
camera.setLocation(0,0,180);
camera.rotateY(180 * interpolatedTime);
//把咱们的摄像头加在变换矩阵上
camera.getMatrix(matrix);
//设置翻转中心点
matrix.preTranslate(-centerX, -centerY);
matrix.postTranslate(centerX,centerY);
camera.restore();
}
}
复制代码
数据处理
不少时候从咱们要对服务器上获取下来的列表数据进行一次二次加工,以便转化为咱们界面上要显示的数据,这些操做可能会比较耗时。好比字符串拼接、时间格式化等操做都是比较耗时的操做。比较好的实践是将列表数据的加工在notifyDataSetChanged()以前在后台线程作好,在Adapter中只作数据的绑定。
界面优化 优化布局层级减小过渡绘制、优化布局方式提升布局效率。
避免非必要的刷新
如列表控件,在滑动时不要去加载图片,能够在滑动监听里中止图片的加载。
onMeasure
测量本质就是测量自己有多大,也就是给mMeasuredWidth和mMeasuredHeight这两个属性赋值,也就是调用setMeasuredDimension这个方法。另外父view测量子view的时候调用的measure方法,还有一些衍生方法如measureChildWithMargins。
onLayout
做用是子view应该怎样放置,也就是设置子view的mLeft、mTop、mRight、mBottom属性。该方法在View中是空实现,很显然主要用于ViewGroup。父view放置子view的时候调用layout方法。
onDraw
具体长什么样。
contentprovider是运行在哪一个进程里面的
contentprovider的oncreate方法,运行在ui线程。可是其余的方法,运行在非ui线程,例如call、query、delete、insert、upate等方法
别的主线程调用它的时候会被阻塞吗?
别的主线程调contentprovider里面方法的时候,虽然他的call、query、delete、insert、upate等方法运行在非ui线程,可是其余调用方法是会被阻塞的。好比你在activity的oncreate方法中调用contentprovider的query等方法,oncreate方法会被阻塞
若是不一样的其余应用,同时调用了这个contentprovider的同一个方法,它们会相互阻塞吗?好比有三个应用同时都在调用这个provider的插入方法,它们会相互阻塞仍是并发运行
无论Provider访问者是同一个进程的不一样线程,仍是不一样的进程,Provider方其实是同一个Provider对象实例,当并发访问CP的同一个方法的时候,这个方法运行在不一样的线程中,不会相互影响
两种服务启动区别
同时用两种方式启动同一个service 会产生几个service实例?
同时调用两种方法启动同一个方法,只会启动一个服务,可是其生命周期有所不一样,取决于两种方法启动服务的前后顺序。
绑定的service 页面关闭后未解除绑定 如何销毁
bindService时LoadApk将ServiceConnection用map保存了起来,当Activity被destroy时会执行removeContextRegistrations来清除 该context的相关注册。因此Activity退出时服务也被解绑。
Intentservice原理
介绍:
IntentService是继承于Service并处理异步请求的一个类,在IntentService内有一个工做线程来处理耗时操做,启动IntentService的方式和启动传统Service同样,同时,当任务执行完后,IntentService会自动中止,而不须要咱们去手动控制。另外,能够启动IntentService屡次,而每个耗时操做会以工做队列的方式在IntentService的onHandleIntent回调方法中执行,而且,每次只会执行一个工做线程,执行完第一个再执行第二个,以此类推。
原理:
IntentService第一次启动时会调用onCreate方法,建立一个HandlerThread对象,并开启HandlerThread线程,而后使用HandlerThread的Looper对象初始化mServiceHandler对象,经过mServiceHandler发送消息在HandlerThread中执行操做。每次启动IntentService都会调用onStartCommand()方法,onStartCommand方法会调用onStart方法。onStart方法中只是调用mServiceHandler发送了一个消息,交给HandlerThread处理,HandlerThread在handleMsg方法里调用handleIntent处理消息,完成后调用stop方法中止任务。
* Bundle
四大组件中的三大组件(Activity,BroadcaseReceiver,Service)都是支持在Intent中传递Bundle数据的,因为Bundle实现了Parcelable接口,因此能够很方便的在不一样进程间传输。
* 使用文件共享
享文件也是一种不错的进程间通讯方式,两个进程经过读/写同一个文件来交换数据。但有一点要注意:android系统是基于Linux的,使得其并发读/写文件能够没限制地进行,甚至两线程同时对同一文件进行写操做都是容许的,尽管这可能出问题。So,重点:文件共享方式适合在对数据同步要求不高的进程间进行通讯,而且要妥善处理并发读/写问题。
* Messenger
Messenger译为信使,顾名思义,主要做用就是传递消息。经过它可在不一样进程中传递Message对象,在Message中放入要传递的数据,便可轻松地实现数据的进程间传递了。Messenger是一种轻量级的IPC方案,底层实现是AIDL。
* AIDL
上面说到Messenger,其是以串行的方式处理客户端发来的消息,若是有大量的并发请求,那么使用Messenger就不太合适了。同时Messenger主要做用就是传递消息,不少时候咱们可能须要跨进程调用服务端的方法,这种情形Messenger就没法作到了,可是咱们可使用AIDL来实现跨进程的方法调用。
* ContentProvider
ContentProvider是Android中提供的专门用于不一样应用间进行数据共享的方式,从这一点,它天生就适合进程间通讯。和Messenger同样,contentProvider的底层实现一样是Binder,因而可知,Binder在Android中是何等的重要。虽然ContentProvider底层是用Binder,但它的使用过程要比AIDL 简单许多,由于系统已经作了封装。系统预置了许多ContentProvider,好比通信录信息,日程变信息等,要跨进程访问这些信息,只须要经过ContentResolver的query、update、insert和delete方法便可。
* Socket
Socket 也称为“套接字”。是网络通讯中的概念,它分为流式套接字和用户数据报套接字两种,分别对应于网络传输控制层中的TCP和UDP协议。TCP是面向链接的协议,提供稳定的双向通讯功能,TCP链接的创建须要通过“三次握手”才能完成,为了提供稳定的数据传输功能,其自己提供了超时重传机制,所以具备很高的稳定性。而UDP是无链接的,提供不稳定的单向通讯功能,固然UDP也能实现双向通讯功能。在性能上,UDP 具备更高的效率,缺点是不保证数据必定可以正确传输,尤为是在网络阻塞的状况下。
复制代码
* 服务端
服务端首先要建立一个Service用来监听客户端的链接请求,而后建立一个AIDL文件,将暴露给客户端的接口在这个AIDL文件中声明,最后在Service中实现这个接口便可。    
* 客户端
客户端所要作的事情就稍微简单一些,首先须要绑定服务Service,绑定成功后,将服务端的Binder对象转成AIDL借口所属的类型,接着就能够调用AIDL中的方法了。
* AIDL接口建立
建立一个后缀为AIDL的文件,里面声明接口和方法。
* 远程服务端Service的实现
建立一个Binder对象并在onBind中返回它,这个对象继承自.Stub并实现了它内部的AIDL方法
* 客户端的实现
首先绑定远程服务,绑定成功后将服务端返回的Binder对象转换成AIDL接口,而后就能够经过这个接口去调用服务端的远程方法
复制代码
Android系统是基于Linux系统的,理论上应该使用Linux内置的IPC方式。Linux中的IPC方式有管道、信号量、共享内存、消息队列、Socket,Android使用的Binder机制不属于Linux。Android不继承Linux中原有的IPC方式,而选择使用Binder,说明Binder具备必定的优点。 Android系统为了向应用开发者提供丰富的功能,普遍的使用了Client-Server通讯方式,如媒体播放、各类传感器等,Client-Server的通讯方式是Android IPC的核心,应用程序只须要做为Client端,与这些Server创建链接,便可使用这些功能服务。
下面经过一些功能点,一一排除Linux系统中五种IPC方式,解释为何Android选择使用Binder:
从通讯方式上说,咱们但愿获得的是一种Client-Server的通讯方式,但在Linux的五种IPC机制中,只有Socket支持这种通讯方式。虽然咱们能够经过在另外四种方式的基础上架设一些协议来实现Client-Server通讯,但这样增长了系统的复杂性,在手机这种条件复杂、资源稀缺的环境下,也难以保证可靠性;
从传输性能上说,Socket做为一款通用接口,其传输效率低,开销大,主要用在跨网络的进程间通讯和本机上进程间的低速通讯;消息队列和管道采用存储-转发方式,即数据先从发送方拷贝到内存开辟的缓存区中,而后再从内核缓存区拷贝到接收方缓存区,至少有两次拷贝过程;共享内存虽然无需拷贝,但控制复杂,难以使用;而Binder只须要拷贝一次;
从安全性上说,Android做为一个开放式的平台,应用程序的来源普遍,所以确保只能终端的安全是很是重要的。Linux传统的IPC没有任何安全措施,彻底依赖上层协议来确保,具体有如下两点表现:
综上,Binder是一种基于Client-Server通讯模式的通讯方式,传输过程只须要一次拷贝,能够为发送方添加UID/PID身份,支持实名Binder和匿名Binder,安全性高。
Singletask 生命周期 launchMode为singleTask的时候,经过Intent启到一个Activity,若是系统已经存在一个实例,系统就会将请求发送到这个实例上,但这个时候,系统就不会再调用一般状况下咱们处理请求数据的onCreate方法,而是调用onNewIntent方法。
onNewIntent->onRestart->onStart->onResume
Android onPause和onStop的比较
Fragmentmanager和supportfm 区别
3.0如下:getSupportFragmentManager()
3.0以上:getFragmentManager()
嵌套fragment获取manager getChildFragmentManager()是fragment中的方法, 返回的是管理当前fragment内部子fragments的manage
@Override
public void onResume() {
super.onResume();
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if(keyEvent.getAction() == KeyEvent.ACTION_DOWN && i == KeyEvent.KEYCODE_BACK){
Toast.makeText(getActivity(), "按了返回键", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
}
复制代码
// ①先定义接口BackHandleInterface
public interface BackHandleInterface {
void onSelectedFragment(BackHandleFragment backHandleFragment);
}
复制代码
// ②定义公用的Fragment
public abstract class BackHandleFragment extends Fragment {
private BackHandleInterface backHandleInterface;
/**
* 全部继承BackHandledFragment的子类都将在这个方法中实现物理Back键按下后的逻辑
* FragmentActivity捕捉到物理返回键点击事件后会首先询问Fragment是否消费该事件
* 若是没有Fragment消息时FragmentActivity本身才会消费该事件
*/
public abstract boolean onBackPressed();
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getActivity() instanceof BackHandleInterface){
this.backHandleInterface = (BackHandleInterface)getActivity();
}else{
throw new ClassCastException("Hosting Activity must implement BackHandledInterface");
}
}
@Override
public void onStart() {
super.onStart();
backHandleInterface.onSelectedFragment(this);
}
}
复制代码
//须要实现监听的Fragment的Activity实现接口
//主要的是onSelectedFragment()和onBackPressed()其余方法能够忽略
public class EdittextActivity extends AppCompatActivity implements BackHandleInterface {
private BackHandleFragment backHandleFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
addFragment(R.id.fragmentContainer, new EdittextFragment());
}
public void addFragment(int containerViewId, Fragment fragment){
final FragmentTransaction transaction = this.getSupportFragmentManager().beginTransaction();
transaction.add(containerViewId, fragment);
transaction.commit();
}
@Override
public void onSelectedFragment(BackHandleFragment backHandleFragment) {
this.backHandleFragment = backHandleFragment;
}
@Override
public void onBackPressed() {
//if判断里面就调用了来自Fragment的onBackPressed()
//同样!!,若是onBackPressed是返回false,就会进入条件内进行默认的操做
if(backHandleFragment == null || !backHandleFragment.onBackPressed()){
if(getSupportFragmentManager().getBackStackEntryCount() == 0){
super.onBackPressed();
}else{
getSupportFragmentManager().popBackStack();
}
}
}
}
复制代码
//须要监听的Fragment
public class EdittextFragment extends BackHandleFragment {
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_edittext, container, false);
return view;
}
@Override
public boolean onBackPressed() {
Toast.makeText(getActivity(), "按了返回键", Toast.LENGTH_SHORT).show();
return true;//由于这里return true 因此不会返回上一个页面,方便我截图
}
}
复制代码
alter为AlertDialog类型对象,注意要在alter.show()语句前加入
alert.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
复制代码
而后在AndroidManifest.xml中加入权限:
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"></uses-permission>
复制代码
优势:
缺点:
最明显的建立一个Activity须要配合建立多个接口类和实现类,每一个操做都须要经过接口回调的方式进行,虽然逻辑清晰代码,同时也形成了类的增多和代码量的加大。
官方文档明确指出,SharedPreferences不支持多线程,进程也是不安全的 若是想要实现线程安全需从新实现其接口,以下:
private static final class SharedPreferencesImpl implements SharedPreferences {
...
public String getString(String key, String defValue) {
synchronized (this) {
String v = (String)mMap.get(key);
return v != null ? v : defValue;
}
}
...
public final class EditorImpl implements Editor {
public Editor putString(String key, String value) {
synchronized (this) {
mModified.put(key, value);
return this;
}
}
...
}
}
复制代码
PathClassLoader和DexClassLoader都是继承与BaseDexClassLoader,BaseDexClassLoader继承与ClassLoader。DexClassLoader:可以加载自定义的jar/apk/dex PathClassLoader:只能加载系统中已经安装过的apk 因此Android系统默认的类加载器为PathClassLoader,而DexClassLoader能够像JVM的ClassLoader同样提供动态加载。