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

84

被折叠的 条评论
为什么被折叠?



