算法_加密_优化Android进阶之路Android知识

Android 性能优化总结

2016-10-25  本文已影响593人  dooze

将从以下几个方面总结Android的应用性能优化

性能

内存


糟糕的用户体验

关于帧

帧速率

常见操作耗时

读写操作在性能差一些的机子上可能时间会有出入

往磁盘写内容的时候,会随着磁盘的剩余空间的较少而导致写速率不断减低

永远不要做阻塞UI线程的事情,用一个新的线程去做可能会影响UI体验的事情

四种可以异步的实现:

  1. Runnable
  2. Thread
  3. Future
  4. ExecutorService
new Thread(new Runnable() {
  @Override
  public void run() {
    // do some heavy work
  }
}).start();

new AsyncTask<URL, Integer, Integer>() {
  protected Long doInBackground(URL... urls) {
    final int count = urls.length;
      for ( int i = 0; i < count; i++ ) {
        Downloader.download(url);
        publishProgress(i);
    }
  return count;
  }
  protected void onProgressUpdate(Integer... progress) {
    setProgress(progress[0]);
  }
  protected void onPostExecute(Integer result) {
    showDialog(“Downloaded “ + result + “ files”);
  }
}
HandlerThread mHandlerThread = new HandlerThread("WorkerThread");
Handler handler = new Handler(mHandlerThread.getLooper()) {
  @Override
  public void handleMessage(Message msg) {
    switch (msg.what) {
      case JOB_1:
        // do job #1
      break;
      case JOB_2:
        // do job #2
      break;
    }
  }
};


handler.sendEmptyMessage(JOB_1);
handler.sendEmptyMessage(JOB_2);


handler.post(new Runnable() {
  @Override
  public void run() {
    // do more work
  }
});


@Override
protected void onDestroy() {
  mHandlerThread.quit();
  super.onDestroy();
}
    
new AsyncQueryHandler(getContentResolver()) {
  @Override
  protected void onQueryComplete(int token, Object cookie,
    Cursor cursor) {
      if (token == 0) {
        // get data from cursor
      }
    }
    }.startQuery(0, // token
        null, // cookie
        RawContacts.CONTENT_URI, null, // projection
        RawContacts.CONTACT_ID + "<?", // selection
        new String[] { "888" }, // selectionArgs
        RawContacts.DISPLAY_NAME_PRIMARY + " ASC" // orderby
        );
public class WorkerService extends IntentService {
  public WorkerService() {
    super("WorkerThread");
  }
  @Override
  protected void onHandleIntent(Intent intent) {
    String action = intent.getAction();
    if ("com.test.DO_JOB_1".equals(action)) {
        // do job #1
    }
  }
}



startService(new Intent("com.test.DO_JOB_1"));

UI线程性能总结

View Hierarchy

Tips:

I/O性能优化

String sql = “INSERT INTO table VALUES (?, ?)”;
SQLiteStatement stmt = mDatabase.compileStatement(sql);
DatabaseUtils.bindObjectToProgram(stmt, 1, 1);
DatabaseUtils.bindObjectToProgram(stmt, 2, 2);
stmt.execute();
stmt.close();

//或者使用 PreparaStatement
getContentResolver().registerContentObserver(uri, true,
  new ContentObserver(new Handler()) {
    @Override
    public void onChange(boolean selfChange) {
      mDirty = true;
    }
  });

  @Override
  protected void onResume() {
    super.onResume();
    if (mDirty) {
      // start query again
      mDirty = false;
    }
  }

滑动性能优化(List)

Memory

在系统级别,Android使用低内存驱动程序运行修改过的OOM Killer

包括:

Android中的低内存阈值(init.rc中)

# Define the memory thresholds at which the above process classes will
# be killed. These numbers are in pages (4k).

setprop ro.FOREGROUND_APP_MEM 2048
setprop ro.VISIBLE_APP_MEM 3072
setprop ro.PERCEPTIBLE_APP_MEM 4096
setprop ro.HEAVY_WEIGHT_APP_MEM 4096
setprop ro.SECONDARY_SERVER_MEM 6144
setprop ro.BACKUP_APP_MEM 6144
setprop ro.HOME_APP_MEM 6144
setprop ro.HIDDEN_APP_MEM 7168
setprop ro.EMPTY_APP_MEM 8192

OOM_ADJ基于重要性级别(init.rc中)

# Define the oom_adj values for the classes of processes that can be 
# killed by the kernel. These are used in ActivityManagerService. 

setprop ro.FOREGROUND_APP_ADJ 0 
setprop ro.VISIBLE_APP_ADJ 1 
setprop ro.PERCEPTIBLE_APP_ADJ 2 
setprop ro.HEAVY_WEIGHT_APP_ADJ 3 
setprop ro.SECONDARY_SERVER_ADJ 4 
setprop ro.BACKUP_APP_ADJ 5 
setprop ro.HOME_APP_ADJ 6 
setprop ro.HIDDEN_APP_MIN_ADJ 7 
setprop ro.EMPTY_APP_ADJ 15 

进程重要性级别

Low Memory 回调

Activity.onLowMemory()
Fragment.onLowMemory()
Activity.onSaveInstanceState(Bundle)
Service.onLowMemory()
ContentProvider.onLowMemory()

在应用程序级别,Android限制了多少内存可以分配给每个应用程序。

Android为每个应用程序定义了一个堆限制,并指示何时抛出OutOfMemoryError

Android Studio 中 Heap窗口中的相关术语

术语 解释
Heap limit 应用在Dalvik堆中的最大允许占用空间
Heap size 当前Dalvik堆的大小
Allocated 应用在Dalvik堆上分配的字节总数
Free Heap size – Allocated
% Used Allocated / Heap size * 100%
External allocation (3.0之前) Bitmap byte[]

ActivityManager.getMemoryClass() 可以查看当前应用Heap size limit

OOM 发生的情形

Heap size + external allocation + new allocation request >= Heap limit

Heap size + new allocation request >= Heap limit

new allocation request : 新的内存开辟请求大小

不代表进程内存使用的情形

查看内存使用情况

adb shell procrank -p

PID Vss Rss Pss Uss cmdline!
3156 80272K 80220K 59228K 57624K com.htc.launcher
1455 94540K 58728K 37488K 36060K system_server
9000 55224K 55200K 33900K 32412K com.roguso.plurk
6713 47912K 47880K 27719K 26788K tw.anddev.aplurk
1624 44804K 44760K 24954K 24200K android.process.acore
2081 44992K 44960K 23205K 21628K com.htc.android.mail
1604 41288K 41248K 22393K 21752K com.htc.android.htcime
1594 40912K 40844K 21588K 20284K com.htc.weatheridlescreen
1622 39904K 39872K 21297K 20696K com.android.phone

VSS(Virtual Set Size):进程可以访问的页面总数

RSS(Resident Set Size): RAM中进程可以访问的页总数

PSS(Proportion Set Size):进程在RAM中使用的页面总数,其中每个页面的大小是页面总数除以共享它的进程数

USS(Unique Set Size):进程可以访问的非共享页面的数量

Vss Rss Pss Uss ShCl ShDi PrCl PrDi Name
------- ------- ------- ------- ------- ------- ------- ------- 
4K 4K 0K 0K 4K 0K 0K 0K /system/bin/app_process
4K 4K 0K 0K 4K 0K 0K 0K /system/bin/app_process
13908K 13908K 11571K 11508K 2400K 0K 11508K 0K [heap]
0K 0K 0K 0K 0K 0K 0K 0K [heap]
4K 4K 4K 4K 0K 0K 4K 0K [heap]
36K 36K 0K 0K 0K 36K 0K 0K /dev/__properties__
.......

adb shell dumpsys meminfo <pid>

Applications Memory Usage (kB):
Uptime: 89133197 Realtime: 106110266

** MEMINFO in pid 11961 [com.htc.friendstream] **
                native dalvik other total limit bitmap nativeBmp
          size: 15032  8535   N/A   23567 32768 N/A    N/A
     allocated: 14565  5697   N/A   20262 N/A   4669   1918
          free: 162    2838   N/A   3000  N/A   N/A    N/A
         (Pss): 4105   2550   13952 20607 N/A   N/A    N/A
(shared dirty): 2440   1928   5532  9900  N/A   N/A    N/A
  (priv dirty): 4044   708    12716 17468 N/A   N/A    N/A

Objects
           Views: 0 ViewRoots: 0
     AppContexts: 0 Activities: 0
          Assets: 7 AssetManagers: 7
   Local Binders: 11 Proxy Binders: 15
Death Recipients: 1
 OpenSSL Sockets: 0!

Private Dirty = USS

无法分页到磁盘并且不与任何其他进程共享的进程内部RAM量

当进程消失时,系统可以使用的RAM

/dev/ashmem/dalvik-heap : 在Dalvik级别为堆分配的匿名页面

[heap], [anonymous] : 由malloc()在本机级别分配的匿名页面

/system/framework/*.odex (release build)

/data/dalvik-cache/*.odex (debug build) : 文件支持的mmap页面

Garbage collection(垃圾收集)

Memory leaks(内存泄露)

常见的因为Context 或着 Activity造成的内存泄露

  1. 在Activity中存在长期存在的指向非静态内部类实例对象的引用

    public class TestActivity extends Activity{
      static LeakyTest leaky = null;
      class LeakyTest{
        void doSoming(){
          //doing
        }
      }
      
      @Override
      protected void onCreate(Bundle saveInstanceStates){
        super.onCreate(saveInstanceStates);
        if(leaky==null)
          leaky = new LeakyTest();
        //....
      }
      //.....
    }
    
  2. 在Activity中有超出Activity生命周期且长期存活的线程

       new Thread(new Runnable(){
         @Override
         public void run(){
           //do long-live works
         }
       }).start();
    

有用的方法

例如得到的日志信息:

D/dalvikvm(9050):GC_CONCURRENT free 2049k, 65% free  3571k/9991k, external 4703k/5261k, paused 2ms+2ms

D/dalvikvm(9050): <u>GC_CONCURRENT</u> free 2049k, 65% free 3571k/9991k, external 4703k/5261k, paused 2ms+2ms

下划线处GC的原因:

GC_CONCURRENT

GC_FOR_MALLOC

GC_EXTERNAL_ALLOC

GC_HPROF_DUMP_HEAP

GC_EXPLICIT

D/dalvikvm(9050): GC_CONCURRENT <u>free 2049k</u>, 65% free 3571k/9991k, external 4703k/5261k, paused 2ms+2ms

下划线处GC的原因:

内存释放

D/dalvikvm(9050): GC_CONCURRENT free 2049k, <u>65% free 3571k/9991k</u>, external 4703k/5261k, paused 2ms+2ms

下划线处GC的原因:

内存释放

堆进行信息统计

D/dalvikvm(9050): GC_CONCURRENT free 2049k, 65% free 3571k/9991k, <u>external 4703k/5261k,</u> paused 2ms+2ms

下划线处GC的原因:

内存释放

堆进行信息统计

内部内存进行信息统计

D/dalvikvm(9050): GC_CONCURRENT free 2049k, 65% free 3571k/9991k, external 4703k/5261k, <u>paused 2ms+2ms</u>

下划线处GC的原因:

内存释放

堆进行信息统计

内部内存进行信息统计

时间暂停

强烈推荐郭神关于内存泄露分析的文章:

Android最佳性能实践(二)——分析内存的使用情况

其他优化建议

//缩放图片
BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, opts);
final int originalWidth = opts.outWidth;
final int originalHeight = opts.outHeight;
final int originalDim = Math.max(originalWidth, originalHeight);
opts = new BitmapFactory.Options();
opts.inSampleSize = 1;
while ( originalDim > MAX_IMAGE_DIM ) {
  opts.inSampleSize *= 2;
  originalDim /= 2;
}
return BitmapFactory.decodeFile(path, opts);
//对比之间的例子,这里改成了静态内部类
public class MainActivity extends Activity {
  static Leaky leak = null;
  static class Leaky {
    void doSomething() {
        //doing
    }
  }
  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (leak == null) {
      leak = new Leaky();
    }
  }
}
public class MainActivity extends Activity {
static Leaky leak = null;
static class Leaky {
private final Context mContext; //final修饰
  public Leaky(Context context) {
    super();
    mContext = context;
    doSomethingWithOuterInstance();
  }
  void doSomethingWithOuterInstance() {
    String text = mContext.getString(R.string.hello);
    System.out.println(text);
  }
}
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
  if (leak == null) {
      leak = new Leaky(this);
  }
}
}
public class MainActivity extends Activity {
static Leaky leak = null;
static class Leaky {
private final WeakReference<Context> mContext;//使用了弱引用
public Leaky(Context context) {
  super();
  mContext = new WeakReference<Context>(context);
  doSomethingWithOuterInstance();
}
void doSomethingWithOuterInstance() {
  Context context = mContext.get();
  if (context != null) {
    String text = context.getString(R.string.hello);
    System.out.println(text);
  }
  }
}
@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  if (leak == null) {
    leak = new Leaky(this);
  }
}
}

更多优化建议,请移步郭神博客

Android最佳性能实践

上一篇 下一篇

猜你喜欢

热点阅读