本文为楼主原创,转载请表明出处:http://blog.youkuaiyun.com/suma_sun/article/details/52740147
前段时间公司的一个H5项目查出一个bug,奇葩的八阿哥!
第一次安装的app进入到WebView界面后,HOME键返回桌面,再从桌面点击app进入该应用,展示的界面居然不是最后展示的WebView界面,而是启动界面!
这个Bug是测试提出来的,web端和我手机都没有测出来,很奇葩,借了测试的手机我也复现不出来…拿去给测试她就能复现…真是神了!!!
没办法开始排查问题吧(网上根本找不到答案,毕竟这个情况不好描述,也发了知乎,但是木有人回)。
一开始以为是webview的设置问题。结果注释掉设置,她还是能复现。好吧问题不在这。修改了启动模式,singleTask然并卵…还是这样。差点怀疑人生了…
把全部生命周期都打印了,明明最后一个界面都没有onDestroy,为什么桌面重启会从引导界面开始启动…
简单的demo:
1.StartActivity界面点击按钮跳转到MainActivity。
2.短按Home键触发了onStop。
3.从桌面点击app图标进入app,MainActivity并没有onDestroy然而还是从StartActivity开始启动。
很奇怪,写法上完全感觉不出和以前写的app有啥区别,为什么会出现这么奇葩的问题。埋头苦思,无意间想起了一个关键——测试的安装app方式!为什么同一个apk在我手上就触发不了,测试就可以!为了方便我都是通过adb安装的,而测试不是!测试都是直接用手机浏览器从邮箱里下我得apk,然后使用手机自带的安装器安装app!
好吧这时我才反应过来测试说的第一次安装才会有这个现象原来是这么回事!使用系统安装的app才会触发这个现象,后面又发现了不用系统安装器安装不会触发,而用系统安装器安装的也不是百分百触发。最后定位出来了!是点击了系统安装器安装成功后的打开按钮打开app才会这样!
删掉应用,打开log(no fileters) 在点击apk前清一下Log!
10-05 08:43:33.465 2274-2483/system_process I/ActivityManager: START u0 {act=android.intent.action.VIEW dat=file:///storage/emulated/0/Download/app-debug.apk typ=application/vnd.android.package-archive flg=0x17000000 cmp=com.android.packageinstaller/.PackageInstallerActivity} from uid 10019 on display 0
ActivityManager告诉我们启动的是这个com.android.packageinstaller/.PackageInstallerActivity
这就是系统安装器具体实现类。PackageInstallerActivity这个类是在源码中的,下载源码需要用到git这里就不介绍了,自己找吧。直接用google搜索PackageInstallerActivity也可以看到源码,当然肯定没有下载到本地方便。(google还是很靠谱的第一条就是源码了,不像某度)
// Buttons to indicate user acceptance
private Button mOk;
private Button mCancel;
目测这个mOk就是目标了。
接着找InstallAppProgress这个类,这个应该就是安装的时候有进度条的那个界面了。
public void onClick(View v) {
if(v == mDoneButton) {
if (mAppInfo.packageName != null) {
Log.i(TAG, "Finished installing "+mAppInfo.packageName);
}
finish();
} else if(v == mLaunchButton) {
startActivity(mLaunchIntent);
finish();
}
}
直接找到点击事件处理。mDoneButton肯定是安装完那个完成按钮了,那另一个就是我们的目标了!关键就在mLaunchIntent里面,接着找Intent初始化的地方。
if (msg.arg1 == PackageManager.INSTALL_SUCCEEDED) {
mLaunchButton.setVisibility(View.VISIBLE);
centerTextDrawable.setLevel(0);
centerTextLabel = R.string.install_done;
// Enable or disable launch button
mLaunchIntent = getPackageManager().getLaunchIntentForPackage(
mAppInfo.packageName);
boolean enabled = false;
if(mLaunchIntent != null) {
List<ResolveInfo> list = getPackageManager().
queryIntentActivities(mLaunchIntent, 0);
if (list != null && list.size() > 0) {
enabled = true;
}
}
if (enabled) {
mLaunchButton.setOnClickListener(InstallAppProgress.this);
} else {
mLaunchButton.setEnabled(false);
}
}
关键代码:
mLaunchIntent =getPackageManager().getLaunchIntentForPackage(
mAppInfo.packageName);
通过包名获取要启动的应用的启动界面Intent。
看不出什么问题,那么应该是Intent的参数问题了。
I/ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10000000 pkg=com.suma.testinstaller cmp=com.suma.testinstaller/.StartActivity} from uid 10046 on display 0
这个是系统安装器打开的intent信息。
I/ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.suma.testinstaller/.StartActivity (has extras)} from uid 10039 on display 0
这个是桌面打开的intent信息。
I/ActivityManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.suma.testinstaller/.StartActivity (has extras)} from uid 10039 on display 0
这个是返回桌面后从桌面点击app图标启动app的intent信息(系统安装器启动或者桌面启动都一样)。
flg=0x10000000 启动新的一个task。
flg=0x10200000 如果已经存在直接使用,否则创建一个新的task。
只要是桌面启动都是0x10200000,而且不会有问题,那么问题就不在flg上。
很明显,在系统安装器中启动的程序和桌面启动的程序uid不一样(每个应用有一个独立uid,uid相同才可以共享数据),uid不一样的情况下是无法共享数据的,所以在桌面启动的时候会重新启动首界面。
既然得出该bug是在framework层,那就不是我application层可以处理的了。为此我特意测试了下QQ,一模一样的问题。