技术点:
- 传说中的黑白屏问题优化
- 黑白屏在商业APP中的价值
- App启动流程
- 如何使用Traceview工具对优化定位处理
1 系统的启动
- 打开电源,引导芯片代码加载引导程序BootLoader到RAM中去执行
- BootLoader把操作系统拉起来
- Linux内核启动开始系统设置,找到一个init.rc文件启动初始化进程
- init进程初始化和启动属性服务,之后开启Zygote进程
- Zygote开始创建JVM并注册JNI方法,开启SystemServer
- 启动Binder线程池和SystemServiceManager,并启动各种服务
- AMS启动Launcher
2 Launcher启动
- 通过adb shell dumpsys activity activities查看当前的activity名
先后台将所有app关闭,然后打开一个app。我的app中首页是MainActivity。
Running activities (most recent first):
TaskRecord{4145f710 #270 A com.hongx.hxlsn01 U 0}
Run #2: ActivityRecord{4102f8d0 u0 com.hongx.hxlsn01/.MainActivity}
TaskRecord{4170c6d8 #2 A com.huawei.android.launcher U 0}
Run #1: ActivityRecord{4170a3f8 u0 com.huawei.android.launcher/.Launcher}
TaskRecord{41e3ffe8 #157 I com.android.settings/.Settings$UsbSettingsActivity U 0}
Run #0: ActivityRecord{4155e5c8 u0 com.android.settings/.UsbSettings}
mResumedActivity: ActivityRecord{4102f8d0 u0 com.hongx.hxlsn01/.MainActivity}
mFocusedActivity: ActivityRecord{4102f8d0 u0 com.hongx.hxlsn01/.MainActivity}
mLastPausedActivity: ActivityRecord{4170a3f8 u0 com.huawei.android.launcher/.Launcher}
Running activities 表示正在运行的activity。
mLastPausedActivity 表示已经暂停的activity 为Launcher。
再去按下home键,dumpsys activity activities 后查看当前的activity名
Running activities (most recent first):
TaskRecord{4170c6d8 #2 A com.huawei.android.launcher U 0}
Run #2: ActivityRecord{4170a3f8 u0 com.huawei.android.launcher/.Launcher}
TaskRecord{4145f710 #270 A com.hongx.hxlsn01 U 0}
Run #1: ActivityRecord{4102f8d0 u0 com.hongx.hxlsn01/.MainActivity}
TaskRecord{41e3ffe8 #157 I com.android.settings/.Settings$UsbSettingsActivity U 0}
Run #0: ActivityRecord{4155e5c8 u0 com.android.settings/.UsbSettings}
mResumedActivity: ActivityRecord{4170a3f8 u0 com.huawei.android.launcher/.Launcher}
mFocusedActivity: ActivityRecord{4170a3f8 u0 com.huawei.android.launcher/.Launcher}
mLastPausedActivity: ActivityRecord{4102f8d0 u0 com.hongx.hxlsn01/.MainActivity}
从输出中可以看出,正在运行的activity为Launcher,已暂停的activity变成了MainActivity。
Launcher 概述
系统启动的最后一步是启动一个应用程序用来显示系统中已经安装的应用程序,这个应用程序就叫作 Launcher。Launcher 在启动过程中会请求 PackageManagerService 返回系统中已经安装的应用程序的信息,并将这些信息封装成一个快捷图标列表显示在系统屏幕上,这样用户可以通过点击这些快捷图标来启动相应的应用程序。
3 application的启动
E:\tools\android-src\android-6.0.1_r1\packages\apps\
Launcher2\src\com\android\launcher2\Launcher.java
当我们用手点击一个图标时,就到了这个类public final class Launcher extends Activity
执行onClick(View view)方法,会把这个应用的相关信息传入
先获取一个intent —> startActivitySafely(v, intent, tag) --> startActivity(v, intent, tag); --> startActivity(intent);
startActivity(intent)会开一个APP进程
ActivityThread.java做为入口 , 用attach开启app ,再加载application和activity
thread.attach(false);—>mgr.attachApplication(mAppThread)
会通过远端进程去回调
private void handleBindApplication(AppBindData data)
Application app = data.info.makeApplication(创建Application对象
mInstrumentation.callApplicationOnCreate(app);---->
app.onCreate();
4 黑白屏问题
res/values/styles.xml文件
白屏 <style name="AppTheme" parent="Theme.AppCompat.Light">
黑屏 <style name="AppTheme">(在以前的老版本上有效,现在的版本默认使用透明处理了)
找到一个父类name="Platform.AppCompat.Light"中定义了
<item name="android:windowBackground">用来控制黑白屏
解决办法:
1.在自己的<style name="AppTheme" parent="Theme.AppCompat.Light">中加入windowsbackground
2.设置windowbackground为透明的 <item name="android:windowIsTranslucent">true</item>
但这2种方法会有个问题,所有的activity启动都会显示
3.单独做成一个主题
<style name="AppTheme.Launcher">
<item name="android:windowBackground">@drawable/bg</item>
</style>
<style name="AppTheme.Launcher1">
<item name="android:windowBackground">@drawable/bg</item>
</style>
<style name="AppTheme.Launcher2">
<item name="android:windowBackground">@drawable/bg</item>
</style>
再在功能清单中的单独activity下设置
<activity
android:theme="@style/AppTheme.Launcher"
然后在程序中使用setTheme(R.style.AppTheme);
让APP中所有的activity还是使用以前的样式,这样做就只有启动时才使用自己的样式
QQ中的用法
<item name="android:windowDisablePreview">true</item>
<item name="android:windowBackground">@null</item>
5 启动时间的查看
-
4.4版本以后Logcat 输入Displayed筛选系统日志 不过滤信息No Filters
-
4.4以前 adb shell am start -W com.lqr.wechat/com.lqr.wechat.activity.SplashActivity
adb shell am start -W com.lqr.wechat/com.lqr.wechat.activity.SplashActivity
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=com.lqr.wechat/.activity.SplashActivity }
Status: ok
Activity: com.lqr.wechat/.activity.SplashActivity
ThisTime: 883
TotalTime: 883
WaitTime: 915
Complete
- ThisTime:最后一个启动的Activity的启动耗时;
- TotalTime:自己的所有Activity的启动耗时;
- WaitTime: ActivityManagerService启动App的Activity时的总时间(包括当前Activity的onPause()和自己Activity的启动)。
查看进程 adb shell ps
在输出中找到进程
u0_a533 25343 1275 4484104 190652 0 0 efg S com.lqr.wechat
u0_a533 25368 1275 4388180 97448 0 0 efg S com.lqr.wechat:core
u0_a533 25401 1275 4385168 93684 0 0 ebg S com.lqr.wechat:cosine
u0_a533 25436 1 4389532 41984 0 0 ebg S com.lqr.wechat:cosine
Am.java
AM路径\frameworks\base\cmds\am\src\com\android\commands\am
IActivityManager.WaitResult result = null;
int res;
final long startTime = SystemClock.uptimeMillis();
ActivityOptions options = null;
if (mStackId != INVALID_STACK_ID) {
options = ActivityOptions.makeBasic();
options.setLaunchStackId(mStackId);
}
if (mWaitOption) {
result = mAm.startActivityAndWait(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
res = result.result;
} else {
res = mAm.startActivityAsUser(null, null, intent, mimeType,
null, null, 0, mStartFlags, profilerInfo,
options != null ? options.toBundle() : null, mUserId);
}
final long endTime = SystemClock.uptimeMillis();
if (result == null) {
result = new IActivityManager.WaitResult();
result.who = intent.getComponent();
}
System.out.println("Status: " + (result.timeout ? "timeout" : "ok"));
if (result.who != null) {
System.out.println("Activity: " + result.who.flattenToShortString());
}
if (result.thisTime >= 0) {
System.out.println("ThisTime: " + result.thisTime);
}
if (result.totalTime >= 0) {
System.out.println("TotalTime: " + result.totalTime);
}
System.out.println("WaitTime: " + (endTime-startTime));
System.out.println("Complete");
在这个初始化时就已经进行了时间的计算:
frameworks\base\services\core\java\com\android\server\am\ActivityRecord.java 文件中计算
void windowsDrawnLocked() --->reportLaunchTimeLocked(SystemClock.uptimeMillis())
中完成时间的统计;
private void reportLaunchTimeLocked(final long curTime) {
final ActivityStack stack = task.stack;
if (stack == null) {
return;
}
final long thisTime = curTime - displayStartTime;
final long totalTime = stack.mLaunchStartTime != 0
? (curTime - stack.mLaunchStartTime) : thisTime;
if (SHOW_ACTIVITY_START_TIME) {
Trace.asyncTraceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER, "launching: " + packageName, 0);
EventLog.writeEvent(EventLogTags.AM_ACTIVITY_LAUNCH_TIME,
userId, System.identityHashCode(this), shortComponentName,
thisTime, totalTime);
StringBuilder sb = service.mStringBuilder;
sb.setLength(0);
sb.append("Displayed ");
sb.append(shortComponentName);
sb.append(": ");
TimeUtils.formatDuration(thisTime, sb);
if (thisTime != totalTime) {
sb.append(" (total ");
TimeUtils.formatDuration(totalTime, sb);
sb.append(")");
}
Log.i(TAG, sb.toString());
}
mStackSupervisor.reportActivityLaunchedLocked(false, this, thisTime, totalTime);
if (totalTime > 0) {
//service.mUsageStatsService.noteLaunchTime(realActivity, (int)totalTime);
}
displayStartTime = 0;
stack.mLaunchStartTime = 0;
}
6 Trace工具分析代码执行时间
Debug.startMethodTracing(filePath);
//中间为需要统计执行时间的代码
Debug.stopMethodTracing();
adb pull /storage/emulated/0/app1.trace 把文件拉出来分析
把pull到电脑上的文件拖到AS中就可以分析了
效果如下图:
优化方案:
1.开线程 没建handler 没操作UI 对异步要求不高
2.懒加载 用到的时候再初始化,如网络,数据库操作
public class App extends Application {
@Override
public void onCreate() {
super.onCreate();
File file = new File(Environment.getExternalStorageDirectory(), "app1.trace");
Log.i(TAG, "onCreate: " + file.getAbsolutePath());
//把分析结果存在一个文件
Debug.startMethodTracing(file.getAbsolutePath());
//对全局属性赋值
mContext = getApplicationContext();
mMainThread = Thread.currentThread();
mMainThreadId = android.os.Process.myTid();
mMainLooper = getMainLooper();
mHandler = new Handler();
//因为LQRUIKit中已经对ImageLoader进行过初始化了
//initImageLoader(getApplicationContext());
new Thread(){
@Override
public void run() {
//如果要用线程来节约了这些初始化的时间
//1.里面的API不能去创建handler
//2.不能有UI操作
//3.对异步要求不高
initNim();
initImagePicker();
initOkHttp();//可以懒加载
}
}.start();
initNim();
initImagePicker();
initOkHttp();//可以懒加载
NIMClient.init(this, loginInfo(), options());
Debug.stopMethodTracing();
}
...
}