之前客户需要实现这么一个需求:
现在有一个外壳,当用户安装了这个外壳之后,集成在外壳里面的其他几个应用也安装好了。
说白了就是需要实现静默安装,类似于流氓软件。
当然想法是好的,如果真的有那么容易实现,Android的安全机制实在是......太弱了。
后来,使用了另外一个解决方案。
就是在安装了这个外壳之后,监听这个外壳安装好的广播,然后在该检测广播的onReceive方法里面检测其余的几个apk是否已经安装,如果
安装了就依次检测下一个应用是否安装,如果没有,则启动安装器提示安装。
不过前提条件是需要知道其余几个app的包名。
首先贴几个需要用到的方法:
package com.example.androidinstalltestdemo; import java.io.File; import java.io.FileOutputStream; import java.io.InputStream; import java.util.List; import android.content.Context; import android.content.Intent; import android.content.pm.PackageInfo; import android.content.pm.PackageManager; import android.net.Uri; /** * <p> * </p> * * @author xiangxm */ public class Common { /** * 检查模块是否安装 * * @param context * @param packageName * 需要检查的包名称 * @return */ public static boolean isAppInstalled(Context context, String packageName) { final PackageManager packageManager = context.getPackageManager(); // 获取所有已安装程序的包信息 List<PackageInfo> pinfo = packageManager.getInstalledPackages(0); for (int i = 0; i < pinfo.size(); i++) { if (pinfo.get(i).packageName.equalsIgnoreCase(packageName)) { return true; } else { continue; } } return false; } /** * 拷贝apk到应用 目录 * * @param ctx * Context变量 */ public static void copyApkFromAssets(Context ctx, String apkName) { // 所有集成的APK所存放的位置。 String filePath = "/assets/" + apkName; try { // 从assets读取文件流 InputStream is = ctx.getClass().getResourceAsStream(filePath); // 将该文件流写入到本应用程序的私有数据区this.getFilesDir().getPath(); FileOutputStream fos = ctx.openFileOutput(apkName, Context.MODE_PRIVATE + Context.MODE_WORLD_READABLE); byte[] buffer = new byte[1024]; int len = 0; while ((len = is.read(buffer)) != -1) { fos.write(buffer, 0, len); } fos.flush(); is.close(); fos.close(); } catch (Exception e) { e.printStackTrace(); } } /** * * 安装apk apk路径在:/data/data/" + ctx.getPackageName() + "/files下 * * @param ctx * @param apkName */ public static void Install_New(Context ctx, String apkName) { // 如果文件不存在则再次复制一遍 File file = new File(ctx.getFilesDir().getPath() + "/" + apkName); if (!file.exists()) { // 拷贝apk文件到应用目录下。 copyApkFromAssets(ctx, apkName); } Intent intentInstall = new Intent(); // 开始启动安装器安装apk intentInstall.setAction(android.content.Intent.ACTION_VIEW); intentInstall.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive"); intentInstall.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ctx.startActivity(intentInstall); } /** * 安装apk apk们的路径:/data/data/" + ctx.getPackageName() + "/files下 * * @param ctx * @param apkName */ public static void installApk_New(Context ctx, String apkName) { // 应用目录 File f = new File(ctx.getFilesDir().getPath() + "/" + apkName); // 下面执行安装 Intent intent = new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Intent.ACTION_VIEW); String type = "application/vnd.android.package-archive"; intent.setDataAndType(Uri.fromFile(f), type); ctx.startActivity(intent); } }
大致思路: 启动app时候就检测需要的几个app是否已经安装,然后根据实际情况拷贝apk文件到对应目录。
监听安装完成的广播。
<!-- 注册安装apk监听 --> <receiver android:name=".InstallDeleteBroadcastReceiver" > <intent-filter> <action android:name="android.intent.action.PACKAGE_ADDED" /> <action android:name="android.intent.action.PACKAGE_REMOVED" /> <action android:name="android.intent.action.PACKAGE_REPLACED" /> <data android:scheme="package" > </data> </intent-filter> </receiver>
检查是否是平板:
/** * 判断是否是平板 * * @param context * @return */ public static boolean isTablet(Context context) { return (context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_LARGE; }