android批量安装APK

本文介绍了如何在Android设备上实现批量安装APK,避免手动安装带来的不便。由于Google的安全措施,官方SDK不提供静默安装API,但系统开发者可以通过调用隐藏的PackageManager方法实现。文章展示了应用界面和安装过程,并提供了主要代码示例,说明了编译系统时整合应用的方法。若需完整工程,可联系作者获取。

    在android机上安装APK时一般都是借助PC端的91手机助手或者豌豆荚之类的工具;或者直接将APK下载到android机子上手动点击安装,手动安装时系统会弹出“是否要安装该应用程序?”的对话框让你选择是否安装,如果一次只安装一两个APK还可以在每次弹出对话框时选择“安装“,如果一次需要安装数十个时这是相当费力而且容易漏掉APK没有安装的。

    能不能自己写个应用来实现一次批量安装所有APK呢?答案是肯定的,但是对于一个只熟悉android应用开发的人员是办不到的。因为google为了防止出于一些特殊目的的开发人员编写应用程序偷偷的在你的android机子上安装一些有特殊目的的APK,已经将静默安装APK的API屏蔽了,在官方发布的SDK中是没有这样的API给你调用的。但是对于系统开发人员还是很容易开发出这样的应用的,因为我们可以在编译系统时去编译我们调用了google隐藏了的API的应用。

    先上个我写的批量安装APK应用的界面和批量安装APK时的效果图,该应用实现的功能是先扫描外置SD、TF卡下的所有APK,点击”安装APK“按钮后即开始一次批量安装完扫描到的APK,不需要用户去干预。

    该应用主要用到的是android.content.pm.PackageManager类下的installPackage方法来实现静默安装所有APK的。

   工程文件列表:

updatepackage
│  .classpath
│  .project
│  Android.mk
│  AndroidManifest.xml
│  MainActivity.apk
│  
├─res
│  ├─drawable
│  │      back.jpg
│  │      
│  ├─drawable-hdpi
│  │      ic_action_search.png
│  │      ic_launcher.png
│  │      
│  ├─drawable-ldpi
│  │      ic_launcher.png
│  │      
│  ├─drawable-mdpi
│  │      ic_action_search.png
│  │      ic_launcher.png
│  │      
│  ├─drawable-xhdpi
│  │      ic_action_search.png
│  │      ic_launcher.png
│  │      
│  ├─layout
│  │      activity_main.xml
│  │      
│  ├─menu
│  │      activity_main.xml
│  │      
│  ├─values
│  │      dimens.xml
│  │      strings.xml
│  │      styles.xml
│  │      
│  └─values-large
│          dimens.xml
│          
└─src
    └─com
        └─example
            └─adnroid
                └─updatepackage
                        ApkFile.java
                        ApkSearchUtils.java
                        MainActivity.java
                        UpdateBroadcastReceiver.java
    android系统源代码下添加我们的updatepackage工程,工程下添加用于编译的Android.mk文件内容如下所示:

LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_MODULE_TAGS := optional

LOCAL_SRC_FILES := $(call all-subdir-java-files)

LOCAL_PACKAGE_NAME := MainActivity
LOCAL_DEX_PREOPT :=false
LOCAL_CERTIFICATE := platform
include $(BUILD_PACKAGE)

    布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" 
    >
    
   <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dip"
        android:text="@string/textview1text"/>
    <ProgressBar
        android:id="@+id/progressBar1"
        style="?android:attr/progressBarStyleHorizontal"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dip"
        />
    <TextView
        android:id="@+id/textView2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dip"
        android:text= "@string/textview2text"/>
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dip"
        android:text="@string/button1text"/>

  

</LinearLayout>

    AndroidManifest.xml文件:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.adnroid.updatepackage"
    android:versionCode="1"
    android:versionName="1.0" 
    >

    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="8" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.example.adnroid.updatepackage.MainActivity"
            android:label="@string/title_activity_main" 
            android:launchMode="singleTask">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name="UpdateBroadcastReceiver" android:enabled="true">
            <intent-filter >
                <action android:name="com.example.adnroid.updatepackage.hello"/>
            </intent-filter>
        </receiver>
    </application>
	<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
	<uses-permission android:name="android.permission.READ_PHONESTATE" />
	<uses-permission android:name="android.permission.INSTALL_PACKAGES" />
	<uses-permission android:name="android.permission.DELETE_PACKAGES" />
	<uses-permission android:name="android.permission.CLEAR_APP_CACHE" />
	<uses-permission android:name="android.permission.CLEAR_APP_USER_DATA" />
</manifest>

    主要界面MainActivity.java代码:

package com.example.adnroid.updatepackage;

import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.content.Intent;
import android.net.Uri;
import java.io.File;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.IPackageInstallObserver;


public class MainActivity extends Activity {
	int count,i;
	ApkSearchUtils apksearchutils;
	private static final int  UPDATE = 0;
	ProgressBar progressbar1;
	Button button1;
	TextView textview1;
	TextView textview2;
	private final int INSTALL_COMPLETE = 1;
	boolean threadrun=true;
	boolean installcomplete=false;
	private final int INSTALL_COMPLETED = 1;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        button1=(Button)findViewById(R.id.button1);
        progressbar1=(ProgressBar)findViewById(R.id.progressBar1);
        textview1=(TextView)findViewById(R.id.textView1);
        textview2=(TextView)findViewById(R.id.textView2);
        progressbar1.setMax(100);
        apksearchutils=new ApkSearchUtils(this);
        apksearchutils.FindAllAPKFile(new File("/mnt/sdcard/"));
        
    	count=apksearchutils.getMyFiles().size();
		i=0;
		Log.e("zkliu","count:"+String.valueOf(count)+","+"i:"+String.valueOf(i));
        button1.setOnClickListener(new Button.OnClickListener() {
			@Override
			public void onClick(View arg0) {
			
		        new Thread(new Runnable() {
		        	
					@Override
					public void run() {
						threadrun=true;
						
						installcomplete=true;
						while(threadrun){
							try{
								if(!installcomplete){
									Thread.sleep(10);
								}
								else{
									installcomplete=false;
									Message msg=new Message();
									msg.what=UPDATE;
									myhandler.sendMessage(msg);
							    	}
								
							}catch(Exception e){
								e.printStackTrace();
							}
							
						}
						button1.setEnabled(true);
					}
				}).start();
			}
		});
        
    }
    Handler myhandler=new Handler(){
    	public void handleMessage(android.os.Message msg) {
    		switch(msg.what){
    		case UPDATE:
			if(i<count){
				button1.setEnabled(false);
	    			progressbar1.setProgress((int)((double)(i+1)/(double)count*100));
	    			textview1.setText("安装进度:"+String.valueOf((int)((double)(i+1)/(double)count*100))+"%");
	    			textview2.setText("当前安装文件为:"+apksearchutils.getMyFiles().get(i).getFilePath());
	    			Log.e("zkliu",R.string.textview2text+apksearchutils.getMyFiles().get(i).getFilePath());

				Uri uri = Uri.fromFile(new File(apksearchutils.getMyFiles().get(i).getFilePath()));
				int installFlags = 0;
				PackageManager pm = getPackageManager();
				try {
					PackageInfo pi = pm.getPackageInfo(apksearchutils.getMyFiles().get(i).getPackageName(),PackageManager.GET_UNINSTALLED_PACKAGES);
					if(pi != null) 
					{
					    installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
					}
				} 
				catch (NameNotFoundException e) 
				{
				}   
				PackageInstallObserver observer = new PackageInstallObserver();
				pm.installPackage(uri, observer, installFlags,apksearchutils.getMyFiles().get(i).getPackageName());
    				i++;
			}
			else{
				threadrun=false;
				button1.setEnabled(true);
			}
    			break;
		case INSTALL_COMPLETED:
			installcomplete=true;
			break;
    		}
    	}
    };

    class PackageInstallObserver extends IPackageInstallObserver.Stub {
        public void packageInstalled(String packageName, int returnCode) {
            Message msg = myhandler.obtainMessage(INSTALL_COMPLETED);
            msg.arg1 = returnCode;
            myhandler.sendMessage(msg);

        }
    };


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    
}

     在编译android系统时编译该应用的指令如下所示:

root@l-desktop:~/A13/android4.0# source build/envsetup.sh
including device/samsung/maguro/vendorsetup.sh
including device/samsung/tuna/vendorsetup.sh
including device/softwinner/common/vendorsetup.sh
including device/softwinner/nuclear-evb_mmc/vendorsetup.sh
including device/softwinner/nuclear-evb/vendorsetup.sh
including device/softwinner/nuclear-mini/vendorsetup.sh
including device/ti/panda/vendorsetup.sh
including sdk/bash_completion/adb.bash
root@l-desktop:~/A13/android4.0# cd device/softwinner/common/packages/updatepackage/
root@l-desktop:~/A13/android4.0/device/softwinner/common/packages/updatepackage# mmm ./
find: `device/softwinner/nuclear-evb/apkwithlib': 没有那个文件或目录
find: `device/softwinner/nuclear-evb/apkwithlib/lib': 没有那个文件或目录
============================================
PLATFORM_VERSION_CODENAME=REL
PLATFORM_VERSION=4.0.4
TARGET_PRODUCT=full
TARGET_BUILD_VARIANT=eng
TARGET_BUILD_TYPE=release
TARGET_BUILD_APPS=
TARGET_ARCH=arm
TARGET_ARCH_VARIANT=armv7-a
HOST_ARCH=x86
HOST_OS=linux
HOST_BUILD_TYPE=release
BUILD_ID=IMM76D
============================================
make:进入目录'/root/A13/android4.0'
target R.java/Manifest.java: MainActivity (out/target/common/obj/APPS/MainActivity_intermediates/src/R.stamp)
Warning: AndroidManifest.xml already defines versionCode (in http://schemas.android.com/apk/res/android); using existing value in manifest.
Warning: AndroidManifest.xml already defines versionName (in http://schemas.android.com/apk/res/android); using existing value in manifest.
Warning: AndroidManifest.xml already defines minSdkVersion (in http://schemas.android.com/apk/res/android); using existing value in manifest.
Warning: AndroidManifest.xml already defines targetSdkVersion (in http://schemas.android.com/apk/res/android); using existing value in manifest.
target Java: MainActivity (out/target/common/obj/APPS/MainActivity_intermediates/classes)
Copying: out/target/common/obj/APPS/MainActivity_intermediates/classes-jarjar.jar
Copying: out/target/common/obj/APPS/MainActivity_intermediates/emma_out/lib/classes-jarjar.jar
Copying: out/target/common/obj/APPS/MainActivity_intermediates/classes.jar
Copying: out/target/common/obj/APPS/MainActivity_intermediates/noproguard.classes.jar
target Dex: MainActivity
Copying: out/target/common/obj/APPS/MainActivity_intermediates/noproguard.classes.dex
target Package: MainActivity (out/target/product/generic/obj/APPS/MainActivity_intermediates/package.apk)
Warning: AndroidManifest.xml already defines versionCode (in http://schemas.android.com/apk/res/android); using existing value in manifest.
Warning: AndroidManifest.xml already defines versionName (in http://schemas.android.com/apk/res/android); using existing value in manifest.
Warning: AndroidManifest.xml already defines minSdkVersion (in http://schemas.android.com/apk/res/android); using existing value in manifest.
Warning: AndroidManifest.xml already defines targetSdkVersion (in http://schemas.android.com/apk/res/android); using existing value in manifest.
 'out/target/common/obj/APPS/MainActivity_intermediates/classes.dex' as 'classes.dex'...
Install: out/target/product/generic/system/app/MainActivity.apk
make:离开目录“/root/A13/android4.0”

    如果需要完整工程,可以发邮件到:229425962@qq.com索取。

 

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值