android 读取U盘apk文件信息,拔掉U盘导致进程被杀

来源:互联网 发布:汪峰 知乎 编辑:程序博客网 时间:2024/05/22 04:35
最近一直在搞appStore,之前一直都是从网络获取apk下载进行安装,现在车机要求需要支持从U盘安装apk,本以为简单很容易的事,却一不小心掉了一个坑里,爬了好久,主要是没很明显的报错,没遇到过的话真的很难发现错误。

一、首先来看下错误,以及造成的错误原因

这里写图片描述

由上图可知,当U盘拔掉之后,网易云音乐apk依旧被占用,从而系统发出了sighup,kill信号,导致当前进程被杀掉,也就是说我们读取apk信息时,资源未能及时释放。

二、解决思路,解决方案

既然是因为U盘拔掉后,资源未释放引起的,那解决的方案无非就是找到占用的地方,释放掉资源。然而你会发现你将所有可能引起的资源占用都处理置空,拔掉U盘时,将数据清空,还是会奔溃。具体原因以及解决思路可以看看这两篇博客

关于Usb直接拔出导致的进程崩溃问题
Android 开发:加载未安装apk图标-拔出U盘导致进程被杀的解决方案

    上述两篇博客已经讲的很详细了,引起原因以及解决方案,都提供了不同的思路,但是他们只是针对,获取apk的图标,可能他们的需求只是需要个图标而已,针对图标进行了操作,但是在我的应用中,不仅仅需要图标,还需要应用的版本,名称,包名等等信息,从而我进行了各种尝试,确实如果我只是获取图标,通过他们的方式确实不会再出现进程被杀的情况,但是当我将所有需要的其他信息,依旧会报错。

三、我的做法,以及解决思路

首先先上代码:

public static UninstallApkInfo cloneUninstallAPKInfo(Context ctx, String archiveFilePath) {        UninstallApkInfo uninstallApkInfo = new UninstallApkInfo();        Log.i("Apkinfo", "parse " + archiveFilePath);        PackageInfo pakinfo = null;        PackageManager pm = ctx.getPackageManager();        pakinfo = pm.getPackageArchiveInfo(archiveFilePath, PackageManager.GET_ACTIVITIES);        if (pakinfo != null) {            ApplicationInfo appinfo = pakinfo.applicationInfo;            appinfo.sourceDir = archiveFilePath;            appinfo.publicSourceDir = archiveFilePath;            uninstallApkInfo.setAppVersion(pakinfo.versionName);            //   uninstallApkInfo.setAppName(pm.getApplicationLabel(appinfo).toString());// 得到应用名 );            //  uninstallApkInfo.setAppIcon(appinfo.loadIcon(pm));//得到应用图标            uninstallApkInfo.setAppPkgName(appinfo.packageName);            uninstallApkInfo.setFileName(archiveFilePath);            uninstallApkInfo.setInstallState(ApkOperateManager.INSTALL_NONE);            AssetManager assetManager = new AssetManager();            assetManager.addAssetPath(archiveFilePath);            Resources resources = new Resources(assetManager, ctx.getResources().getDisplayMetrics(), ctx.getResources().getConfiguration());            uninstallApkInfo.setAppIcon(resources.getDrawable(appinfo.icon));            uninstallApkInfo.setAppName(resources.getString(appinfo.labelRes));            assetManager.close();            File file = new File(archiveFilePath);            uninstallApkInfo.setAppSize(file.length() / (1024 * 1024));            Log.i("UN", uninstallApkInfo.appName + uninstallApkInfo.appPkgName);        } else {            Log.w("Apkinfo", "get no pkginfo from " + archiveFilePath);        }        if (pakinfo == null)            return null;        else            return uninstallApkInfo;    }
以上代码是不会出现U盘被占用的情况,首先对比一下我获取appName跟appIcon,注释的是我之前会出错的写法,然后再看看我现在获取的方式,首先先看出错的写法,我们用到了PackageManager(pm)跟PackageInfo(pakinfo),而现在的获取方式只是通过PackageInfo,我们进源码可以看到PackageInfo只是一个javaBean实现了Parcelable接口,也就是说PackageInfo只是用来放置apk里面的信息数据的,并没有进行实际的文件操作,我的理解是,读取apk文件信息的操作应该可能是放在PackageManager里面进行的,当我们用PackageManager来获取图标或者应用名时,PackageManager因为被我们引用了从而无法回收释放资源,而我们只用到PackageInfo,PackageManager也就能成功释放掉了,也就不会再占用U盘文件了。(仅代表个人见解,如有不对的地方或者更深的理解请指教)

好了U盘占用问题得到了解决,现在又遇到了一个问题就是只通过PackageInfo并不能获取到图标跟应用名信息,可是我们就是需要啊,那该怎么办?从上面提到的第二遍博客得到了解决的方案,有兴趣的可以看那篇博客得到更详细的说明,PackageInfo里面的ApplicationInfo里面含有图标资源和应用名的资源id,然后再通过AssetManager 来获取apk的resources,从而通过资源id得到apk的图标和应用名,并且AssetManager提供了close方法,所以获取到之后及时关闭资源也就不会出现U盘占用的问题了.
AssetManager assetManager = new AssetManager();            assetManager.addAssetPath(archiveFilePath);            Resources resources = new Resources(assetManager, ctx.getResources().getDisplayMetrics(), ctx.getResources().getConfiguration());            uninstallApkInfo.setAppIcon(resources.getDrawable(appinfo.icon));            uninstallApkInfo.setAppName(resources.getString(appinfo.labelRes));            assetManager.close();
说明:AssetManager的所需要的几个方法都是隐藏的(@hide),我这边AssetManager能直接调用,是因为我们系统是自己定制的,已经让系统层那边将这些接口暴露了,如果你们系统是可以自己定制的,那么你可以采用跟我一样的方式让系统层暴露接口,否则你只能通过反射来调用这些接口,不会的话可以查看上面推荐的第二遍博客里面的第三个方案。
阅读全文
0 0
原创粉丝点击