Andoid性能优化之进程和线程

1、进程

默认情况下,一个应用中的所有组件都在相同的 Linux 进程中运行,且大多数应用都不应改变这一点。 但是,如果需要控制某个组件所属的进程,可执行如下操作:通过 android:process 属性可设置组件(Android四大组件)在那个进程中运行。


当内存不足时,Android 可能会在某一个时刻关闭某个进程,进程内的应用组件也随之被关闭。当这些组件需要再次运行时,系统会为其重启进程。 进程间通信:可使用 bindService() Or ContentProvider


2、线程

应用启动时,Android 会为其创建一个名称为 Main(主线程)的执行线程。该进程负责界面绘制 & 响应用户操作。 当该线程被阻塞,用户会感到卡顿,甚至ANR。此外,Android 界面工具包并非线程安全工具包。所以开发中不得通过其他线程操作UI。 所以,Android 的单线程模式必须遵守两条规则:

1、不要阻塞 UI 线程。阻塞超过5秒,显示ANR对话框。

2、不要在 UI 线程之外访问 Android UI 工具包


PS:代码上其实可以绕开主线程修改UI,但是强烈不建议这么做,这是一个错误的实践。


可能被多个线程使用的方法,必须实现为线程安全方法。例如 ContentProvider 的 query()、insert()、delete()、update() 和 getType() 方法。


线程优先级

在应用中创建和管理线程时,Google 强烈建议设置线程的优先级。通过使用 android.os.Process 类的 setThreadPriority() 来明确调整线程优先级。

			
		private static class MyThread extends Thread {

			private final int tid;
	
			public MyThread() {
				super();
				tid = Process.myTid();
				//设置 调用该方法的线程 的优先级
				Process.setThreadPriority(THREAD_PRIORITY_BACKGROUND);
			}
	
			@Override
			public void run() {
				while (true) {
					try {
						Thread.sleep(600);
						System.out.println("doing" + Process.getThreadPriority(tid));
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
	
		}
			
		

默认情况下,系统会将线程的优先级设置为与生成它的线程具有相同的优先级。主线程的优先级默认为 THREAD_PRIORITY_VIDEO = -10 。 在主线程中创建的线程,如果没有设置过优先级,其优先级和主线程一致都为-10。


绝大多数情况下子线程的优先级绝不能高于主线程。子线程优先级过高,可能干扰主线程,导致应用掉帧。太低,可能导致异步任务(例如图片加载)达不到所需的速度。 如需了解更多内容,请参阅 android.os.Process 类源码。


3、工作线程(子线程)

为什么要使用子线程:保证主线程的流畅渲染和快速响应用户输入。把冗长的任务放在子线程。只有保证每秒 60 帧,用户才不会感到卡顿。


根据开发经验,执行时间超过200ms的方法就要考虑放到子线程执行。因为实际开发中,总是大量方法一起调用,导致CPU处理时间快速累加。 影响界面的流畅渲染(一帧渲染时间超过16毫秒)。应用甚至出现明显的卡顿。


Android 视图对象不是线程安全的。无论是创建、使用还是销毁界面对象,应用都应在主线程上完成。这意味着开发者应制定允许多个子线程将工作结果传回主线程的通道。 需要注意的是,子线程不能包含对UI(View、Fragment、Activity)的引用,极易引起内存泄漏。

  1. 子线程显示引用View导致的内存泄漏
    • 工作线程完成之前,view已从视图中移除,子线程的显示引用导致View内存泄漏。系统会在对象引用消失后回收该对象。
    • 还是上面的情况,如果View包含对Activity 的引用,则该Activity需要等到子线程工作完毕才能被回收
    • 在工作线程的执行过程中,如果屏幕旋转,这种情况可能导致内存中有两个Activity对象
  2. 子线程隐式引用View导致的内存泄漏
    • 匿名内部类,默认持有外部类的引用。当内部类为工作线程时,可能导致Activity的销毁出现延迟,进而给内存带来更多压力。 此问题的直接解决方法是将内部类定义为静态类,或在其自己的文件中定义,从而移除隐式引用。或者 通过 WeakReference 包裹引用的外部类对象

尽管在软件层面,我们的代码可以创建数百个线程,但这会导致性能问题。CPU实际上只能并行处理少量线程,线程过多会导致系统调度问题。 还需要考虑到线程不是免费的,它们会占用内存。每个线程至少需要占用 64k 内存。为了更好的使用线程,我们可以在应用中创建一个线程池, 或者复用(第三库 或 系统的)线程池,从而提高性能。