自己动手写一个APK安装器(一)

    技术2022-05-18  14

    一、安装器的功能:

       1.安装程序功能;

       2.卸载程序功能。

     

    二、技术要点:

       1.扫描SD卡和ROM上的所有APK文件;

       2.用代码解析APK文件,获取APK文件的icon、label和packagename;

       3.调用系统的安装程序进行安装;

       4.获得所有用户已经安装的程序列表;

       5.调用系统的卸载程序进行卸载。

     

    三、实现步骤:

      1.扫描SD卡和ROM上的所有APK文件

     public class FileHelper { private Context context; private ArrayList<APKFileModel> apkFilesList; public FileHelper(Context context) { this.context = context; } /** scan all APK files in SD card and ROM. */ public ArrayList<APKFileModel> scanAllAPKFile() { if (!Environment.getExternalStorageState().equals( Environment.MEDIA_MOUNTED)) { return null; } String mCardPath = Environment.getExternalStorageDirectory().getPath(); apkFilesList = new ArrayList<APKFileModel>(); getFiles(mCardPath); return apkFilesList; } //Get all apk files. private void getFiles(String path) { File f = new File(path); File[] files = f.listFiles(); if (files != null) { for (int i = 0; i < files.length; i++) { File ff = files[i]; if (ff.isDirectory()) { getFiles(ff.getPath()); } else { if (ff.getName().toLowerCase().endsWith(".apk")) { APKFileModel apkFile = new APKFileModel(ff, context); apkFilesList.add(apkFile); } } } } } /** Check if file exists. */ public boolean isFileExist(String path) { File file = new File(path); if (file.exists()) return true; return false; } }

    一般情况下安装文件的后缀都是“.apk”,但是我如果把一个APK文件手动把后缀名改为大写的话发现也是可以安装的,所以代码里面就把文件名转化为小写再比较后缀。

     

       2.用代码解析APK文件获取APK文件的icon、label和packagename

      

    String PATH_PackageParser = "android.content.pm.PackageParser"; String PATH_AssetManager = "android.content.res.AssetManager"; try { Class pkgParserCls = Class.forName(PATH_PackageParser); Class[] typeArgs = new Class[1]; typeArgs[0] = String.class; Constructor pkgParserCt = pkgParserCls.getConstructor(typeArgs); Object[] valueArgs = new Object[1]; valueArgs[0] = apkPath; Object pkgParser = pkgParserCt.newInstance(valueArgs); DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); typeArgs = new Class[4]; typeArgs[0] = File.class; typeArgs[1] = String.class; typeArgs[2] = DisplayMetrics.class; typeArgs[3] = Integer.TYPE; Method pkgParser_parsePackageMtd = pkgParserCls.getDeclaredMethod( "parsePackage", typeArgs); valueArgs = new Object[4]; valueArgs[0] = new File(apkPath); valueArgs[1] = apkPath; valueArgs[2] = metrics; valueArgs[3] = 0; Object pkgParserPkg = pkgParser_parsePackageMtd.invoke(pkgParser, valueArgs); Field appInfoFld = pkgParserPkg.getClass().getDeclaredField( "applicationInfo"); ApplicationInfo info = (ApplicationInfo) appInfoFld .get(pkgParserPkg); Class assetMagCls = Class.forName(PATH_AssetManager); Constructor assetMagCt = assetMagCls.getConstructor((Class[]) null); Object assetMag = assetMagCt.newInstance((Object[]) null); typeArgs = new Class[1]; typeArgs[0] = String.class; Method assetMag_addAssetPathMtd = assetMagCls.getDeclaredMethod( "addAssetPath", typeArgs); valueArgs = new Object[1]; valueArgs[0] = apkPath; assetMag_addAssetPathMtd.invoke(assetMag, valueArgs); Resources res = context.getResources(); typeArgs = new Class[3]; typeArgs[0] = assetMag.getClass(); typeArgs[1] = res.getDisplayMetrics().getClass(); typeArgs[2] = res.getConfiguration().getClass(); Constructor resCt = Resources.class.getConstructor(typeArgs); valueArgs = new Object[3]; valueArgs[0] = assetMag; valueArgs[1] = res.getDisplayMetrics(); valueArgs[2] = res.getConfiguration(); res = (Resources) resCt.newInstance(valueArgs); CharSequence label = null; PackageManager pm = context.getPackageManager(); PackageInfo packageInfo = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES); ApplicationInfo appInfo = packageInfo.applicationInfo; // 这里就是读取一个apk程序的icon,label,packagename if (info.labelRes != 0) { label = res.getText(info.labelRes); this.appName = label.toString(); }else{ this.appName = (appInfo.name == null) ? appInfo.backupAgentName : appInfo.name; } if (info.packageName != null) { this.packageName = info.packageName; }else{ this.packageName = appInfo.packageName; } if (info.icon != 0) { this.icon = res.getDrawable(info.icon); } else { this.icon = pm.getApplicationIcon(appInfo); } } catch (Exception e) { e.printStackTrace(); } }

    这里采用的是两种方式相结合的方式读取APK文件的信息,单纯的使用第一种方式(指利用反射机制的方式)可以读取绝大多数APK文件的信息,但是也会遇到一两个读不出来的情况,这时候就需要第二种方式(指利用PackageManager的方式)来辅助读取信息。

    这样除非该APK文件已经损坏,不然怎么也能读点东西出来吧,呵呵。

      3.调用系统的安装程序进行安装

     Intent intent = new Intent(Intent.ACTION_VIEW); final Uri uri = Uri.fromFile(new File(path)); intent.setDataAndType(uri, "application/vnd.android.package-archive"); startActivity(intent);

      4.获得所有用户已经安装的程序列表

     

    private void getInstalledApps() { List<PackageInfo> packs = getPackageManager().getInstalledPackages(0); for (int i = 0; i < packs.size(); i++) { PackageInfo p = packs.get(i); if ((p.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) > 0) { continue; } InstalledAppModel newInfo = new InstalledAppModel(); newInfo.setAppname(p.applicationInfo.loadLabel(getPackageManager()) .toString()); newInfo.setPname(p.packageName); newInfo.setVersionName(p.versionName); newInfo.setVersionCode(p.versionCode); newInfo.setIcon(p.applicationInfo.loadIcon(getPackageManager())); installedAppInfoList.add(newInfo); } }

    这里唯一需要注意的地方就是如何过滤掉系统自带的APP,我想是因为如果是系统应用的话它的flags会被设置为0。

      5.调用系统的卸载程序进行卸载

     final Uri uri = Uri.parse("package:" + packageName); Intent intent = new Intent(Intent.ACTION_DELETE, uri); startActivity(intent);

     

    今天就先发这么多,剩下的留到下回分解,哈哈!!!!


    最新回复(0)