设备厂商总是会预装很多apk(更奇葩的居然还有病毒,我是不喜欢这种预装的东西,挺讨厌的)
如果把所有apk放到system/app下,也不太现实.比较system分区容量是有限的.
如果把所有apk扔到data/app下.第一次开机时间太久,估计在4-6分钟的样子.显然无法接受.
所以这个安装应运而生,提供了两种方式
1.需要预装的apk都打包到zip文件里
2.需要预装的apk都放到指定文件夹
代码大体结构是这样:
主要安装代码在thread和service里
service里的代码:
public class Ins3rdAppInstallService extends Service {
private PackageManager pManager = null;
private IPackageManager ipManager = null;
private int installLocationInIpm = Ins3rdAppConst.ZERO;
private Ins3rdAppBinder myBinder = new Ins3rdAppBinder();
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
// pManager.installPackage(arg0, arg1, arg2, arg3);
return myBinder;
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
pManager = getPackageManager();
ipManager = IPackageManager.Stub.asInterface(ServiceManager
.getService(Ins3rdAppConst.PACKAGE_SERVICE));
try {
installLocationInIpm = ipManager.getInstallLocation();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void InstallApkInBackGround(Uri uri, String packagename,
int location, PackageInstallObserver observer) {
Log.d(Ins3rdAppConst.LOG_TAG, "install " + uri.toString());
int flags = Ins3rdAppConst.ZERO;
flags = ParseInstallLocation(location, flags);
pManager.installPackage(uri, observer, flags, packagename);
}
public void InstallApkInBackGround(Uri uri, String packagename,
int location, PackageInstallObserverWithDir observer) {
Log.d(Ins3rdAppConst.LOG_TAG, "install " + uri.toString());
int flags = Ins3rdAppConst.ZERO;
flags = ParseInstallLocation(location, flags);
pManager.installPackage(uri, observer, flags, packagename);
}
public class Ins3rdAppBinder extends Binder {
public Ins3rdAppInstallService getService() {
return Ins3rdAppInstallService.this;
}
}
private int ParseInstallLocation(int locationInPkg, int installflag) {
switch (locationInPkg) {
case Ins3rdAppConst.INSTALL_LOCATION_AUTO:
installflag |= PackageManager.INSTALL_INTERNAL_SD;
break;
case Ins3rdAppConst.INSTALL_LOCATION_INTERNAL_ONLY:
installflag |= PackageManager.INSTALL_INTERNAL;
break;
case Ins3rdAppConst.INSTALL_LOCATION_PREFER_EXTERNAL:
installflag |= PackageManager.INSTALL_INTERNAL_SD;
break;
case Ins3rdAppConst.INSTALL_LOCATION_UNSPECIFIED:
if (installLocationInIpm == PackageHelper.APP_INSTALL_EXTERNAL) {
installflag |= PackageManager.INSTALL_INTERNAL_SD;
} else {
installflag |= PackageManager.INSTALL_INTERNAL;
}
break;
default:
installflag |= PackageManager.INSTALL_INTERNAL;
break;
}
return installflag;
}
}
thread里的代码:
public class Ins3rdAppParseThread extends HandlerThread implements Callback {
private static final int INIT_DATABASE = 1;
private static final int INSTALL_APK = 2;
private static final int APK_NOT_INSTALL = 0;
private static final int APK_HAS_INSTALL = 1;
private static final String KEY_APK_NAME = "apkname";
private static final String PACKAGE_NAME = "package";
private static final String INSTALL_LOCATION = "location";
private static final String RETRY_TIMES = "retry";
private static final String END_TAG = "smartictag";
private static final String UNKOWN_TAG = "unknown";
private static final int SUCCEEDED = 1;
private static final int RETRY_INTERVAL = 1;
private static final int RETRY_MAX_TIMES = 3;
private static final int COMPLETEPROGRESS = 100;
// private static final int FAILED = 0;
private Handler currentHandler;
private Context mContext;
private ZipFile tmp;
private Ins3rdAppDbServer dbServer;
private Ins3rdAppZipUtil appZipUtil;
private Ins3rdAppBinder binder;
private Ins3rdAppInstallService bindService;
private PackageInstallObserver installObserver;
private LockObject lock;
private String apkName;
private String packageName;
private int retrytimes;
private Ins3rdAppNotification notification;
private int apkcount;
private int currentprogress;
private ServiceConnection connection = new ServiceConnection() {
public void onServiceDisconnected(ComponentName name) {
// TODO Auto-generated method stub
binder = null;
bindService = null;
}
public void onServiceConnected(ComponentName name, IBinder service) {
// TODO Auto-generated method stub
binder = (Ins3rdAppBinder) service;
bindService = binder.getService();
}
};
public class LockObject {
public void PrintLockLog() {
Log.d(Ins3rdAppConst.LOG_TAG,
"Lock the LockObject and installthread wait for notify");
}
public void PrintUnlockLog() {
Log.d(Ins3rdAppConst.LOG_TAG,
"Unlock the LockObject and installthread resume");
}
}
public class PackageInstallObserver extends IPackageInstallObserver.Stub {
public void packageInstalled(String packageName, int retCode)
throws RemoteException {
// TODO Auto-generated method stub
Log.d(Ins3rdAppConst.LOG_TAG, "package " + packageName
+ "install complete and ret " + retCode);
if (retCode == SUCCEEDED) {
Log.d(Ins3rdAppConst.LOG_TAG, "package " + packageName
+ "install success");
dbServer.update(apkName, APK_HAS_INSTALL);
} else {
Log.d(Ins3rdAppConst.LOG_TAG, "package " + packageName
+ "install failed retry times "
+ (retrytimes + RETRY_INTERVAL));
dbServer.update(apkName, APK_NOT_INSTALL, retrytimes
+ RETRY_INTERVAL);
}
appZipUtil.DeleteTmpApkFile(apkName);
currentprogress += (COMPLETEPROGRESS / apkcount);
notification.update(currentprogress);
synchronized (lock) {
lock.PrintUnlockLog();
lock.notify();
}
}
}
public Ins3rdAppParseThread(String name, Context context) {
super(name);
this.mContext = context;
this.lock = new LockObject();
dbServer = new Ins3rdAppDbServer(this.mContext);
appZipUtil = new Ins3rdAppZipUtil();
tmp = appZipUtil.OpenZipFile(Environment
.getExternalSDStorageDirectory().getPath()
+ "/"
+ Ins3rdAppConst.ZIP_NAME);
if (tmp == null) {
ExitProcess();
}
installObserver = new PackageInstallObserver();
notification = Ins3rdAppNotification.getInstance(context);
// TODO Auto-generated constructor stub
}
public boolean handleMessage(Message msg) {
// TODO Auto-generated method stub
if (currentHandler == null) {
Log.d(Ins3rdAppConst.LOG_TAG, "ParseThread running");
currentHandler = new Handler(getLooper(), this);
}
switch (msg.what) {
case INIT_DATABASE:
InitDataBase();
break;
case INSTALL_APK:
BeginInstallApk();
break;
default:
break;
}
return false;
}
public void InitDataBase() {
// File file1 = new File(Environment.getExternalStorageDirectory()
// .getPath() + "/" + "junfa.apk");
// ApplicationInfo info = GetApkInfo(file1);
if (dbServer.queryapk(END_TAG)) {
Log.d(Ins3rdAppConst.LOG_TAG,
"all the install.list entry has add into database");
currentHandler.sendEmptyMessage(INSTALL_APK);
return;
}
appZipUtil.ParseInstallList(appZipUtil.ExactInstallList());
Log.d(Ins3rdAppConst.LOG_TAG, "keyset " + appZipUtil.apkMap.keySet());
Iterator<Entry<String, String>> iterator = appZipUtil.apkMap.entrySet()
.iterator();
while (iterator.hasNext()) {
Entry<String, String> entry = iterator.next();
appZipUtil.apkMap.put(entry.getKey(), UNKOWN_TAG);
Ins3rdAppItem item = new Ins3rdAppItem(entry.getKey(),
entry.getValue(), false);
dbServer.insert(item);
}
dbServer.putendtag();
currentHandler.sendEmptyMessage(INSTALL_APK);
}
public void BeginInstallApk() {
currentThread().setPriority(Process.THREAD_PRIORITY_BACKGROUND);
currentprogress = Ins3rdAppConst.ZERO;
Cursor cursor = dbServer.queryinstallstate(APK_NOT_INSTALL);
Log.d(Ins3rdAppConst.LOG_TAG,
"now start install in ins3rdappinstallservice");
apkcount = cursor.getCount();
if (apkcount != Ins3rdAppConst.ZERO) {
notification.start();
}
File apkfile;
ApplicationInfo applicationInfo;
Intent intent = new Intent(mContext, Ins3rdAppInstallService.class);
mContext.bindService(intent, connection, Service.BIND_AUTO_CREATE);
while (cursor.moveToNext()) {
apkName = cursor.getString(cursor.getColumnIndex(KEY_APK_NAME));
packageName = cursor.getString(cursor.getColumnIndex(PACKAGE_NAME));
retrytimes = cursor.getShort(cursor.getColumnIndex(RETRY_TIMES));
int location = cursor.getShort(cursor
.getColumnIndex(INSTALL_LOCATION));
if (retrytimes >= RETRY_MAX_TIMES) {
Log.d(Ins3rdAppConst.LOG_TAG, apkName + " has install "
+ retrytimes + ", beyond max retry times");
continue;
}
apkfile = appZipUtil.ExtractApk(apkName);
if (apkfile == null) {
continue;
}
if (packageName.compareTo(UNKOWN_TAG) == 0) {
applicationInfo = GetApkInfo(apkfile);
Ins3rdAppItem item = new Ins3rdAppItem(apkName,
applicationInfo.packageName, false,
applicationInfo.installLocation);
location = applicationInfo.installLocation;
dbServer.update(item);
}
if (IsApkInstalled(packageName)) {
Log.d(Ins3rdAppConst.LOG_TAG, "apk " + apkName + " package "
+ packageName + " has installed");
} else {
bindService.InstallApkInBackGround(Uri.fromFile(apkfile),
packageName, location, installObserver);
try {
synchronized (lock) {
lock.PrintLockLog();
lock.wait();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
if (apkcount != Ins3rdAppConst.ZERO) {
notification.stop();
}
if (bindService != null) {
mContext.unbindService(connection);
}
ExitProcess();
}
public ApplicationInfo GetApkInfo(File file) {
Uri uri = Uri.fromFile(file);
return Ins3rdAppPackageUtil.getApplicationInfo(uri);
}
public Boolean IsApkInstalled(String packageString) {
PackageManager pm = mContext.getPackageManager();
try {
PackageInfo pi = pm.getPackageInfo(packageString,
PackageManager.GET_UNINSTALLED_PACKAGES);
if (pi != null) {
return true;
}
return false;
} catch (NameNotFoundException e) {
return false;
}
}
public void ExitProcess() {
Log.d(Ins3rdAppConst.LOG_TAG,
"Process Kill myslef " + mContext.getPackageName());
dbServer.closedb();
Process.killProcess(Process.myPid());
}
}
与service的交互是通过bind的方式来做的,感觉比onstart方便,好用.
安装线程类里有一个observer的回调
函数里主要处理数据库,做了同步.目的是为了apk能串行化安装.不运行并发.