• Android优化指南


    Android系统中GC内存泄漏的原因

    主动回收内存System.gc();、getruntime.runtime.gc

    导致内存泄漏主要的原因是,申请了内存空间而忘记了释放。如果程序中存在对无用对象的引用,那么这些对象就会驻留内存,消耗内存,因为无法让垃圾回收器GC验证这些对象是否不再需要。如果存在对象的引用,这个对象就被定义为"有效的活动",同时不会被释放。要确定对象所占内存将被回收,我们就要务必确认该对象不再会被使用。典型的做法就是把对象数据成员设为null或者从集合中移除该对象。但当局部变量不需要时,不需明显的设为null,因为一个方法执行完毕时,这些引用会自动被清理。

     什么是GC

    GC垃圾收集器,它让创建的对象不需要像c/c++那样delete、free掉,GC的时间系统自身决定,时间不可预测或者说调用System.gc()的时候。 对超出作用域的对象或引用置为空的对象进行清理,删除不使用的对象,腾出内存空间。

    Java带垃圾回收的机制,为什么还会内存泄露呢?

    举个例子 当你堆里某个对象没有被引用时,然后再过一段时间,垃圾回收机制才会回收,那么
    while(true){
    String str=new String("ni hao ni hao ");
    }
    一直循环创建 String对象。。。你觉得堆不会溢出嘛。。。
    垃圾回收 要有2个条件
    1 该对象没有被引用
    2 过了一段时间

    Java 内存泄露的根本原因就是 保存了不可能再被访问的变量类型的引用,回收不确定性

     

    内存溢出和内存泄漏

    内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。memory leak会最终会导致out of memory!

    内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是产生溢出。 

    从用户使用程序的角度来看,内存泄漏本身不会产生什么危害,作为一般的用户,根本感觉不到内存泄漏的存在。真正有危害的是内存泄漏的堆积,这会最终消耗尽系统所有的内存。从这个角度来说,一次性内存泄漏并没有什么危害,因为它不会堆积,而隐式内存泄漏危害性则非常大,因为较之于常发性和偶发性内存泄漏它更难被检测到。

     

     Java中有内存泄露吗?举一些例子?

    1.查询数据库没有关闭游标 2. 构造Adapter时,没有使用缓存的 convertView 3. Bitmap对象不再使用时调用recycle()释放内存 4. 无用时没有释放对象的引用 5. 在Activity中使用非静态的内部类,并开启一个长时间运行的线程,因为内部类持有Activity的引用,会导致Activity本来可以被gc时却长期得不到回收 6.使用Handler处理消息前,Activity通过例如finish()退出,导致内存泄漏 7.动态注册广播在Activity销毁前没有unregisterReceive

     

    内存的优化

    • 回收已经使用的资源,比如游标cursor 、I/O、Bitmap(close并且引用置为null)
    • 合理的使用缓存,比如图片是很耗内存的,使用lru缓存图片和压缩
    • 合理设置变量的作用范围
    • 节制的使用服务,后台任务运行完,即使它不执行任何操作,服务也会一直运行,这些是十分消耗内存的,可以用intentservice
    • 当界面不可见时释放内存,在activity的onTrimMemory方法里与ui的相关资源,在onstop里释放与组件相关的资源
    • 合理的使用多进程,如果后台任务和前台界面是相互独立在,可以在组件标签下写process,这样这个组建就在另一个进程里了。而服务的话更倾向于开启自己所依赖的进程,而那个进程可能很多东西都不需要,比如ui
    • 使用线程池、对象池

    • Bitmap对象在不使用时,应该先调用recycle()释放内存,然后才它设置为null。

     

    说说线程池

    好处

    避免线程的创建和销毁所带来的性能得开销

    能有效控制线程池的最大并发数,避免了大量线程间抢占资源而导致的阻塞现象

    能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能

    由于不需要每次处理复杂逻辑耗时操作,比如加载网络并不需要都开启一个新的线程,可以用线程池处理,把线程存起来,用的时候在取出来,

    在onDestory里去销毁线程,这样就会节省内存

    //AsyncTask就是Handler和线程池的封装
    //自定义线程池
    public class ThreadManager {
    	private ThreadManager() {
    	}
    	private static ThreadManager instance = new ThreadManager();
    	private ThreadPoolProxy longPool;
    	private ThreadPoolProxy shortPool;
    	public static ThreadManager getInstance() {
    		return instance;
    	}
    	// 联网比较耗时
    	// 开启线程数一般是cpu的核数*2+1
    	public synchronized ThreadPoolProxy createLongPool() {
    		if (longPool == null) {
    			longPool = new ThreadPoolProxy(5, 5, 5000L);
    		}
    		return longPool;
    	}
    	// 操作本地文件
    	public synchronized ThreadPoolProxy createShortPool() {
    		if(shortPool==null){
    			shortPool = new ThreadPoolProxy(3, 3, 5000L);
    		}
    		return shortPool;
    	}
    	public class ThreadPoolProxy {
    		private ThreadPoolExecutor pool;
    		private int corePoolSize;
    		private int maximumPoolSize;
    		private long time;
    		public ThreadPoolProxy(int corePoolSize, int maximumPoolSize, long time) {
    			this.corePoolSize = corePoolSize;
    			this.maximumPoolSize = maximumPoolSize;
    			this.time = time;
    		}
    		/**
    		 * 执行任务
    		 * @param runnable
    		 */
    		public void execute(Runnable runnable) {
    			if (pool == null) {
    				// 创建线程池
    				/*
    				 * 1. 线程池里面管理多少个线程2. 如果排队满了, 额外的开的线程数3. 如果线程池没有要执行的任务 存活多久4.
    				 * 时间的单位 5 如果 线程池里管理的线程都已经用了,剩下的任务 临时存到LinkedBlockingQueue对象中 排队
    				 */
    				pool = new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
    						time, TimeUnit.MILLISECONDS,
    						new LinkedBlockingQueue<Runnable>(10));
    			}
    			pool.execute(runnable); // 调用线程池 执行异步任务
    		}
    		/**
    		 * 取消任务
    		 * @param runnable
    		 */
    		public void cancel(Runnable runnable) {
    			if (pool != null && !pool.isShutdown() && !pool.isTerminated()) {
    				pool.remove(runnable); // 取消异步任务
    			}
    		}
    	}
    }
    

      

    三种静态
    • 静态内部类:尽量不要用一个生命周期长于Activity的对象来持有Activity的引用。声明handler为static类,这样内部类就不再持有外部类的引用了,就不会阻塞Activity的释放。在Activity中尽量避免使用生命周期不受控制的非静态类型的内部类,可以使用静态类型的内部类加上弱引用的方式实现。

    • 静态变量:不要直接或者间接引用Activity、Service等。这会使用Activity以及它所引用的所有对象无法释放,然后,用户操作时间一长,内存就会狂升。  

    • 静态引用:应该避免 static 成员变量引用资源耗费过多的实例,比如 Context。尽量使用 getApplicationContext:如果为了满足需求下必须使用 Context 的话:Context 尽量使用 Application Context,因为 Application 的 Context 的生命周期比较长,引用它不会出现内存泄露的问题,而不是activity的context,单例。可以通过调用 Context.getApplicationContext() or Activity.getApplication()来获得
     

    Handler内存泄漏
    Handler作为内部类存在于Activity中,但是Handler生命周期与Activity生命周期往往并不是相同的,比如当Handler对象有Message在排队,则无法释放,进而导致本该释放的Acitivity也没有办法进行回收。
    解决办法:声明handler为static类,这样内部类就不再持有外部类的引用了,就不会阻塞Activity的释放

    System.gc()

    我们可以调用System.gc方法,建议虚拟机进行垃圾回收工作(注意,是建议,但虚拟机会不会这样干,我们也无法预知!)

    下面来看一个例子来了解finalize()System.gc()的使用:

    public class TestGC {  
        public TestGC() {}  
          
        //当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。  
        protected void finalize() {  
            System.out.println("我已经被垃圾回收器回收了...");  
        }  
          
        public static void main(String [] args) {  
            TestGC gc = new TestGC();  
            gc = null;    
            // 建议虚拟机进行垃圾回收工作  
            System.gc();  
        }  
    }  

    如上面的例子所示,大家可以猜猜重写的finalize方法会不会执行?

    答案是:不一定

    因为无论是设置gc的引用为null还是调用System.gc()方法都只是"建议"垃圾回收器进行垃圾回收,但是最终所有权还在垃圾回收器手中,它会不会进行回收我们无法预知!

    AsynTask为什么要设计为只能够一次任务?

    最核心的还是线程安全问题,多个子线程同时运行,会产生状态不一致的问题。所以要务必保证只能够执行一次

     

    java中的soft reference

    StrongReference 是 Java 的默认引用实现, 它会尽可能长时间的存活于 JVM 内, 当没有任何对象指向它时 GC 执行后将会被回收,SoftReference 会尽可能长的保留引用直到 JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存

     

    内存溢出OOM解决方案?(解决方法)

    内存缓存的时候可能内存溢出,因为Android默认给每个app只分配16M的内存,,每个手机不一样,我的手机是3G内存,分配的内存是29m,通过这样可以获得

    int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  

    解决方法1:java中的引用(使用软引用)

        - 强引用 垃圾回收器不会回收, java默认引用都是强引用
        - 软引用 SoftReference   在内存不够时,垃圾回收器会考虑回收
        - 弱引用 WeakReference  在内存不够时,垃圾回收器会优先回收
        - 虚引用 PhantomReference  在内存不够时,垃圾回收器最优先回收

    注意: Android2.3+, 系统会优先将SoftReference的对象提前回收掉, 即使内存够用

    内存中使用LRUCache是最合适的。如果用HashMap来实现,不是不可以,但需要注意在合适的时候释放缓存。至于具体怎么释放,我没考虑过,但用软引用的问题在于,你很难控制缓存的大小,也就是说,只有等到你的内存快要撑爆,你的图片缓存才会被回收。是不是感觉傻傻的?

     解决方法2:LruCache 


        least recentlly use 最少最近使用算法

        会将内存控制在一定的大小内, 超出最大值时会自动回收, 这个最大值开发者自己定。他内部是是一个linkedhashmap以强引用的方式存储外界的缓存对象,提供了get,put方法来操作,当缓存满了,lru会移除较早使用的缓存对象,把新的添加进来。也可以自己remove

     解决方法3:图片压缩

    三级缓存

    • 先读取内存缓存, 因为优先加载, 速度最快,内存缓存没有再读取本地缓存, 次优先加载, 速度也快,本地没有再加载网络缓存, 速度慢,浪费流量在网络缓存中从网络下载图片,并且保存在本地和内存中,在下载的时候可以对图片进行压缩
    • 服务器端下载的图片是使用 Http的缓存机制,每次执行将本地图片的时间发送给服务器,如果俩次访问的时间间隔短,返回码是 304,会读取网络缓存(说明服务端的图片和本地的图片是相同的,直接使用本地保存的图片),如果返回码是 200,则开始下载新的图片并实现缓存。在从服务器获取到图片后,需要再在本地和内存中分别存一份,这样下次直接就可以从内存中直接获取了,这样就加快了显示的速度,提高了用户的体验。
    • 大量图片加载,当用户不停的滑动时,由于ui在主线程操作的,会出现卡顿,可以在滑动的时候停止加载(setOnscrollerListener),在getView方法里只有静止才加载图片
    InputStream inputStream = conn.getInputStream();
    				
    				//图片压缩处理
    				BitmapFactory.Options option = new BitmapFactory.Options();
    				option.inSampleSize = 2;//宽高都压缩为原来的二分之一, 此参数需要根据图片要展示的大小来确定
    				option.inPreferredConfig = Bitmap.Config.RGB_565;//设置图片格式
    				
    				Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, option);
    				return bitmap;
    ....

    本地保存时可以将名字用MD5加密保存

    // 将图片保存在本地,
    			bitmap.compress(CompressFormat.JPEG, 100,
    					new FileOutputStream(file));//100是质量
    //取
    Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
    						file));//decodeStream放的是输入输出流
    				return bitmap;
    
    
    Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(
    						file));//
    				return bitmap;

    内存缓存在保存的时候可以给图片分配内存

    private LruCache<String, Bitmap> mMemoryCache;
    	public MemoryCacheUtils() {
    		long maxMemory = Runtime.getRuntime().maxMemory() / 8;//主流都是分配16m的8/1
    		mMemoryCache = new LruCache<String, Bitmap>((int) maxMemory) {
    			@Override
    			protected int sizeOf(String key, Bitmap value) {
    				int byteCount = value.getRowBytes() * value.getHeight();// 获取图片占用内存大小
    				return byteCount;
    			}
    		};
    	}
    

      

    正常一张720x1080的图片在内存中占多少空间?怎么加载大图片?

    看他的渲染方式,RGB888啥的,占用的字节不同的。

    如果是一张的话压缩处理,大量图片的话用lru

    图片的总大小 = 图片的总像素 * 每个像素占用的大小

    加载大图片

    • 计算机把图片所有像素信息全部解析出来,保存至内存
    • Android保存图片像素信息,是用ARGB保存,所以每个像素占用4个字节,很容易内存溢出

    • 可以对图片的宽高和质量进行压缩

      首先对图片进行缩放

      • 获取屏幕宽高

          //设置缩放比例
          opts.inSampleSize = scale;
          //为图片申请内存
          opts.inJustDecodeBounds = false;
          Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
          iv.setImageBitmap(bm);
        
        
          int scale = 1;
          int scaleX = imageWidth / screenWidth;
          int scaleY = imageHeight / screenHeight;
          if(scaleX >= scaleY && scaleX > 1){
              scale = scaleX;
          }
          else if(scaleY > scaleX && scaleY > 1){
              scale = scaleY;
          }
          Options opts = new Options();
          //请求图片属性但不申请内存
          opts.inJustDecodeBounds = true;
          BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
          int imageWidth = opts.outWidth;
          int imageHeight = opts.outHeight;
          Display dp = getWindowManager().getDefaultDisplay();
          int screenWidth = dp.getWidth();
          int screenHeight = dp.getHeight();
      • 获取图片宽高

         
          //设置缩放比例
          opts.inSampleSize = scale;
          //为图片申请内存
          opts.inJustDecodeBounds = false;
          Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
          iv.setImageBitmap(bm);
        
        
          int scale = 1;
          int scaleX = imageWidth / screenWidth;
          int scaleY = imageHeight / screenHeight;
          if(scaleX >= scaleY && scaleX > 1){
              scale = scaleX;
          }
          else if(scaleY > scaleX && scaleY > 1){
              scale = scaleY;
          }
          Options opts = new Options();
          //请求图片属性但不申请内存
          opts.inJustDecodeBounds = true;
          BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
          int imageWidth = opts.outWidth;
          int imageHeight = opts.outHeight;
        
      • 图片的宽高除以屏幕宽高,算出宽和高的缩放比例,取较大值作为图片的缩放比例,且大于1才缩放

          //设置缩放比例
          opts.inSampleSize = scale;
          //为图片申请内存
          opts.inJustDecodeBounds = false;
          Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
          iv.setImageBitmap(bm);
        
        
          int scale = 1;
          int scaleX = imageWidth / screenWidth;
          int scaleY = imageHeight / screenHeight;
          if(scaleX >= scaleY && scaleX > 1){
              scale = scaleX;
          }
          else if(scaleY > scaleX && scaleY > 1){
              scale = scaleY;
          }
        

          

      2.然后按缩放比例加载图片

        //设置缩放比例
        opts.inSampleSize = scale;
        //为图片申请内存
        opts.inJustDecodeBounds = false;
        Bitmap bm = BitmapFactory.decodeFile("sdcard/dog.jpg", opts);
        iv.setImageBitmap(bm);
      

        

    如何在不失真的条件下显示一张超高清的图片或者长图
    • 针对这个问题,我自己一般用以下两种方法解决: 1、使用WebView来加载该图片; 2、使用MapView或者TileView来显示图片(类似地图的机制);

    subsampling-scale-image-view
     
     
    缩减APK包大小代码
    • 保持良好的编程习惯,不要重复或者不用的代码,谨慎添加libs,移除使用不到的libs。
    • 使用proguard混淆代码,它会对不用的代码做优化,并且混淆后也能够减少安装包的大小。
    • native code的部分,大多数情况下只需要支持armabi与x86的架构即可。如果非必须,可以考虑拿掉x86的部分。
    • 使用Lint工具查找没有使用到的资源。去除不使用的图片,String,XML等等。
    • assets目录下的资源请确保没有用不上的文件。
    • 生成APK的时候,aapt工具本身会对png做优化,但是在此之前还可以使用其他工具如tinypng对图片进行进一步的压缩预处理。
    • jpeg还是png,根据需要做选择,在某些时候jpeg可以减少图片的体积。
    • 对于9.png的图片,可拉伸区域尽量切小,另外可以通过使用9.png拉伸达到大图效果的时候尽量不要使用整张大图。
    • 有选择性的提供hdpi,xhdpi,xxhdpi的图片资源。建议优先提供xhdpi的图片,对于mdpi,ldpi与xxxhdpi根据需要提供有差异的部分即可。
    • 尽可能的重用已有的图片资源。例如对称的图片,只需要提供一张,另外一张图片可以通过代码旋转的方式实现。
    • 能用代码绘制实现的功能,尽量不要使用大量的图片。例如减少使用多张图片组成animate-list的AnimationDrawable,这种方式提供了多张图片很占空间

    ListView的优化

    • 复用convertview , 历史的view对象
    • 减少子孩子查询的次数 viewholder
    • 异步加载数据(把图片缓存)
    • 条目多时分页加载数据
    • 加载时显示进度条让用户等待

    • Item的布局层次结构尽量简单,避免布局太深或者不必要的重绘

    • 避免在 getView 方法中做耗时的操作:
      例如加载本地 Image 需要载入内存以及解析 Bitmap ,都是比较耗时的操作,如果用户快速滑动listview,会因为getview逻辑过于复杂耗时而造成滑动卡顿现象。用户滑动时候不要加载图片,待滑动完成再加载,可以使用这个第三方库glide

    • 应该尽量避免 static 成员变量引用资源耗费过多的实例,比如 Context。
    • 尽量使用 getApplicationContext:如果为了满足需求下必须使用 Context 的话:Context 尽量使用 Application Context,因为Application 的 Context 的生命周期比较长,引用它不会出现内存泄露的问题

    • 在一些场景中,ScollView内会包含多个ListView,可以把listview的高度写死固定下来。
      由于ScollView在快速滑动过程中需要大量计算每一个listview的高度,阻塞了UI线程导致卡顿现象出现,如果我们每一个item的高度都是均匀的,可以通过计算把listview的高度确定下来,避免卡顿现象出现

    • 使用 RecycleView 代替listview:
      每个item内容的变动,listview都需要去调用notifyDataSetChanged来更新全部的item,太浪费性能了。RecycleView可以实现当个item的局部刷新,并且引入了增加和删除的动态效果,在性能上和定制上都有很大的改善

    • ListView 中元素避免半透明:
      半透明绘制需要大量乘法计算,在滑动时不停重绘会造成大量的计算,在比较差的机子上会比较卡。 在设计上能不半透明就不不半透明。实在要弄就把在滑动的时候把半透明设置成不透明,滑动完再重新设置成半透明。

    • 尽量开启硬件加速:
      硬件加速提升巨大,避免使用一些不支持的函数导致含泪关闭某个地方的硬件加速。当然这一条不只是对 ListView。

    布局的优化

    • 尽量重用一个布局文件,使用include标签,多个相同的布局可以复用
    • 减少一个布局的不必要节点
    • 尽量使用view自身的参数,例如:Button,有一个可以把图绘制在左边的参数:android:drawableLeft
    • 使用< ViewStub />标签来加载一些不常用的布局;使用< merge />标签减少布局的嵌套层次

    ViewPager的优化

    • viewpager会默认加载左右俩个页面,有时候我们并不想看,会浪费用户的流量,可以在setOnPageChangeListener的onPageSelected的方法里选中哪个页面,初始化哪个页面
    • 由于viewpager会默认销毁第三页面,可以强制让viewpager加载所有的页面pagerView.setOffscreenPageLimit(pageCount);,但是如果页面多的话就不能这样干了
    • 可以定义一个集合将页面缓存起来,在destroyItem的时候保存起来,在instantiateItem读取集合,有就用,没有的话再创建,就像listview的convertView似的
    	class HomeAdapter extends PagerAdapter {
    		// 当前viewPager里面有多少个条目
    		LinkedList<ImageView> convertView=new LinkedList<ImageView>();
    		@Override
    		public int getCount() {
    			return	Integer.MAX_VALUE;
    		}
    		/* 判断返回的对象和 加载view对象的关系 */
    		@Override
    		public boolean isViewFromObject(View arg0, Object arg1) {
    			return arg0 == arg1;
    		}
    		@Override
    		public void destroyItem(ViewGroup container, int position, Object object) {
    			ImageView view=(ImageView) object;
    			convertView.add(view);// 把移除的对象 添加到缓存集合中
    			container.removeView(view);
    		}
    		@Override
    		public Object instantiateItem(ViewGroup container, int position) {
    			ImageView view;
    			if(convertView.size()>0){
    				view=convertView.remove(0);
    			}else{
    				view= new ImageView(UiUtils.getContext());
    			}
    			bitmapUtils.display(view, HttpHelper.URL + "image?name="
    					+ datas.get(index));
    			container.addView(view); // 加载的view对象
    			return view; // 返回的对象
    		}
    	}
    

      

    内存的优化

    •  回收已经使用的资源,比如游标cursor 、I/O、Bitmap(close并且引用置为null)
    •   合理的使用缓存,比如图片是很耗内存的,使用lru缓存图片和压缩
    • 合理设置变量的作用范围
    • 节制的使用服务,后台任务运行完,即使它不执行任何操作,服务也会一直运行,这些是十分消耗内存的,可以用intentservice
    • 当界面不可见时释放内存,在activity的onTrimMemory方法里与ui的相关资源,在onstop里释放与组件相关的资源
    • 合理的使用多进程,如果后台任务和前台界面是相互独立在,可以在组件标签下写process,这样这个组建就在另一个进程里了。而服务的话更倾向于开启自己所依赖的进城,而那个进程可能很多东西都不需要,比如ui
    • 使用线程池、对象池
    • Bitmap对象在不使用时,应该先调用recycle()释放内存,然后才它设置为null。

    代码优化

    这部分就是是细微的优化,但是细微多了也就内存节约了

    任何一个Java类,包括内部类、匿名类,都要占用大概500字节的内存空间。
    任何一个类的实例要消耗12-16字节的内存开支,因此频繁创建实例也是会一定程序上影响内存的,所以要避免创建不必要的对象

    1. 如果有一个需要拼接的字符串,那么可以优先考虑使用StringBuffer或者StringBuilder来进行拼接,而不是加号连接符,因为使用加号连接符会创建多余的对象,拼接的字符串越长,加号连接符的性能越低
    2. 尽量使用基本数据类来代替封装数据类型,int比Integer要更加高效,其它数据类型也是一样

    使用静态

    1. 使用枚举通常会比使用静态常量要消耗两倍以上的内存,在Android开发当中应当尽可能地不使用枚举。
    2. 如果你并不需要访问一个对象中的某些字段,只是想调用它的某个方法来去完成一项通用的功能,那么可以将这个方法设置成静态方法,这会让调用的速度提升15%-20%,同时也不用为了调用这个方法而去专门创建对象了,这样还满足了上面的一条原则。另外这也是一种好的编程习惯,因为我们可以放心地调用静态方法,而不用担心调用这个方法后是否会改变对象的状态(静态方法内无法访问非静态字段)

     对常量使用static final修饰符

    使用增强型for循环语法

    多使用系统封装好的API,比如:indexOf(),System.arraycopy()

      性能优化:尽量使用drawable对象保存图片而不是bitmap

                            drawable = Drawable.createFromStream(new URL(url).openStream(), "image.png");

  • 相关阅读:
    修改sharepoint列表样式
    Zend Server 安装与配置图文教程
    安装zendstudio和破解方法及配置svn
    C#索引器
    曝光尚德机构亲身经历请大家不要上当
    不支持PowerShell 2.0版本(don't support PowerShell version 2.0. )
    SharePoint Project Server List 列表CURD操作使用rest api接口
    解决启动SQL Server Management Studio 17时报Cannot find one of more components...的问题
    Project Server2016升级安装问题项目中心无法显示
    Project Server 2016 RestAPI调用测试
  • 原文地址:https://www.cnblogs.com/sixrain/p/5614025.html
Copyright © 2020-2023  润新知