android对单个应用所施加的内存限制,比如16M
常用的缓存策略:LruCache
与DiskLruCache
,其中LruCache
用作内存缓存,而DiskLruCache
用作磁盘缓存。
Bitmap
支持从文件系统,资源,输入流与字节数组中加载。通过BitmapFactory.options
来缩放图片,使用inSampleSize
为1时,采样后图片与原始图片一样大,为2时,宽高均为原来电放费1/2,像素数为原图1/4.占用内存大小为原图1/4.inSampleSize
取值应为2的指数,比如1,2,4,8,16,如果外界传来的是3,系统会选择2来代替,但并非在所有android版本都成立。如果采样率为3,那么缩放的图片大小就会小于所期望的大小,导致图片被拉伸而模糊。
获取采样率遵循的流程:
BitmapFactory.options的inJustDecodeBounds
参数设为true并加载图片BitmapFactory.options
中取出图片原始宽高信息,它们对应于outWidtht与outHeight
参数inSampleSize
BitmapFactory.options的inJustDecodeBounds
参数设为false并重新加载图片
public static Bitmap decodeSampleBitmapFromResource(Resources res,int resId,int reqWidth,int reqHeight){ BitmapFactory.Options options=new BitmapFactory.Options(); options.inJustDecodeBounds=true; BitmapFactory.decodeResource(res,resId,options); options.inSampleSize = calculateInSampleSize(options,reqWidth,reqHeight); options.inJustDecodeBounds = false; return BitmapFactory.decodeResource(res,resId,options); } public static int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){ int height=options.outHeight; int width = options.outWidth; int inSampleSize=1; if(height>reqHeight||width>reqWidth){ int halfHeight = height/2; int halfWidth = width/2; while ((halfHeight/inSampleSize)>=reqHeight&&(halfWidth/inSampleSize)>reqWidth){ inSampleSize *=2; } } return inSampleSize;}
从内存中加载图片比从存储设备加载图片要快,既提高了程序的效率又节省了用户流量。
LruCache
:当缓存快慢时,会淘汰近期最少使用的缓存。
LruCache
的使用其实比较简单,可以归结为以下要点:
LruCache
内部使用 LinkedHashMap
实现,所以 LruCache
保存的是键值对LruCache
本身对缓存项是强引用LruCache
的读写是线程安全的,内部加了 synchronized。也就是 put(K key, V value) 和 get(K key) 内部有 synchronized
4.key
和 value 不接受 null 。所以如果 get 到了 null ,那就说明是没有缓存5. Override sizeOf(K key, V value)
方法
Override entryRemoved(boolean evicted, K key, V oldValue, V newValue)
和 create(K key)
方法
使用LruCache建议采用support-v4兼容包LRUCache
,内部采用一个LinkedHashMap
以强引用方式存储外界缓存对象,提供get和put方法来完成缓存的获取与添加。
final Bitmap bitmap=null;
int maxMemory= (int) (Runtime.*getRuntime*().maxMemory()/102);
int cacheSize=maxMemory/8;
LruCache<String,Bitmap> lruCache=new LruCache<String, Bitmap>(cacheSize){
@Override
protected int sizeOf(String key, Bitmap value)
return bitmap.getRowBytes()*bitmap.getHeight()/1024;
}
};
DiskLruCache
实现存储设备缓存,即磁盘缓存,通过将缓存对象写入文件系统实现缓存效果。
DiskLruCache
缓存添加通过Editor
完成,Editor
表示一个缓存对象的编辑对象。DiskLruCache
不允许同时编辑一个缓存对象。之所以将url转换成key,因为图片url可能有特殊字符,一般采用url的md5
值作为key.通过Editor
的commit
方法来提交操作,通过Editor的abort
方法来回退整个操作。
DiskLruCache的get
方法得到一个Snapshot
对象,接着在通过Snapshot
得到文件输入流,自然就得到Bitmap对象了。为了避免加载图片导致OOM,通过BitmapFactory.options
加载缩放图片。一个优秀的图片加载框架ImageLoader具备:
在Android中,有一个限制,就是整个应用的方法数不能超过65536
,否则就会出现编译错误,并且程序无法安装到手机上。
Android中单个dex(它是Android系统可执行文件,包含应用程序全部指令与运行时数据)文件所能够包含的最大方法数为65536,这包含androidFrameWork,依赖的jar包,以及应用本身代码所有方法。
解决方法:
multidex
方案专门解决这个问题,通过将一个dex文
件拆分为多个dex避免单个dex文件方法数越界。使用crashHandler
来获取应用崩溃信息
UncaughtExceptionHandler
接口,重写uncaughtException
方法,在这个方法中获取异常信息,可以选择将异常信息存储到sd卡中,然后找合适的机会上传到服务器上,这样开发人员就可以分析用户的crash
场景并在以后版本中修复。**uncaughtException
方法,其中thread
为出现未捕获异常的线程,ex为未捕获的异常,有了这个ex,就可以获取到异常信息了。在代码中加入支持multidex的功能
manifest
文件中指定Application为MultiDexApplication
Application
继承自MultiDexApplication
Application的attachBaseContext
方法,这个方法比aplication的onCreate
要先执行。采用以上方法,如果越界,Gradle就会在apk中打包2个或多个dex文件。
产生的问题:
Dalvik linerAlloc
的bug,导致multidex
无法在android4.0以前手机上运行。目前极少遇到。宿主指的是普通apk,而插件一般是指经过处理的dex或apk,在主流的插件化框架中多采用经过特殊处理的apk来作为插件,都需要用到代理activity概念。
使用插件化需要解决三个基础性的问题:
getAssets()
与getResources()
Activity
的生命周期管理。反射方式与接口方式。过多使用反射方式会带来性能开销ClassLoader
的管理。为了更好对多插件进行支持,合理管理各个插件的DexClassLoader
,这样同一个插件可以采用同一个ClassLoader
去加载类,避免多个ClassLoader加载同一个类所造成的类型转换错误<include>
标签<merge>
标签:一般和<include>
一起使用,减少布局的层级使用merge
来组织子元素可以减少布局的层级。例如我们在复用一个含有多个子控件的布局时,肯定需要一个ViewGroup
来管理,例如这样 :
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ImageView
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:scaleType="center"
android:src="@drawable/golden_gate" />
<TextView
android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="20dip" android:layout_gravity="center_horizontal|bottom" android:padding="12dip"
android:background="#AA000000"
android:textColor="#ffffffff"
android:text="Golden Gate" />
</FrameLayout>
将该布局通过include
引入时就会多引入了一个FrameLayout
层级,此时结构如下 :
使用merge
标签就会消除上图中蓝色的FrameLayout
层级。示例如下 :
<merge xmlns:android="http://schemas.android.com/apk/res/android"> <ImageView
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:scaleType="center"
android:src="@drawable/golden_gate" />
<TextView
android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="20dip" android:layout_gravity="center_horizontal|bottom" android:padding="12dip"
android:background="#AA000000"
android:textColor="#ffffffff"
android:text="Golden Gate" />
</merge>
效果图如下 :
ViewStub
,它是非常轻量级且宽高都是0,按需加载布局文件,在布局中不可见,提高了程序的初始化性能。
((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
或者
View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();
指的是View的onDraw方法要避免大量操作
onDraw
可能会被频繁调用,就会一瞬间产生大量临时对象,不仅占用过多内存,还会导致系统频繁gc
,降低程序执行效率animator.cancel()
来停止动画避免在主线程中做太多事情
Activity
如果5秒之内无法响应屏幕触摸事件或者键盘输入事件就会出现ANR
Broadcast
如果10秒未执行完操作就会ANRService
是20秒当一个进程中出现ANR,系统会在/data/anr
目录下创建一个文件traces.txt
。通过这个文件就可以定位出ANR原因。
ANR对话框,是在AMS收到SHOW_NOT_RESPONDING_UI_MSG
消息之后弹出的。这个消息分别来自于:
ContentProvider
超时;input
事件分派超时;在input事件分派超时的时候,有两种情况不会弹框,分别是:
debug
时;kill
掉子进程)viewHolder
并避免在getView
中执行耗时操作通过BitmapFactory.Options
根据需要对图片进行采样
使用线程池
static final
修饰SparseArray
和Pair
等,它们具有更好的性能