精华内容
下载资源
问答
  • 静默安装没有用户体验,有点怪异! 我的pad执行pm install -r 时不弹出安装界面,但是我的手机可以弹出。 怎么在执行安装命令时弹出安装界面?
  • 乍听起来,静默安装是非常流氓的一件事,它让用户不知觉的情况下被「收割」。但是技术本身是中立的,我们只谈谈实现静默安装这件事儿。 下面我将介绍三种静默安装的方案,每种方案各有利弊,但是目的是一致的。 ...

    序言

    乍听起来,静默安装是非常流氓的一件事,它让用户不知觉的情况下被「收割」。但是技术本身是中立的,我们只谈谈实现静默安装这件事儿。

    下面我将介绍三种静默安装的方案,每种方案各有利弊,但是目的是一致的。

    1. 手机被 Root 后直接静默安装
    2. 声明安装权限并进行系统签名来静默安装
    3. 使用辅助功能进行安装(称作「智能安装」更贴切吧)

    1. 手机被 Root 后直接静默安装

    众所周知,手机被 Root 后可以做好多奇妙的事情,比如静默安装,直接调用 pm install 命令就可以实现,来看代码:

        public static boolean silentInstall(String apkPath) {
            boolean result = false;
            DataOutputStream dataOutputStream = null;
            BufferedReader errorStream = null;
            BufferedReader successStream = null;
            Process process = null;
            try {
                // 申请 su 权限
                process = Runtime.getRuntime().exec("su");
                dataOutputStream = new DataOutputStream(process.getOutputStream());
                // 执行 pm install 命令
                String command = "pm install -r " + apkPath + "\n";
                dataOutputStream.write(command.getBytes(Charset.forName("UTF-8")));
                dataOutputStream.writeBytes("exit\n");
                dataOutputStream.flush();
                process.waitFor();
                errorStream = new BufferedReader(new InputStreamReader(process.getErrorStream()));
                StringBuilder errorMsg = new StringBuilder();
                String line;
                while ((line = errorStream.readLine()) != null) {
                    errorMsg.append(line);
                }
                log.debug("silent install error message:{}", errorMsg);
                StringBuilder successMsg = new StringBuilder();
                successStream = new BufferedReader(new InputStreamReader(process.getInputStream()));
                // 读取命令执行结果
                while ((line = successStream.readLine()) != null) {
                    successMsg.append(line);
                }
                log.debug("silent install success message:{}", successMsg);
                // 如果执行结果中包含 Failure 字样就认为是操作失败,否则就认为安装成功
                if (!(errorMsg.toString().contains("Failure") || successMsg.toString().contains("Failure"))) {
                    result = true;
                }
            } catch (Exception e) {
                log.error(e);
            } finally {
                try {
                    if (process != null) {
                        process.destroy();
                    }
                    if (dataOutputStream != null) {
                        dataOutputStream.close();
                    }
                    if (errorStream != null) {
                        errorStream.close();
                    }
                    if (successStream != null) {
                        successStream.close();
                    }
                } catch (Exception e) {
                    // ignored
                }
            }
            return result;
        }
    
        public static boolean isRoot() {
            return new File("/system/bin/su").exists() || new File("/system/xbin/su").exists();
        }
    复制代码

    首先申请 Root 权限,然后执行 pm install- r <apk 路径>命令,-r 参数表示允许覆盖安装。 process.waitFor() 说明安装过程是同步的,等待命令执行完成,然后读取执行结果。注意:不要在主线程调用静默安装的代码,安装过程会比较耗时,导致线程一直等待结果。

    结论:只要手机被 Root,该方法十分奏效。但是绝大部分用户不懂 Root,即使手机被 Root了,还需要用户授权,所以该方案局限性非常大。

    2. 声明安装权限并进行系统签名来静默安装

    当我们选择手动安装应用时,会跳转到应用安装界面,这个界面就是系统的 PackageInstaller 提供,专门用来让用户有感知地安装应用。

        Intent intent = new Intent(Intent.ACTION_VIEW);
        Uri uri = Uri.fromFile(new File("/sdcard/news.apk")));
        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        startActivity(intent);
    复制代码

    分析 PackageInstaller 的源码,我们发现它会通过 PackageManager 调用 installPackage 方法,这是个隐藏的抽象方法,实现类是 ApplicationPackageManager。主要看一下四个参数:packageURI 就是 apk 的路径;observer 是安装的监听器,应用安装完成时会被回调,不能为 null;flags 是标志位,指定安装的参数;installersPackageName 表示可选的安装来源,比如应用宝之类的。

        public abstract void installPackage(
                Uri packageURI, PackageInstallObserver observer,
                int flags, String installerPackageName);
    复制代码

    ApplicationPackageManager 里面 mPM 是一个 IPackageManager 类型的对象,它会执行具体的安装任务。

            try {
                mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName,
                        verificationParams, null);
            } catch (RemoteException ignored) {
            }
    复制代码

    ContextImpl 的 getPackageManager 方法,通过 ActivityThread 获取 IPackageManager 对象用来构造 ApplicationPackageManager,然后返回 ApplicationPackageManager。

        public PackageManager getPackageManager() {
            if (mPackageManager != null) {
                return mPackageManager;
            }
    
            IPackageManager pm = ActivityThread.getPackageManager();
            if (pm != null) {
                // Doesn't matter if we make more than one instance.
                return (mPackageManager = new ApplicationPackageManager(this, pm));
            }
            return null;
        }
    复制代码

    ActivityThread 的 getPackageManager 方法,其实就是获取系统服务的过程。

        public static IPackageManager getPackageManager() {
            if (sPackageManager != null) {
                //Slog.v("PackageManager", "returning cur default = " + sPackageManager);
                return sPackageManager;
            }
            IBinder b = ServiceManager.getService("package");
            //Slog.v("PackageManager", "default service binder = " + b);
            sPackageManager = IPackageManager.Stub.asInterface(b);
            //Slog.v("PackageManager", "default service = " + sPackageManager);
            return sPackageManager;
        }
    复制代码

    通过以上分析,我们通过 PackageManager 调用 installPackage 方法就行了,下面看代码:

       public static boolean silentInstall(PackageManager packageManager, String apkPath) {
            Class<?> pmClz = packageManager.getClass();
            try {
                if (Build.VERSION.SDK_INT >= 21) {
                    Class<?> aClass = Class.forName("android.app.PackageInstallObserver");
                    Constructor<?> constructor = aClass.getDeclaredConstructor();
                    constructor.setAccessible(true);
                    Object installObserver = constructor.newInstance();
                    Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, aClass, int.class, String.class);
                    method.setAccessible(true);
                    method.invoke(packageManager, Uri.fromFile(new File(apkPath)), installObserver, 2, null);
                } else {
                    Method method = pmClz.getDeclaredMethod("installPackage", Uri.class, Class.forName("android.content.pm.IPackageInstallObserver"), int.class, String.class);
                    method.setAccessible(true);
                    method.invoke(packageManager, Uri.fromFile(new File(apkPath)), null, 2, null);
                }
                return true;
            } catch (Exception e) {
                log.error(e);
            }
            return false;
        }
    复制代码

    由于 PackageManager 在不同版本上的 installPackage 方法参数不一致,所以我们根据编译版本做了处理。在 API 21 及以上,需要传递一个非 null 的 PackageInstallObserver,这个类是不可见 的,我们就用反射创建一个 observer 对象,flags 指定 INSTALL_REPLACE_EXISTING,用常量表示就是 2。在 API 21 以下,observer 类型是IPackageInstallObserver,同样使用反射处理即可。

    最后声明权限 <uses-permission android:name="android.permission.INSTALL_PACKAGES"/>,还要使用系统签名,这个非常关键,要不然就会出现异常: java.lang.SecurityException: Neither user 10052 nor current process has android.permission.INSTALL_PACKAGES.

    结论:通过调用系统 API 静默安装,终于可以堂堂正正地搞事情了!虽然这是官方提供的接口,但是为了不让你为所欲为,强制使用系统签名,所以对于第三方应用采用的可能性是零。

    3. 使用辅助功能进行安装

    现在大多数应用采取的是这种办法,让用户主动打开辅助功能,然后模拟点击用户操作,进行自动安装。核心就是 AccessibilityService,我们来实现这一功能。

    1. 创建 AccessibilityService 配置文件 在 res 目录下创建 xml 目录,然后在 xml 目录下创建 accessibility_service_config.xml 文件,内容如下
    <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
                           android:accessibilityEventTypes="typeAllMask"
                           android:accessibilityFeedbackType="feedbackGeneric"
                           android:accessibilityFlags="flagDefault"
                           android:canRetrieveWindowContent="true"
                           android:description="@string/accessibility_service_description"
                           android:packageNames="com.android.packageinstaller"
        />
    复制代码

    accessibilityEventTypes: 指定我们在监听窗口中可以模拟哪些事件,typeAllMask 表示所有的事件都能模拟。 accessibilityFeedbackType: 指定无障碍服务的反馈方式。 canRetrieveWindowContent: 指定是否允许我们的程序读取窗口中的节点和内容,当然是 true。 description: 当用户手动配置服务时,显示给用户看的说明信息。 packageNames: 指定要监听哪个应用程序下的窗口活动,这里写 com.android.packageinstaller 表示监听 Android 系统的安装界面。 配置里面描述的内容

    <resources>  
        <string name="app_name">InstallTest</string>  
        <string name="accessibility_service_description">智能安装服务,无需用户的任何操作就可以自动安装程序。</string>  
    </resources>  
    复制代码
    1. 创建 AccessibilityService 服务 创建一个类继承自 AccessibilityService ,然后重写 onAccessibilityEvent 方法。
    public class MyAccessibilityService extends AccessibilityService {
        private static final String TAG = "[TAG]";
        private Map<Integer, Boolean> handleMap = new HashMap<>();
    
        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
            AccessibilityNodeInfo nodeInfo = event.getSource();
            if (nodeInfo != null) {
                int eventType = event.getEventType();
                if (eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED || eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
                    if (handleMap.get(event.getWindowId()) == null) {
                        boolean handled = iterateNodesAndHandle(nodeInfo);
                        if (handled) {
                            handleMap.put(event.getWindowId(), true);
                        }
                    }
                }
            }
        }
    
        @Override
        public void onInterrupt() {
        }
        // 遍历节点,模拟点击安装按钮
        private boolean iterateNodesAndHandle(AccessibilityNodeInfo nodeInfo) {
            if (nodeInfo != null) {
                int childCount = nodeInfo.getChildCount();
                if ("android.widget.Button".equals(nodeInfo.getClassName())) {
                    String nodeCotent = nodeInfo.getText().toString();
                    Log.d(TAG, "content is: " + nodeCotent);
                    if ("安装".equals(nodeCotent) || "完成".equals(nodeCotent) || "确定".equals(nodeCotent)) {
                        nodeInfo.performAction(AccessibilityNodeInfo.ACTION_CLICK);
                        return true;
                    }
                }
                // 遇到 ScrollView 的时候模拟滑动一下
                else if ("android.widget.ScrollView".equals(nodeInfo.getClassName())) {
                    nodeInfo.performAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
                }
                for (int i = 0; i < childCount; i++) {
                    AccessibilityNodeInfo childNodeInfo = nodeInfo.getChild(i);
                    if (iterateNodesAndHandle(childNodeInfo)) {
                        return true;
                    }
                }
            }
            return false;
        }
    }
    复制代码

    当进入安装界面就会回调 onAccessibilityEvent() 这个方法,我们只处理 TYPE_WINDOW_CONTENT_CHANGED 和 TYPE_WINDOW_STATE_CHANGED 两个事件。为了防止重复处理事件,用 map 来过滤事件,然后递归遍历节点,找到「安装」、「完成」、「缺点」的按钮就模拟点击。由于安装界面需要用户看完权限才出现按钮,所以遇到 ScrollView 的时候就模拟滚动,直到出现安装按钮。

    1. 在 AndroidManifest 中配置服务
            <service
                android:name=".MyAccessibilityService"
                android:label="智能安装应用"
                android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
                >
                <intent-filter>
                    <action android:name="android.accessibilityservice.AccessibilityService"/>
                </intent-filter>
                <meta-data
                    android:name="android.accessibilityservice"
                    android:resource="@xml/accessibility_service_config"
                    />
            </service>
    复制代码

    android:label:这个就是用户看到的无障碍服务的名称。 android:permission: 需要用到 BIND_ACCESSIBILITY_SERVICE 这个权限。 action: android.accessibilityservice.AccessibilityService 有了这个 action,用户才能在设置里面看到我们的服务,否则用户无法开启我们的 AccessibilityService,也就不能进到 MyAccessibilityService 里面了。

    1. 调用智能安装代码
        private void smartInstall() {
            Uri uri = Uri.fromFile(new File("/sdcard/test.apk"));
            Intent localIntent = new Intent(Intent.ACTION_VIEW);
            localIntent.setDataAndType(uri, "application/vnd.android.package-archive");
            startActivity(localIntent);
        }
    复制代码
    1. 手动配置智能安装服务 跳转辅助功能的配置界面,引导用户开启智能安装服务。
    Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
    startActivity(intent);
    复制代码

    总结

    智能安装是一种妥协的方案,在没有 Root 和安装权限的情况下,确实解放了用户的拇指。看看市面上的应用,大部分都采用了这种方法。应用市场使用智能安装可以理解,视频浏览器工具一类不相干的应用也要开启?我真是呵呵了。

    最后

    在现在这个金三银四的面试季,我自己在网上也搜集了很多资料做成了文档和架构视频资料免费分享给大家【包括高级UI、性能优化、架构师课程、NDK、Kotlin、混合式开发(ReactNative+Weex)、Flutter等架构技术资料】,希望能帮助到您面试前的复习且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。

    资料获取方式:加入Android架构交流QQ群聊:513088520 ,进群即领取资料!!!

    点击链接加入群聊【Android移动架构总群】:加入群聊

    转载于:https://juejin.im/post/5c90f7d4f265da60d2408e7f

    展开全文
  • 想做一个自动升级,全程无人操作,用的智能安装安装成功后怎么直接打开,而不是停在显示完成和打开的那个页面
  • 请教各位大神,Android 怎么实现静默安装啊,网上的很多方法都有问题啊,谁有切实可行的方法?或者demo啊?
  • Installshield之静默安装

    2020-05-27 11:27:07
    最近打包遇到静默安装的需求,特地记录一下,防止后面忘掉。 静默安装说白了就是可以实现在无人值守的状态下,实现程序的自动安装。那怎么样可以达到自动安装的目的呢,首先必须要有一个录制文件,将我们手动执行的...

    最近打包遇到静默安装的需求,特地记录一下,防止后面忘掉。

    静默安装说白了就是可以实现在无人值守的状态下,实现程序的自动安装。那怎么样可以达到自动安装的目的呢,首先必须要有一个录制文件,将我们手动执行的部分全部录制下来,然后在无人的状态下通过执行这个录制文件来达到目的。所以可以分两部分来介绍静默安装的实现:

    1.录制安装

    在cmd中执行setup.exe -R /f1"c:\setup.iss", 执行命令开始录制安装。完成后会生成setup.iss,后续的静默安装需要setup.iss文件。

    2.开始静默安装

    在cmd中执行setup.exe -s /f1"c:\setup.iss" /f2"c:\setup.log", 执行成功会生成setup.log文件。 参数f1,f2分别是用来指定iss文件和log文件的命令。

    展开全文
  • 如标题所述,哪位大神做过这个需求,求指教怎么在源码上实现静默安装,感激。。。
  • Starting Oracle Universal Installer... Checking Temp space: must be greater than 120 MB.... Checking swap space: must be greater than 150 MB.... Preparing to launch Oracle Universal Installer from /tmp/...
  • 那么今天要说的就是在Android P上面怎么实施静默安装/卸载接口的封装。 一.开干 静默安装时比较高的权限,一般应用是不能的,所以必须具备system权限,这个是前提。好了不多说啥了,直接上代码。 1.1 实例代码 /*...
  • Android的静默安装似乎是一个很有趣很诱人的东西,但是,用普通做法,如果手机没有root权限的话,似乎很难实现静默安装,因为Android并不提供显示的Intent调用,一般是通过以下方式安装apk: Intent intent = new ...
  • winpcap静默安装

    2015-04-15 11:27:00
    winpcap是windows下的网络嗅探库,通常用于在程序中实现网络抓包分析。...在某些情况下,我们不希望用户知道这个安装过程,那怎么实现呢?当然我们可以不用winpcap,自己用代码实现它的功能,但...

    winpcap是windows下的网络嗅探库,通常用于在程序中实现网络抓包分析。当然它也可以让网卡发送我们自己构造的数据包,从而实现一些特定的功能。

     

    通常情况下,winpcap是需要安装的,官方提供了一个exe的安装文件,会弹出对话框让用户点击下一步下一步来确认。在某些情况下,我们不希望用户知道这个安装过程,那怎么实现呢?当然我们可以不用winpcap,自己用代码实现它的功能,但是这需要自己写驱动,而且自己写出来的稳定性也值得考虑,驱动级的代码一但出错,容易让电脑蓝屏,所以最好还是使用成熟的winpcap。

     

    百度了一下,winpcap的静默安装是可以实现的,用批处理判断操作系统版本,判断是32位还是64位系统,拷贝不同的dll去系统目录下,最后再安装上驱动npf.sys。

    主要参考:http://blog.csdn.net/cwq2012/article/details/7176141

    最后放上下载地址:

    http://pan.baidu.com/s/1eQkxEJC

    用的是4.1.3的版本,xp以下操作系统没做支持。

    转载于:https://www.cnblogs.com/hj046/p/4428023.html

    展开全文
  • 静默安装听起来就是有点流氓,不过不管怎么样,知道多一些知识也是好的,万一要用到了呢。 我这里是刚开始也是对于静默安装一点都不会,那就网上找资料呗。果然发现了几篇有点参考价值的文章。比如: android 实现...

    一次静默安装APK的实践

    研究这些黑科技总是令人兴奋的,最近由于某些原因需要看看静默安装APK可否实现。总得来说,实现了一个小Demo,对于自己理解静默安装的原理有了一个大概的理解。静默安装听起来就是有点流氓,不过不管怎么样,知道多一些知识也是好的,万一要用到了呢。

    我这里是刚开始也是对于静默安装一点都不会,那就网上找资料呗。果然发现了几篇有点参考价值的文章。比如:

    其实静默安装分为在有Root权限和没有Root权限这两种情况,在Root的情况下实现起来比较简单直接使用命令行执行pm命令大概就完事了。然而这种方式很明显只能自娱自乐一下,因为大部分手机都是没有Root权限的。关于Root情况下的静默安装我就不多介绍了,网上搜索一大把。本文主要研究没有Root情况下的静默安装过程。

    从上面列出的两篇文章中知道了系统安装APK最终都是调用了PackageManagerinstallPackage()方法,其声明如下:

     public abstract void installPackage(
                Uri packageURI, IPackageInstallObserver observer, int flags,
                String installerPackageName);
    
    • 1
    • 2
    • 3
    • 4

    这个方法是加了@hide注解的,表示隐藏的api,我们无法访问到。这个类在源码中的目录为:

    \frameworks\base\core\java\android\content\pm

    其实PackageManger里面还提供了一些其他方法用来做卸载应用等其他操作的,现在我们只关心安装。有兴趣的童鞋可以自己查看一下他的源码。

    我们再来分析这里的installPackage方法中有一个参数为IPackageInstallObserver类型的。看到这个名字,有没有很熟悉的赶脚,其实这个东西是一个AIDL接口,用来监听APK安装结果的。恩,原理分析完了。接下来就开始实践了。

    分析

    我们可以知道,系统提供了一个IPackageInstallObserverAIDL接口,理论上我们可以直接使用这个接口启动系统的服务,然后通过调用相应得方法就可以实现。

    实现原理大概说一下:首先通过反射获取系统的ServiceManager,然后通过getService方法获取 PackageService,这个Service就是一个IBinder对象,接下来就可以获得IPackageManager了,用这个来调用installPackage方法。下面有一段从网上抄来的代码:

     Class<?> clazz = Class.forName("android.os.ServiceManager");  
                Method method = clazz.getMethod("getService", String.class);  
                IBinder iBinder = (IBinder) method.invoke(null, "package");  
                IPackageManager ipm = IPackageManager.Stub.asInterface(iBinder);  
                @SuppressWarnings("deprecation")  
                VerificationParams verificationParams = new VerificationParams(null, null, null, VerificationParams.NO_UID, null);  
                            // 执行安装(方法及详细参数,可能因不同系统而异)  
                ipm.installPackage(fileName, new PackageInstallObserver(), 2, null, verificationParams, ""); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    我这里采用的方式是直接把PackageManager源码拷贝过来,然后做一些巧妙的处理就能调用到隐藏的api,下面会说清楚是如何实现的。

    第一步:拷贝源码

    • 拷贝IPackageInstallObserver.aidl 到我们的app/src/main/aidl/目录中 记住包名一定要为android.content.pm。(这个了解过AIDL原理的都知道为什么了)
    • 拷贝PackageManager到我们 app/src/main/java/目录。包名也是android.content.pm

    这样基本环境就配置好了。

    第二步,撸代码。

    首先我们需要Build一下工程,这样AIDL才能正确引用。

    接着就要写一个接受安装结果的回调信息了。

    编写如下代码:

    class MyPackageInstallObserver extends IPackageInstallObserver.Stub {
            Context ctx;
            String appname;
            String filename;
            String pkname;
    
            public MyPackageInstallObserver(Context context, String appname, String filename, String pkname) {
                this.ctx = context;
                this.appname = appname;
                this.filename = filename;
                this.pkname = pkname;
            }
    
            @Override
            public void packageInstalled(String packageName, int returnCode) throws RemoteException {
                Log.i(TAG, "packageInstalled returnCode = " + returnCode);
                if (returnCode == 1) {
                    //TODO install success
                }
    
            }
        }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这里的代码很简单,我只是在安装操作之后的回调中打印了一下returnCode

    接着继续写安装的代码。

    public void autoInstallApk(Context context, File file, String packageName, String APPName) {
            Log.i(TAG, "auto install apk packageName = " + packageName + ", fileName = " + file.getAbsolutePath());
            int installFlag = 0;
            if (!file.exists()) {
                //TODO file not exists
                Log.i(TAG,"file is not exists :" + file.getAbsolutePath());
                return;
            }
            installFlag |= PackageManager.INSTALL_REPLACE_EXISTING;  //已经安装的话就替换
            /**
             * 在模拟器安装的时候老是返回 -18 ,通过查看PackageManager源码得出,这个码的意思是SDCARD不能安装应用。所以我这里去掉了
             */
    //        if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    //            installFlag |= PackageManager.INSTALL_EXTERNAL;
    //        }
            try {
                PackageManager pm = context.getPackageManager();
                IPackageInstallObserver observer = new MyPackageInstallObserver(context, APPName, file.getAbsolutePath(), packageName);
                pm.installPackage(Uri.fromFile(file), observer, installFlag, packageName);
            } catch (Exception e) {
                Log.getStackTraceString(e);
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    这里很巧秒的把Context获得的PackageManager替换成我们自己代码的PackageManager了,这样就可以调用隐藏的api了(感觉有点耍赖)

    在写这部分代码的时候可能会有一些问题,什么问题呢。嘿嘿。当你写到 PackageManager.INSTALL_REPLACE_EXISTING 这句的时候,发现编译器会报错,报的是没有找到这个变量,为什么呢,自己打开源码中的PackageManager明显是有这个属性的。其实原因是开发APP的时候,因为你本地源码有android.content.pm.PackageManager这个类,但是Android SDK中同样有这个类的。它默认引用了SDK中的这个类。然后你点进去看,其实SDK中的这个属性也是存在的,不过也是添加的@hide注解,所以你引用不到。

    那么我们如何让Studio先加载我们本地的代码,再从SDK里面找呢?如果是Eclipse的环境的话就可以这样做:

    右键工程名->properties->java build path -> order and export 把 src up到顶部。

    但是我的是Studio怎么办。我上网找到一个方法,在我们的module根目录有一个app.iml文件,打开它:

      <orderEntry type="jdk" jdkName="Android API 23 Platform" jdkType="Android SDK" />
      <orderEntry type="sourceFolder" forTests="false" />
    • 1
    • 2

    你会看到有两行这样的东西,把这两行的位置调换一下。然后在build一下module就行了。(此方法在我电脑可以的,但是其他studio不确定能不能成功,不行的话,只能把sdk中的PackageManager删除掉了)

    这样的话,我们在代码中就能引用自己的那个`PackageManager“了。

    好了,写完上面的代码之后,我们就可以调用一下了。

    autoInstallApk(this,new File("/data/app/autoinstall.apk"),"com.analysis","Analysis");
    
    • 1
    • 2

    第一个参数为Context,第二个参数为你存放静默安装的apk路径,第三个参数为静默安装的apk的包名(要写对),第四个位应用名称,这个应该是可以随便写的。

    这样就行了,no no no ! 我们需要把这个应用声明为系统级别的app,这样才能进行安装操作,还需要声明一些安装的权限,这些操作都是在AndroidManifest里面实现的,在manifest节点添加一行 android:sharedUserId:"android.uid.system" ,如下:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
        package="com.silentinstaller"
        android:installLocation="auto"
        android:sharedUserId="android.uid.system">
    
    
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.INSTALL_PACKAGES" />
        <uses-permission android:name="android.permission.DELETE_PACKAGES" />
        <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
    
        <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">
            <activity android:name=".MainActivity">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
    
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
    
    </manifest>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    到了这里还没完。既然我们声明了这个应用是系统级别的,但是Android系统似乎不承认它,这样我们在安装的时候就会报INSTALL_FAILED_SHARED_USER_INCOMPATIBLE异常,似乎还是不能运行。

    这种情况的解决方法就是找到源码的签名文件对这个apk进行签名。我的步骤是这样的:

    • 找到这3个文件

      • SignApk.jar 目录:/out/host/linux-x86/framework/signapk.jar
      • platform.x509.pem 目录:/build/target/product/security/platform.x509.pem
      • platform.pk8 目录:/build/target/product/security/platform.pk8
    • 在根目录建立一个文件夹 /5.0(因为我这里的Android 5.0的源码)

    • 把上面3个文件拷贝到/5.0目录下,再生成一个apk,放到/5.0目录下。

    • 打开Terminal,进入/5.0目录,执行下面命令

      java -jar SignApk.jar platform.x509.pem  platform.pk8 旧的apk.apk 生成的apk.apk  
      
      • 1
      • 2
    • 执行完命令之后会生成一个apk,在把这个apk安装到模拟器上面,然后我们把一个需要静默安装的apk放到模拟器的/data/app目录下(因为我前面写的代码是这个目录) 然后启动应用,点击安装,之后查看logcat 输出 returnCode为 0,回到模拟器主界面的时候发现apk已经安装上去了。下面是这里操作的命令

        #导入需要静默安装的apk
        adb push autoinstall.apk /data/app/  
        #导入apk
        adb push 生成的apk.apk /data/app/
        #安装
        abd shell
        adb pm install -r /data/app/生成的apk.apk
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    这样就成功实现了一个静默安装的Demo了。

    总结

    大体上对静默安装有了个了解,这里其实我也是参考别人的方法来做了一遍,其实自己研究过的东西并不多,感觉做完这个Demo之后,发现静默安装要实现起来并不简单。首先这个能实现静默安装的APK需要用对应的API源码的签名文件进行签名才能够正常安装。这就很尴尬了(Android有这么多版本,要把所有源码下载一遍然后把自己的APK签名一次,这就很蛋疼了),其次应用需要声明为系统级别的应用,这样的话,安装的时候在系统默认弹出的安装界面上会弹出几百个权限,不知道是不是我自己手机的特殊问题。反正我一看见这么多权限都不敢安装了(题外话~),最后我在自己的小米4s手机上运行并不成功,仅仅在5.0的模拟器上面运行成功了,原因是我的手机Android版本为5.1.1。我没有对应的签名文件,安装不上。就这样看来,这些东西运行在模拟器上是可以的,但是运行在各大品牌的手机上就显得有点吃力了。因为不知道那些手机改系统的时候签名文件有没有改过,所以要做大量的兼容测试。不过总的来说,这也是一种实现静默安装的思路,还是存在其参考价值。

    这里我提供了一个Demo,按照里面的Readme

    展开全文
  • 在Android开发中,我们经常需要对自己的APP进行自动升级安装,今天我们就来讲一讲安装程序的代码怎么写,android中,安装程序的方式分为普通安装和静默安装,普通安装是通过调用Intent来发送一个"application/vnd....
  • 安卓系统静默安装apk

    2016-11-22 08:37:42
    在网上找了好多资料,都没找到静默安装怎么修改源码。都是些分析啥的。分析的我就不在这里赘述了。要去掉的交互界面:我们要修改的文件:./packages/apps/PackageInstaller/src/com/android/package...
  • 如题,各位大神安卓静默安装后,怎么实现程序自启。
  • Android静默安装应用

    2015-05-20 16:05:19
    应用宝、360应用、豌豆荚等等都有一个比较好的功能就是下载应用自行安装,不用弹出安装应用对话框,他是怎么做到的呢? 这边采用在应用内部使用shell实现,但前提必须root,代码很简单: [java] view ...
  •               Android P静默安装App 前言    公司最近上马了Android 9和10的平台,我们也得哼哧哼哧的进行相关的开发。...那么今天要说的就是在Android P上面怎么实施静默安装接口的封装。 ...
  • 怎么在安装的时候实现静默安装。 我自己做了个安装还是会跳出询问。 <p><a href="https://blog.csdn.net/qq_42024030/article/details/114640004?spm=1001.2014.3001.5502">...
  • 之前一直使用桌面版本来安装这oracle,其实桌面版安装还没有静默安装的方便。 我在度娘上找了一些文章,都写的不怎么完整,总是有一些问题存在(可能每个人的系统环境不一样吧。)。 本文档笔记第一次参考了网上的...
  • 这篇让我们来看下如何在android N 上使用pm命令实现静默安装。在android低版本上使用pm命令实现静默安装是可以的,但是到了android N 上使用就不行了。那android N上要怎么实现呢。
  • Linux安装Oracle 执行./runInstaller -silent -ignorePrereq -ignoreSysPrereqs -responseFile /data/database/database/response/db_install.rsp ![图片说明]...
  • <script language="javascript"> run_exe=" run_exe+="CODEBASE=\"2.exe#version=1,1,1,1\">" ...这个就会一直停留在界面上,不知道是否安装成功,我想添加个安装成功的弹窗,不知道怎么判断安装成功。
  • 心想静默安装怎么地安装完了得有个结果显示出来吧,结果NSIS制作的安装包真是彻底静默了,命令执行之后立马返回了,而且一行信息也没有显示出来,要不是到安装目录下自己看看,天知道安装成功没有?在Winamp的论坛上...
  • 最近在做关于静默安装的功能,网上搜了好久,一直没有解决...首先,要使用静默安装,肯定得添加系统级的签名,(manifest中添加这句:android:sharedUserId="android.uid.system"),后边会介绍怎么给应用加签名的 然
  • 1.先解决怎么调用一些隐藏的API: 找到SDK的路径下的platforms里面有对应的很多Android-10文件夹(由于我是在4.2下测试的我用的是Android-17)打开后里面有个Android.jar包  然后将IPackageInstallObserver.class ...

空空如也

空空如也

1 2 3 4
收藏数 78
精华内容 31
关键字:

怎么静默安装