AssetManager的释放跟踪
AssetManager的创建很容易找到,就是new AssetManager的地方,但是在java里怎么调到的finalize()方法,进而调用到C++里的AssetManager的析构方法的呢?
屏蔽掉以下代码
boolean doPostDeleteLI(boolean delete) {
if (DEBUG_SD_INSTALL) Slog.i(TAG, "doPostDeleteLI() del=" + delete);
final List<String> allCodePaths = getAllCodePaths();
boolean mounted = PackageHelper.isContainerMounted(cid);
if (mounted) {
// Unmount first
if (PackageHelper.unMountSdDir(cid)) {
mounted = false;
}
}
if (!mounted && delete) {
cleanUpResourcesLI(allCodePaths);
}
return !mounted;
}
让系统不发生重启
观察AssetManager的创建和释放
创建
05-01 09:05:20.879: I/assetCpp(19300): Creating AssetManager 0x7f678ce000 #8
05-01 09:05:20.886: W/assetCpp(19300): In 0x7f678ce000 Asset zip path: /mnt/asec/com.example.myservice-2/base.apk
中间log
05-01 09:05:28.384: D/PackageManagerService(19300): =====pms L13193, if (!mounted && delete)
05-01 09:05:28.384: D/PackageManagerService(19300): =====pms L13198, return !mounted
05-01 09:05:28.384: D/PackageManagerService(19300): =====9 returnCode = deletePackageX(
05-01 09:05:28.384: D/PackageManagerService(19300): =====9 observer.onPackageDeleted, packageName=com.example.myservice, returnCode=1
释放
05-01 09:05:28.580: W/AssetManager(19300): AssetManager android.content.res.AssetManager@1cf2bc finalized with non-zero refs: 5
05-01 09:05:28.582: D/AssetManager(19300): =====CCAM incRefsLocked, this=android.content.res.AssetManager@e3f0648, iNum=0, mNumRefs=34
05-01 09:05:28.582: W/AssetManager(19300): Reference from here
05-01 09:05:28.592: W/assetJNI(19300): =====jni Destroying AssetManager 0x7f678ce000 for Java object 0x7f88447ef4
05-01 09:05:28.592: W/assetJNI(19300): =====jni L2178 delete am; am=0x7f678ce000
当不重启的时候,在deletePackageX后的一段时间内,AssetManager进行了释放
通过和发生重启时的log进行对比,发现正常释放的log里有
05-02 04:21:22.871: I/ActivityManager(18460): Force stopping com.UCMobile appid=10148 user=0: pkg removed
从ams里查找pkg removed
查找,得到是private final boolean forceStopPackageLocked(String packageName, int appId,
里进行的打印,增加堆栈打印log
进行查看,得到其调用堆栈
05-02 04:21:22.871: V/ActivityManager(18460): Broadcast: Intent { act=android.intent.action.PACKAGE_REMOVED dat=package:com.UCMobile flg=0x4000010 (has extras) } ordered=false userid=0 callerApp=null
05-02 04:21:22.871: D/ActivityManager(18460): =====ams, forceStopPackageLocked
05-02 04:21:22.871: D/ActivityManager(18460): ===== begin =====
05-02 04:21:22.871: D/ActivityManager(18460): com.android.server.am.ActivityManagerService.forceStopPackageLocked(ActivityManagerService.java:6531)
05-02 04:21:22.871: D/ActivityManager(18460): com.android.server.am.ActivityManagerService.broadcastIntentLocked(ActivityManagerService.java:18084)
05-02 04:21:22.871: D/ActivityManager(18460): com.android.server.am.ActivityManagerService.broadcastIntent(ActivityManagerService.java:18463)
05-02 04:21:22.871: D/ActivityManager(18460): com.android.server.pm.PackageManagerService$9.run(PackageManagerService.java:11070)
05-02 04:21:22.871: D/ActivityManager(18460): android.os.Handler.handleCallback(Handler.java:815)
05-02 04:21:22.871: D/ActivityManager(18460): android.os.Handler.dispatchMessage(Handler.java:104)
05-02 04:21:22.871: D/ActivityManager(18460): android.os.Looper.loop(Looper.java:207)
05-02 04:21:22.871: D/ActivityManager(18460): android.os.HandlerThread.run(HandlerThread.java:61)
05-02 04:21:22.871: D/ActivityManager(18460): com.android.server.ServiceThread.run(ServiceThread.java:46)
05-02 04:21:22.871: I/ActivityManager(18460): Force stopping com.UCMobile appid=10148 user=0: pkg removed
在boolean doPostDeleteLI(boolean delete)里进行sleep操作,来验证是否是由于vold里处理的太快,而导致AMS里来不及释放导致的重启,结果发现无论sleep多久,都会发生重启,而且log中没有
I/ActivityManager(18460): Force stopping com.UCMobile appid=10148 user=0: pkg removed
根据调用堆栈里的
05-02 04:21:22.871: D/ActivityManager(18460): com.android.server.pm.PackageManagerService$9.run(PackageManagerService.java:11070)
找到
pms
11070
am.broadcastIntent(null, intent, null, finishedReceiver,
0, null, null, null, android.app.AppOpsManager.OP_NONE,
null, finishedReceiver != null, false, id);
在pms的方法sendPackageBroadcast里
// Modified for ThemeManager
final void sendPackageBroadcast(final String action, final String pkg,
final String intentCategory, final Bundle extras, final String targetPkg,
final IIntentReceiver finishedReceiver, final int[] userIds) {
mHandler.post(new Runnable() {
@Override
public void run() {
也就是说,sendPackageBroadcast没有及时执行,一直在等待。
注意到,是采用mHandler.post(new Runnable()的方式执行的,再查看卸载流程
public void deletePackage中
// Queue up an async operation since the package deletion may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
final int returnCode = deletePackageX(packageName, userId, flags);
if (observer != null) {
try {
observer.onPackageDeleted(packageName, returnCode, null);
} catch (RemoteException e) {
Log.i(TAG, "Observer no longer exists.");
} //end catch
} //end if
} //end run
});
使用了同一个mHandler,所以sendPackageBroadcast里的处理一直要等待deletePackageX操作完成才会开始执行,所以之前的sleep无效
查看L的代码,在final void sendPackageBroadcast(final String action, final String pkg里没有使用mHandler.post(new Runnable()去执行,
屏蔽掉mHandler.post(new Runnable()后,可以正常卸载SD卡里的应用了
sendPackageBroadcast是在deletePackageX方法里的
if (res) {
info.sendBroadcast(true, systemUpdate, removedForAllUsers);
调用的
接着查看AMS里做了怎样的操作进行了释放
6. forceStopPackageLocked
查看AMS中的 private final boolean forceStopPackageLocked(String packageName, int appId,
方法
……
if (doit) {
if (purgeCache && packageName != null) {
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
ac.removePackage(packageName);
}
}
if (mBooted) {
mStackSupervisor.resumeTopActivitiesLocked();
mStackSupervisor.scheduleIdleLocked();
}
}
return didSomething;
}
点击一个应用的时候,会加载其对应的base.apk,其调用堆栈为
05-02 04:21:12.863: D/AssetManager(18460): android.content.res.AssetManager.addAssetPath(AssetManager.java:653)
05-02 04:21:12.863: D/AssetManager(18460): android.app.ResourcesManager.getTopLevelResources(ResourcesManager.java:221)
05-02 04:21:12.863: D/AssetManager(18460): android.app.ActivityThread.getTopLevelResources(ActivityThread.java:1854)
05-02 04:21:12.863: D/AssetManager(18460): android.app.LoadedApk.getResources(LoadedApk.java:558)
05-02 04:21:12.863: D/AssetManager(18460):
Resources resources = packageInfo.getResources(mainThread);
android.app.ContextImpl.<init>(ContextImpl.java:1884)
05-02 04:21:12.863: D/AssetManager(18460): android.app.ContextImpl.createPackageContextAsUser(ContextImpl.java:1733)
05-02 04:21:12.863: D/AssetManager(18460): android.app.ContextImpl.createPackageContextAsUser(ContextImpl.java:1718)
05-02 04:21:12.863: D/AssetManager(18460): com.android.server.AttributeCache.get(AttributeCache.java:114)
05-02 04:21:12.863: D/AssetManager(18460):
AttributeCache.Entry ent = AttributeCache.instance().get(packageName, com.android.server.am.ActivityRecord.<init>(ActivityRecord.java:564)
05-02 04:21:12.863: D/AssetManager(18460):
ActivityRecord r = new ActivityRecord(
com.android.server.am.ActivityStackSupervisor.startActivityLocked(ActivityStackSupervisor.java:1763)
05-02 04:21:12.863: D/AssetManager(18460): com.android.server.am.ActivityStackSupervisor.startActivityMayWait(ActivityStackSupervisor.java:1153)
05-02 04:21:12.863: D/AssetManager(18460): com.android.server.am.ActivityManagerService.startActivityAsUser(ActivityManagerService.java:4271)
05-02 04:21:12.863: D/AssetManager(18460): com.android.server.am.ActivityManagerService.startActivity(ActivityManagerService.java:4258)
05-02 04:21:12.863: D/AssetManager(18460): android.app.ActivityManagerNative.onTransact(ActivityManagerNative.java:168)
05-02 04:21:12.863: D/AssetManager(18460): com.android.server.am.ActivityManagerService.onTransact(ActivityManagerService.java:2703)
AttributeCache
查看
AttributeCache
public Entry get(String packageName, int resId, int[] styleable, int userId) {
synchronized (this) {
Package pkg = mPackages.get(packageName);
HashMap<int[], Entry> map = null;
Entry ent = null;
if (pkg != null) {
map = pkg.mMap.get(resId);
if (map != null) {
ent = map.get(styleable);
if (ent != null) {
return ent;
}
}
} else {
Context context;
try {
context = mContext.createPackageContextAsUser(packageName, 0,
new UserHandle(userId));
if (context == null) {
return null;
}
} catch (PackageManager.NameNotFoundException e) {
return null;
}
pkg = new Package(context);
mPackages.put(packageName, pkg);
}
if (map == null) {
map = new HashMap<int[], Entry>();
pkg.mMap.put(resId, map);
}
try {
ent = new Entry(pkg.context,
pkg.context.obtainStyledAttributes(resId, styleable));
map.put(styleable, ent);
} catch (Resources.NotFoundException e) {
return null;
}
return ent;
}
}
}
AssetManager的创建可以追溯到这里,
private final boolean forceStopPackageLocked(String packageName, int appId,
里起释放作用的是
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
ac.removePackage(packageName);
}
AttributeCache
public void removePackage(String packageName) {
synchronized (this) {
mPackages.remove(packageName);
}
}
private final WeakHashMap<String, Package> mPackages =
new WeakHashMap<String, Package>();
7. 另外一种解决尝试
AttributeCache使用了一个单例
把
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
ac.removePackage(packageName);
}
添加到PMS里的
boolean doPostDeleteLI(boolean delete) {
if (DEBUG_SD_INSTALL) Slog.i(TAG, "doPostDeleteLI() del=" + delete);
final List<String> allCodePaths = getAllCodePaths();
boolean mounted = PackageHelper.isContainerMounted(cid);
if (mounted) {
// Unmount first
if (PackageHelper.unMountSdDir(cid)) {
mounted = false;
}
}
if (!mounted && delete) {
cleanUpResourcesLI(allCodePaths);
}
return !mounted;
}
int pos = cid.lastIndexOf("-");
if (pos > 0)
{
String packageName = cid.substring(0, pos);
AttributeCache ac = AttributeCache.instance();
if (ac != null) {
Slog.d(TAG, "=====927, if (ac != null), ac.removePackage(packageName), " + packageName);
ac.removePackage(packageName);
}
//System.out.println("newString=" + newString);
}
else
{
Slog.d(TAG, "=====927, not find -");
}
}
进行验证,这样方法是可行的。
优化
使用INSTALL_PACKAGE_SUFFIX代替"-"
// Suffix used during package installation when copying/moving
// package apks to install directory.
private static final String INSTALL_PACKAGE_SUFFIX = "-";