안 드 로 이 드 성능 최적화 시작 최적화
인터넷 에 서 는 8 초 법칙 이라는 말 이 유행 하고 있다.사용자 가 한 페이지 를 열 고 8 초 동안 열 리 지 않 으 면 사용 자 는 대개 포기 하고 한 사용자 의 유실 을 의미한다.여기 서 시동 최적화 의 중요성 을 알 수 있다.
2.시작 하 는 분류
2.1 콜 드 시동
먼저 냉 가동 의 흐름 도 를 살 펴 보 겠 습 니 다.
그림 에서 알 수 있 듯 이 앱 이 시작 되 는 과정 은 Activity Manager Proxy 는 IPC 를 통 해 AMS(Activity Manager Service)를 호출 하고 AMS 는 IPC 를 통 해 앱 프로 세 스 를 시작 하 며 ApplicationThread 는 반 사 를 통 해 애플 리 케 이 션 을 만 들 고 연결 하 며 마지막 으로 Activity Thread 를 통 해 activity 의 생명 주 기 를 제어 한다.관련 페이지 의 수명 주기 에서 ViewRootImpl 을 통 해 view 를 실현 합 니 다.응용 프로그램의 시작 을 완료 합 니 다.
2.2 열 가동
열 시작 속도 가 가장 빠 릅 니 다.프로 세 스 가 배경 에서 프론트 로 전환 하 는 과정 입 니 다.
2.3 온도 가동
온도 시작 은 페이지 의 수명 주 기 를 한 번 만 다시 걸 을 수 있 지만 프로 세 스 에 대해 서 는 애플 리 케 이 션 이 다시 만 들 지 않 습 니 다.
3.방향 최적화
위 에서 작 동 하 는 몇 가지 방식 을 소개 한 것 을 보면 우 리 는 작 동 최적화 에 대해 기본적으로 냉 작 동 만 최적화 하면 된다 는 것 을 알 수 있다.그러나 콜 드 가동 의 시작 절차 에서 시스템 이 많이 만들어 져 서 우 리 는 조작 할 방법 이 없다.우리 가 할 수 있 는 일 은 바로 application 의 생명 주기 와 activity 의 생명 주기 라 는 부분 입 니 다.작 동 최적화 는 흔히 이 두 부분 에서 시작 합 니 다.
4.시작 시간의 측정 방식
4.1 adb 명령 방식 사용(오프라인 사용 이 편리 함)
adb shell am start-W 가방 명/가방 명+클래스 명
This Time:마지막 activity 의 시작 시간
TotalTime:모든 activity 의 시작 시간
WaitTime:AMS 가 activity 를 시작 하 는 데 걸 리 는 총 시간
여 기 는 제 가 메 인 화면 에 직접 들 어 갔 기 때문에 중간 에 Splash Activity 가 없습니다.모든 This Time 과 Total Time 의 시간 은 같 습 니 다.
장점:오프라인 에서 사용 하기에 편리 하고 오프라인 에서 사용 하기에 적합 한 제품 과 경쟁 품 을 얻 는 시간 을 비교 합 니 다.
단점:온라인 으로 가 져 갈 수 없고 얻 는 시간 은 대략적인 시간 이 라 고 할 수 있 을 뿐 엄밀 하지 않다.
4.2 수 동 타 점 방식
System.currentTimeMillis()를 통 해 타임 스탬프 를 찍 습 니 다.
단점:분명 합 니 다.코드 에 대한 침입 성 이 매우 큽 니 다.모든 작업 을 하 는 데 걸 리 는 시간 이 라면 코드 가 징 그 러 워 보 입 니 다.
5,우아 한 획득 방법 시간 소모
5.1 AOP Aspect Oriented Programming 절단면 프로 그래 밍
AOP:사전 컴 파일 방식 과 런 타임 동적 프 록 시 를 통 해 프로그램 기능 을 통합 적 으로 유지 하 는 기술 입 니 다.그의 핵심 사상 은 응용 프로그램의 업무 논리 처리 부분 을 유 니 버 설 서비스 부분 인'횡 절 관심 사'와 분리 하 는 것 이다.
OOP:패키지,계승,다 중 등 개념 을 도입 하여 대상 차원 구 조 를 구축 하고 개발 자 들 이 수직 관 계 를 정의 할 수 있 지만 수평적 관계 에 적합 하지 않 습 니 다.
AOP 는 OOP 의 보완 과 보완 이 라 고 할 수 있다.
5.2 aspectj 의 사용
AspectJ 는 절단면 프로 그래 밍 을 위 한 프레임 워 크 로 자바 의 확장 과 호 환 자바 입 니 다.AspectJ 는 AOP 문법 을 정 의 했 습 니 다.자바 바이트 인 코딩 규범 을 지 키 는 Class 파일 을 만 드 는 전문 컴 파일 러 가 있 습 니 다.
프로젝트 의 루트 디 렉 터 리 에 build.gradle 의존 도 를 추가 합 니 다:
dependencies {
classpath 'com.android.tools.build:gradle:3.5.2'
classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
app 의 build.gradle 에 의존 도 를 추가 합 니 다.
apply plugin: 'android-aspectjx'
dependencies 에 추가
implementation 'org.aspectj:aspectjrt:1.9.4'
그리고 클래스 를 만 듭 니 다.
package com.noahedu.myapplication.aspectj;
import android.util.Log;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class MyApplicationAspectj {
@Around("call(* com.noahedu.myapplication.MyApplication.**(..))")
public void getTime(ProceedingJoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
String name = signature.getName();
long time = System.currentTimeMillis();
try {
joinPoint.proceed();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
Log.e("MyApplicationAspectj " ,(name + " cost " + (System.currentTimeMillis() - time)));
}
}
이렇게 하면 우리 가 실행 할 때 logcat 에서 application 의 onCreate 방법 에서 모든 호출 방법 을 출력 하 는 데 시간 이 걸 립 니 다.2020-07-10 14:22:27.151 1619-1619/? E/MyApplicationAspectj: taskOne cost 150
2020-07-10 14:22:29.203 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskTwo cost 2052
2020-07-10 14:22:29.554 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskThrid cost 351
2020-07-10 14:22:30.556 1619-1619/com.noahedu.myapplication E/MyApplicationAspectj: taskFour cost 1001
이렇게 하면 우 리 는 애플 리 케 이 션 의 어떠한 코드 도 건 드 리 지 않 았 고 각 방법의 시간 을 얻 을 수 있 으 며 코드 에 거의 침입 하지 않 았 다.
6.최 적 화 된 도구 선택 시작
6.1 traceview
TraceView 는 Android SDK 에 내 장 된 도구 입 니 다.그 는 trace 파일 을 불 러 와 해당 코드 의 실행 시간,횟수 와 호출 스 택 을 도형 화 된 형식 으로 보 여 줌 으로 써 우리 가 분석 할 수 있 습 니 다.
Debug.startMethodTracing("MyApplication");
//TODO
Debug.stopMethodTracing();
프로젝트 를 실행 하면 SD 카드 에서 해당 하 는 trace 파일 을 찾 을 수 있 습 니 다.Android studio 라면 오른쪽 아래 에서 직접 찾 을 수 있 습 니 다.DeviceFileExporer-->sdcard-->Android-->data-->files--->자신의 프로젝트 의 패키지 이름
그리고 더 블 클릭 하면 파일 을 볼 수 있 습 니 다.
장점:간단 하고 도형 형식 으로 실 행 된 시간,스 택 호출 등 을 보 여 줍 니 다.
단점:우리 가 최적화 하 는 방향 에 영향 을 줄 수 있 습 니 다.도형 화 전시 이자 CPU 자원 을 차지 하기 때문에 얻 는 시간 이 실제 보다 큽 니 다.
7.시동
위 에서 임 무 를 수행 하 는 시간 을 얻 는 방식 과 도 구 를 몇 개 더 소개 했다.그러면 우리 가 어떤 방법 을 알 게 되 었 을 때 우 리 는 어떻게 처리 해 야 합 니까?
package com.noahedu.myapplication;
import android.app.Application;
import android.os.Debug;
import android.util.Log;
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
Debug.startMethodTracing("MyApplication");
taskOne();
taskTwo();
taskThrid();
taskFour();
Debug.stopMethodTracing();
}
public void taskOne(){
try {
Thread.sleep(150);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void taskTwo(){
try {
Thread.sleep(2050);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void taskThrid(){
try {
Thread.sleep(350);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public void taskFour(){
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
현재 애플 리 케 이 션 의 onCreate 방법 중 몇 가지 작업 이 있 습 니 다.각각 시간 이 다 릅 니 다.많은 학생 들 이'비동기 처리'라 고 말 할 수 있 습 니 다.스 레 드 를 열 고 스 레 드 탱크 에 넣 거나 Intent Service 를 만들어 서 실행 합 니 다.그러면 저희 가 몇 가지 생각 을 해 봐 야 될 것 같 아 요.첫째:비동기 처리 입 니 다.한 페이지 에 SDK 를 사용 해 야 하 는데 초기 화 되 지 않 았 습 니까?
둘째:taskTwo 에 taskOne 의 반환 값 이 필요 하 다 면?taskOne 이 taskTwo 전에 실 행 될 것 이 라 고 어떻게 보장 합 니까?
셋째:스 레 드 를 열 고 몇 개의 스 레 드 를 열 까요?많 으 면 자원 낭 비 를 초래 하고 자원 이 적 으 면 합 리 적 인 이용 도 없다.
저 는 개인 적 으로 시작 최적화 에 대해 application 에서 onCreate()에서 초기 화 작업 을 해 야 한다 고 생각 합 니 다.우 리 는 먼저 이 작업 들 에 대해 우선 순위 구분 을 해 야 합 니 다.우선 순위 가 높 은 작업 에 대해 우 리 는 우선 처리 할 수 있 습 니 다.우선 순위 가 낮은 것 에 대해 우 리 는 적당 한 지연 으로 로드 할 수 있 습 니 다.
그 다음 에 많은 학생 들 이 우선 순위 가 낮은 임 무 를 지연 로 딩 하 는 것 을 좋아 합 니 다.예 를 들 어 new Handler().postDelayed().이런 것 은 매우 바람 직 하지 않다 고 생각 합 니 다.만약 에 postDelayed 에 놓 인 임 무 는 2s 가 걸 리 고 1s 가 지연 되 어 처리 된다 면 2s 임 무 를 수행 하 는 과정 에서 사용자 가 조작 하 는 것 이 매우 카드 가 되 지 않 습 니까?분명 합 니 다.이것 은 지표 가 근본 을 다스 리 지 않 는 것 이다.
7.1 시동기 의 사상
위 에서 말 한 몇 개의 통 증 점 에 대해 어떻게 위의 몇 개의 통 증 점 을 처리 하고 코드 의 유지 가능성 을 확보 할 수 있 습 니까?다시 말 하면 신인 이 전체 과정 을 이해 하지 않 고 바로 일 을 할 수 있다 는 것 일 까?그럼 시동 이 왔 습 니 다.
시동기 핵심 사상:CPU 다 핵 을 충분히 이용 하여 작업 순 서 를 자동 으로 정리 합 니 다.
7.2 시동기 의 원리
1.퀘 스 트 는 모두 Task 대상 으로 봉 하여 집합 에 전송 합 니 다.
2.모든 임무 의존 관계 에 따라 방향 무 환 도 를 형성 한 다음 에 토폴로지 순 서 를 통 해 임무 의 집행 절 차 를 배열 한다.
3.Count Downlatch 를 통 해 어떤 임무 가 수행 되 었 는 지 여 부 를 제어 하고 다음 단 계 를 진행 합 니 다.
4.스 레 드 탱크 가 핵심 스 레 드 를 만 드 는 수량 은 핸드폰 의 핵 수량 에 의 해 결정 된다.
7.3 시동기 사용 방식
7.4 시동기 핵심 코드
작업 정렬 진행
package com.noahedu.launchertool.launchstarter.sort;
import com.noahedu.launchertool.launchstarter.task.Task;
import com.noahedu.launchertool.launchstarter.utils.DispatcherLog;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import androidx.annotation.NonNull;
import androidx.collection.ArraySet;
public class TaskSortUtil {
private static List<Task> sNewTasksHigh = new ArrayList<>();// Task
/**
*
*
* @return
*/
public static synchronized List<Task> getSortResult(List<Task> originTasks,
List<Class<? extends Task>> clsLaunchTasks) {
long makeTime = System.currentTimeMillis();
Set<Integer> dependSet = new ArraySet<>();
Graph graph = new Graph(originTasks.size());
for (int i = 0; i < originTasks.size(); i++) {
Task task = originTasks.get(i);
if (task.isSend() || task.dependsOn() == null || task.dependsOn().size() == 0) {
continue;
}
for (Class cls : task.dependsOn()) {
int indexOfDepend = getIndexOfTask(originTasks, clsLaunchTasks, cls);
if (indexOfDepend < 0) {
throw new IllegalStateException(task.getClass().getSimpleName() +
" depends on " + cls.getSimpleName() + " can not be found in task list ");
}
dependSet.add(indexOfDepend);
graph.addEdge(indexOfDepend, i);
}
}
List<Integer> indexList = graph.topologicalSort();
List<Task> newTasksAll = getResultTasks(originTasks, dependSet, indexList);
DispatcherLog.i("task analyse cost makeTime " + (System.currentTimeMillis() - makeTime));
printAllTaskName(newTasksAll);
return newTasksAll;
}
@NonNull
private static List<Task> getResultTasks(List<Task> originTasks,
Set<Integer> dependSet, List<Integer> indexList) {
List<Task> newTasksAll = new ArrayList<>(originTasks.size());
List<Task> newTasksDepended = new ArrayList<>();//
List<Task> newTasksWithOutDepend = new ArrayList<>();//
List<Task> newTasksRunAsSoon = new ArrayList<>();// , ( )
for (int index : indexList) {
if (dependSet.contains(index)) {
newTasksDepended.add(originTasks.get(index));
} else {
Task task = originTasks.get(index);
if (task.needRunAsSoon()) {
newTasksRunAsSoon.add(task);
} else {
newTasksWithOutDepend.add(task);
}
}
}
// : ――――》 ――――》 ――――》
sNewTasksHigh.addAll(newTasksDepended);
sNewTasksHigh.addAll(newTasksRunAsSoon);
newTasksAll.addAll(sNewTasksHigh);
newTasksAll.addAll(newTasksWithOutDepend);
return newTasksAll;
}
private static void printAllTaskName(List<Task> newTasksAll) {
if (true) {
return;
}
for (Task task : newTasksAll) {
DispatcherLog.i(task.getClass().getSimpleName());
}
}
public static List<Task> getTasksHigh() {
return sNewTasksHigh;
}
/**
* index
*
* @param originTasks
* @return
*/
private static int getIndexOfTask(List<Task> originTasks,
List<Class<? extends Task>> clsLaunchTasks, Class cls) {
int index = clsLaunchTasks.indexOf(cls);
if (index >= 0) {
return index;
}
//
final int size = originTasks.size();
for (int i = 0; i < size; i++) {
if (cls.getSimpleName().equals(originTasks.get(i).getClass().getSimpleName())) {
return i;
}
}
return index;
}
}
작업 코드 실행
package com.noahedu.launchertool.launchstarter.task;
import android.os.Looper;
import android.os.Process;
import com.noahedu.launchertool.launchstarter.TaskDispatcher;
import com.noahedu.launchertool.launchstarter.stat.TaskStat;
import com.noahedu.launchertool.launchstarter.utils.DispatcherLog;
/**
*
*/
public class DispatchRunnable implements Runnable {
private Task mTask;
private TaskDispatcher mTaskDispatcher;
public DispatchRunnable(Task task) {
this.mTask = task;
}
public DispatchRunnable(Task task,TaskDispatcher dispatcher) {
this.mTask = task;
this.mTaskDispatcher = dispatcher;
}
@Override
public void run() {
DispatcherLog.i(mTask.getClass().getSimpleName()
+ " begin run" + " Situation " + TaskStat.getCurrentSituation());
Process.setThreadPriority(mTask.priority());
long startTime = System.currentTimeMillis();
mTask.setWaiting(true);
mTask.waitToSatisfy();
long waitTime = System.currentTimeMillis() - startTime;
startTime = System.currentTimeMillis();
// Task
mTask.setRunning(true);
mTask.run();
// Task
Runnable tailRunnable = mTask.getTailRunnable();
if (tailRunnable != null) {
tailRunnable.run();
}
if (!mTask.needCall() || !mTask.runOnMainThread()) {
printTaskLog(startTime, waitTime);
TaskStat.markTaskDone();
mTask.setFinished(true);
if(mTaskDispatcher != null){
mTaskDispatcher.satisfyChildren(mTask);
mTaskDispatcher.markTaskDone(mTask);
}
DispatcherLog.i(mTask.getClass().getSimpleName() + " finish");
}
}
/**
* Task
*
* @param startTime
* @param waitTime
*/
private void printTaskLog(long startTime, long waitTime) {
long runTime = System.currentTimeMillis() - startTime;
if (DispatcherLog.isDebug()) {
DispatcherLog.i(mTask.getClass().getSimpleName() + " wait " + waitTime + " run "
+ runTime + " isMain " + (Looper.getMainLooper() == Looper.myLooper())
+ " needWait " + (mTask.needWait() || (Looper.getMainLooper() == Looper.myLooper()))
+ " ThreadId " + Thread.currentThread().getId()
+ " ThreadName " + Thread.currentThread().getName()
+ " Situation " + TaskStat.getCurrentSituation()
);
}
}
}
기본 핵심 코드 는 위 에 있 는 이 몇 개 입 니 다.완전한 코드 는 뒤의 demo 에서 제 시 됩 니 다.8.기타 최적화 방안
8.1 지연 작업 분할 초기 화
IdleHandler 기능 을 사용 하여 남 은 실행(우선 순위 가 높 지 않 고 초기 화 를 서 두 르 지 않 는 제3자 SDK 에 적합)
IdleHandler:IdleHandler 는 성능 을 향상 시 키 는 데 사용 할 수 있 습 니 다.주로 현재 스 레 드 메시지 대기 열 이 비어 있 을 때 일 을 하고 싶 은 경우(예 를 들 어 UI 스 레 드 가 표 시 된 후에 스 레 드 가 비어 있 으 면 다른 내용 을 미리 준비 할 수 있 습 니 다)에 사용 되 지만 시간 소모 작업 을 하지 않 는 것 이 좋 습 니 다.쉽게 말 하면 looper 대상 이 시간 이 있 을 때 IdleHandler 의 임 무 를 수행 하 는 것 이다.
앞에서 말 했 듯 이 애플 리 케 이 션 의 작업 에 우선 순 위 를 나 누 면 우선 순위 가 낮은 작업 이 있 으 면 애플 리 케 이 션 의 onCreate 에서 초기 화 하지 않 아 도 되 지 않 을까요?activity 에 넣 어서 진행 할 수 있 나 요?activity 에서 가능 하 다 면 activity 의 그 단계 가 어 울 릴 까요?사실 onCreate()에 서 는 onResume()이 모두 가능 합 니 다.물론 view 의 getView TreeObserver().addOnPreDraw Listener 를 사용 하여 감청 할 수 있 습 니 다.이 이 벤트 는 보기 가 그 려 질 때 이 방법 을 되 돌려 줍 니 다.초기 화 할 SDK 를 리 셋 하 는 방법 으로 IdleHandler 에 확대 할 수 있 습 니 다.
8.2 SP 를 미리 불 러 오기 전에 multidex 에 불 러 올 수 있 습 니 다.이 단계 의 CPU 를 이용 하여
Shared Preferences 는 키 값 으로 데 이 터 를 저장 합 니 다.메모리 에 한꺼번에 불 러 오기 때문에 그 단계 의 CPU 가 상대 적 으로 비어 있 는 것 을 고려 할 수 있 습 니 다.multidex 이전에 불 러 올 수 있다 면 이 단계 의 CPU 를 충분히 이용 하여 진행 합 니 다.
demo 주소:[https://github.com/343661629/startOptimization]
이상 은 안 드 로 이 드 성능 최적화 의 시작 최적화 에 대한 상세 한 내용 입 니 다.안 드 로 이 드 성능 최적화 의 시작 최적화 에 관 한 자 료 는 우리 의 다른 관련 글 을 주목 하 세 요!
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Bitrise에서 배포 어플리케이션 설정 테스트하기이 글은 Bitrise 광고 달력의 23일째 글입니다. 자체 또는 당사 등에서 Bitrise 구축 서비스를 사용합니다. 그나저나 며칠 전 Bitrise User Group Meetup #3에서 아래 슬라이드를 발표했...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.