61阅读

android应用程序-Android应用程序 --- WakeLock 保持后台唤醒状态

发布时间:2018-01-29 所属栏目:Android

一 : Android应用程序 --- WakeLock 保持后台唤醒状态

一些手机app(如微信、QQ等)有新消息来到达,手机屏幕即使在锁屏状态下也会亮起,并提示用户有新消息。但是,一般情况下手机锁屏后,Android系统为了省电以及减少CPU消耗,在一段时间后会使系统进入休眠状态,这时,Android系统中CPU会保持在一个相对较低的功耗状态,而收到新消息必定有网络请求,而网络请求是消耗CPU的操作,那么如何在锁屏状态乃至系统进入休眠后,仍然保持系统的网络状态以及通过程序唤醒手机呢?答案就是Android中的WakeLock机制。

官方对于WakeLock的解释:
PowerManager:This class gives you control of the power state of the device.
PowerManager.WakeLock: lets you say that you need to have the device on.

Android 系统支持应用程序及服务在待机前保存程序运行状态,如待机前关闭文件读写、usb 操作、暂停音乐播放;也支持唤醒后的程序状态恢复,如恢复打开文件进行读写操作,恢复 usb 操作、恢复音乐播放等。这些状态的保存和恢复功能可以保证系统在待机唤醒后能正常工作。

主要提供两种方式:

1、待机广播消息和唤醒广播消息。
2、Wakelock 锁机制。

分为两个部分说明一下:

1、android 系统待机处理机制

待机广播消息和唤醒广播消息
系统在 PowerManagerService 类中注册了 2 个广播分别用于待机前和唤醒后发送。

void initInThread(){
//唤醒后:
mScreenOnIntent=newIntent(Intent.ACTION_SCREEN_ON);//唤醒后发送
mScreenOnIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
//待机前:
mScreenOffIntent=newIntent(Intent.ACTION_SCREEN_OFF);//待机时发送
mScreenOffIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
}

这里顺带说明一下广播接收的优先级问题:
接收者按照在 Manifest.xml 文件中设置的接收顺序依次接收Intent,顺序执行的,接收的优先级可以在系统配置文件中设置:
声明在intent-filter元素的android:priority 属性中,数值越大优先级别越高,其取值范围为-1000到1000。当然也可以在调用IntentFilter对象的setPriority()方法进行设置

Wakelock 锁机制:
应用程序可以通过申请 wakelock 锁的机制来对系统是否待机作出投票,当有任何一个应用申请了 wakelock 锁,待机时没有释放掉,系统是不会进入待机的,直到所有应用的 wakelock 锁都释放掉了,才会进入待机。

2、应用程序使用方法:

实例代码:

[java] view plaincopyprint?
<SPAN> private WakeLock wakeLock = null;

/**
* 获取电源锁,保持该服务在屏幕熄灭时仍然获取CPU时,保持运行
*/
private void acquireWakeLock() {
if (null == wakeLock) {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
| PowerManager.ON_AFTER_RELEASE, getClass()
.getCanonicalName());
if (null != wakeLock) {
Log.i(TAG, "call acquireWakeLock");
wakeLock.acquire();
}
}
}

// 释放设备电源锁
private void releaseWakeLock() {
if (null != wakeLock && wakeLock.isHeld()) {
Log.i(TAG, "call releaseWakeLock");
wakeLock.release();
wakeLock = null;
}
}</SPAN>

private WakeLock wakeLock = null;

/**
* 获取电源锁,保持该服务在屏幕熄灭时仍然获取CPU时,保持运行
*/
private void acquireWakeLock() {
if (null == wakeLock) {
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK
| PowerManager.ON_AFTER_RELEASE, getClass()
.getCanonicalName());
if (null != wakeLock) {
Log.i(TAG, "call acquireWakeLock");
wakeLock.acquire();
}
}
}

// 释放设备电源锁
private void releaseWakeLock() {
if (null != wakeLock && wakeLock.isHeld()) {
Log.i(TAG, "call releaseWakeLock");
wakeLock.release();
wakeLock = null;
}
}
WakeLock 类型以及说明:

PARTIAL_WAKE_LOCK:保持CPU 运转,屏幕和键盘灯有可能是关闭的。
SCREEN_DIM_WAKE_LOCK:保持CPU 运转,允许保持屏幕显示但有可能是灰的,允许关闭键盘灯
SCREEN_BRIGHT_WAKE_LOCK:保持CPU 运转,允许保持屏幕高亮显示,允许关闭键盘灯
FULL_WAKE_LOCK:保持CPU 运转,保持屏幕高亮显示,键盘灯也保持亮度
ACQUIRE_CAUSES_WAKEUP:强制使屏幕亮起,这种锁主要针对一些必须通知用户的操作.
ON_AFTER_RELEASE:当锁被释放时,保持屏幕亮起一段时间

最后 AndroidManifest.xml 声明权限:
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.DEVICE_POWER"/>

应用程序中如果要在待机前保存数据状态的话,要保证此过程中不会进入待机。可以在 onResume() 或者 onStart() 中申请 wakelock 锁,即调用acquireWakeLock()方法。

在 onPause() 或者 onDistroy() 中处理应用待机后再释放掉 wakelock 锁,此时调用releaseWakeLock()方法

最后一点需要注意下:

另外WakeLock的设置是 Activiy 级别的,不是针对整个Application应用的。所以application下有多个activity一定需要注意下!

扩展:wakelock / android wakelock / wakelock detector

二 : 如何在真机上调试Android应用程序(图文详解)

三 : Android应用程序安装过程浅析

我们知道在android中,安装应用是由PackageManager来管理的,但是我们发现PackageManager是一个抽象类,他的installPackage方法也没有具体的实现。那在安装过程中是怎么执行的呐?

调用方[www.61k.com]

查看代码可以知道ApplicationPackageManager是直接继承自PackageManager的,所以最终代码会调用ApplicationPackageManager下的installPackage(Uri packageURI, IPackageInstallObserver observer, int flags,String installerPackageName),而在installPackage里面又调用了installCommon。

installCommon的实现如下:

private void installCommon(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { if (!"file".equals(packageURI.getScheme())) { throw new UnsupportedOperationException("Only file:// URIs are supported"); } if (encryptionParams != null) { throw new UnsupportedOperationException("ContainerEncryptionParams not supported"); } final String originPath = packageURI.getPath(); try { mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName, verificationParams, null); } catch (RemoteException ignored) { } }

可以看到在installCommon最终调用了mPm.installPackage那mPm又是什么?可以发现mPM是一个IPackageManager,他在是ApplicationPackageManager的构造函数中传入的,那是什么时候调用构造函数的呐?在ContextImpl中调用getPackageManager时会进行调用,传入的pm在是ActivityThread中创建的。

@Override public PackageManager getPackageManager() { if (mPackageManager != null) { return mPackageManager; } IPackageManager pm = ActivityThread.getPackageManager(); if (pm != null) { // Doesn&#39;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; }

从上面可以看到IPackageManager是进程间通信的客户端, 首先是IPackageManager是通过IPackageManager.aidl文件生成,同时生成了存根类IPackageManager.Stub,代理类:IPackageManager.Stub.Proxy,他是IBinder类型,那远端又是谁呐?远端就是PackageManagerService,PackageManagerService继承自IPackageManager.Stub,因此最终的调用都是通过aidl由PackageManagerService执行。因此我们主要来看看PackageManagerService中的执行过程。

安装方式

主要有两种方式:
1:系统启动后扫描安装,会调用PackageManagerService的scanPackageLI函数,
2:应用市场安装,应用市场下载后会默认调用PackageManagerService的intallPackage函数,改函数最终也会调用到scanPackageLI,因此只需要分享第二种

流程图

我们可以大致看看代码调用的过程,流程图如下:
android 软件安装 Android应用程序安装过程浅析

执行过程

由于PackageManager最终是由PackageManagerService来执行的

@Overridepublic void installPackage(String originPath, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride) { installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams, packageAbiOverride, UserHandle.getCallingUserId());}

主要传递了6个参数:

1,originPath,安装包的位置,他必须是file类型活在content的URI类型。传递这里的是一个string类型。

2,observer,是一个IPackageInstallObserver类型,回调通知调用者安装完成

3,installFlags,他的值是INSTALL_FORWARD_LOCK,INSTALL_REPLACE_EXISTING,INSTALL_ALLOW_TEST三个中的一个,INSTALL_FORWARD_LOCK表示安装过程中是否锁定,INSTALL_REPLACE_EXISTING表示是否替换安装包,INSTALL_ALLOW_TEST是否测试安装包,如果有改标志,manifest必须配置android:testOnly

4,installerPackageName,安装包包名

5,verificationParams,代表验证参数用于验证包安装。

6,packageAbiOverride,一般传null

该函数调用了installPackageAsUser函数,installPackageAsUser函数如下:

@Overridepublic void installPackageAsUser(String originPath, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null); final int callingUid = Binder.getCallingUid(); enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser"); if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) { try { if (observer != null) { observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null); } } catch (RemoteException re) { } return; } if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { installFlags |= PackageManager.INSTALL_FROM_ADB; } else { // Caller holds INSTALL_PACKAGES permission, so we&#39;re less strict // about installerPackageName. installFlags &= ~PackageManager.INSTALL_FROM_ADB; installFlags &= ~PackageManager.INSTALL_ALL_USERS; } UserHandle user; if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) { user = UserHandle.ALL; } else { user = new UserHandle(userId); } // Only system components can circumvent runtime permissions when installing. if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0 && mContext.checkCallingOrSelfPermission(Manifest.permission .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) { throw new SecurityException("You need the " + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission " + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag"); } verificationParams.setInstallerUid(callingUid); final File originFile = new File(originPath); final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile); final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName, null, verificationParams, user, packageAbiOverride, null); mHandler.sendMessage(msg);}

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

该函数主要做了以下操作,第一强制获取权限,如果被拒绝则退出执行,接着设置installFlags参数,最后一步发送了一个what为INIT_COPY的message,参数为InstallParams,记住该参数,后面还会多次用到,看what的名称INIT_COPY,看起来是表达初始化并且拷贝,那是不是真是这样呐?我们去看看这个操作,这个操做是PackageHandler来执行的,PackageHandler继续字Handler,那我们来具体看看执行代码:

case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; int idx = mPendingInstalls.size(); if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params); // If a bind was already initiated we dont really // need to do anything. The pending install // will be processed later on. if (!mBound) { // If this is the only one pending we might // have to bind to the service again. if (!connectToService()) { Slog.e(TAG, "Failed to bind to media container service"); params.serviceError(); return; } else { // Once we bind to the service, the first // pending request will be processed. mPendingInstalls.add(idx, params); } } else { mPendingInstalls.add(idx, params); // Already bound to the service. Just make // sure we trigger off processing the first request. if (idx == 0) { mHandler.sendEmptyMessage(MCS_BOUND); } } break;}

可以看到首先取出参数params,这个params就是之前传入的InstallParams,接着获取等待安装队列的内容个数,由于初始mBound为false,因此会进入该判断,之后执行了connectToService函数,如个返回false表示连接失败,直接行使params的serviceError函数来结束当前执行,如果为这将paramsa添加到mPendingInstalls的最后一个位置,connectToService连接又是什么呐?当前代码也没有执行任何与copy有段的操作啊?那我们去看看connectToService究竟干了什么?

private boolean connectToService() { if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" + " DefaultContainerService"); Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mBound = true; return true; } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return false;}

可以看到这里bind到了一个service,service的名称是DefaultContainerService,这又是个什么service呐?并且在绑定之前先设置该进程的优先级为THREAD_PRIORITY_DEFAULT,执行完成后再次设置为THREAD_PRIORITY_BACKGROUND,这里我们也没有看到有任何copy的操作,那copy操作究竟在什么地方,绑定的这个服务又是什么?我们来看看绑定的connection参数:

class DefaultContainerConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected"); IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service); mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); } public void onServiceDisconnected(ComponentName name) { if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected"); }}

可以看到当绑定成功后将一个service转换成了一个IMediaContainerService,这个又是什么呐?这个就是在onServiceConnected回调函数中根据参数传进来的IMediaContainerService.Stub的对象引用创建一个远程代理对象。以后PackageManagerService服务通过该代理对象访问DefaultContainerService服务。DefaultContainerService是一个应用服务,具体负责实现APK等相关资源文件在内部或外部存储器上的存储工作,DefaultContainerService服务中提供了一个IMediaContainerService.Stub桩对象。

接下来我们看到这里又发送了一个what为MCS_BOUND的message,参数为之前获得的IMediaContainerService,这里也没有任何copy操作,那我们继续跟进看看该what执行了什么?

case MCS_BOUND: { if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound"); if (msg.obj != null) { mContainerService = (IMediaContainerService) msg.obj; } if (mContainerService == null) { if (!mBound) { // Something seriously wrong since we are not bound and we are not // waiting for connection. Bail out. Slog.e(TAG, "Cannot bind to media container service"); for (HandlerParams params : mPendingInstalls) { // Indicate service bind error params.serviceError(); } mPendingInstalls.clear(); } else { Slog.w(TAG, "Waiting to connect to media container service"); } } else if (mPendingInstalls.size() > 0) { HandlerParams params = mPendingInstalls.get(0); if (params != null) { if (params.startCopy()) { // We are done... look for more work or to // go idle. if (DEBUG_SD_INSTALL) Log.i(TAG, "Checking for more work or unbind..."); // Delete pending install if (mPendingInstalls.size() > 0) { mPendingInstalls.remove(0); } if (mPendingInstalls.size() == 0) { if (mBound) { if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting delayed MCS_UNBIND"); removeMessages(MCS_UNBIND); Message ubmsg = obtainMessage(MCS_UNBIND); // Unbind after a little delay, to avoid // continual thrashing. sendMessageDelayed(ubmsg, 10000); } } else { // There are more pending requests in queue. // Just post MCS_BOUND message to trigger processing // of next pending install. if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting MCS_BOUND for next work"); mHandler.sendEmptyMessage(MCS_BOUND); } } } } else { // Should never happen ideally. Slog.w(TAG, "Empty queue"); } break;}

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

可以看到这里首先获取了传入的参数,如果参数为空,则调用HandlerParams的serviceError,并且清空mPendingInstalls列表,否则获取到等待安装列表中的第一个对象,就是我们最初始添加进的InstallParams,这里我们看到调用了InstallParams的startCopy函数,执行完成后移除该参数,如果等待安装列表为空且当前绑定状态为true,则发一个what为MCS_UNBIND的解绑操作,否则就继续执行该操作,将等待列表中的一个一个执行,MCS_UNBIND与MCS_RECONNECT,这就不详细说了,MCS_UNBIND主要是解绑之前的链接,MCS_RECONNECT是重新绑定链接,那我们继续看看startCopy函数:

final boolean startCopy() { boolean res; try { if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this); if (++mRetries > MAX_RETRIES) { Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); mHandler.sendEmptyMessage(MCS_GIVE_UP); handleServiceError(); return false; } else { handleStartCopy(); res = true; } } catch (RemoteException e) { if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT"); mHandler.sendEmptyMessage(MCS_RECONNECT); res = false; } handleReturnCode(); return res;}

这里会重试多次,如果超过最大次数则发送一个what为MCS_GIVE_UP的message表示安装失败,否则调用handleStartCopy,我们来看看handleStartCopy,这里调用的是InstallParams的handleStartCopy函数:

/* * Invoke remote method to get package information and install * location values. Override install location based on default * policy if needed and then create install arguments based * on the install location. */public void handleStartCopy() throws RemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; // If we&#39;re already staged, we&#39;ve firmly committed to an install location if (origin.staged) { if (origin.file != null) { installFlags |= PackageManager.INSTALL_INTERNAL; installFlags &= ~PackageManager.INSTALL_EXTERNAL; } else if (origin.cid != null) { installFlags |= PackageManager.INSTALL_EXTERNAL; installFlags &= ~PackageManager.INSTALL_INTERNAL; } else { throw new IllegalStateException("Invalid stage location"); } } final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0; final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0; PackageInfoLite pkgLite = null; if (onInt && onSd) { // Check if both bits are set. Slog.w(TAG, "Conflicting flags specified for installing on both internal and external"); ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION; } else { pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride); /* * If we have too little free space, try to free cache * before giving up. */ if (!origin.staged && pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) { // TODO: focus freeing disk space on the target device final StorageManager storage = StorageManager.from(mContext); final long lowThreshold = storage.getStorageLowBytes( Environment.getDataDirectory()); final long sizeBytes = mContainerService.calculateInstalledSize( origin.resolvedPath, isForwardLocked(), packageAbiOverride); if (mInstaller.freeCache(null, sizeBytes + lowThreshold) >= 0) { pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride); } /* * The cache free must have deleted the file we * downloaded to install. * * TODO: fix the "freeCache" call to not delete * the file we care about. */ if (pkgLite.recommendedInstallLocation == PackageHelper.RECOMMEND_FAILED_INVALID_URI) { pkgLite.recommendedInstallLocation = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE; } } } // 设置ret final InstallArgs args = createInstallArgs(this); mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { /* * ADB installs appear as UserHandle.USER_ALL, and can only be performed by * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER. */ int userIdentifier = getUser().getIdentifier(); if (userIdentifier == UserHandle.USER_ALL && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) { userIdentifier = UserHandle.USER_OWNER; } /* * Determine if we have any installed package verifiers. If we * do, then we&#39;ll defer to them to verify the packages. */ final int requiredUid = mRequiredVerifierPackage == null ? -1 : getPackageUid(mRequiredVerifierPackage, userIdentifier); if (!origin.existing && requiredUid != -1 && isVerificationEnabled(userIdentifier, installFlags)) { final Intent verification = new Intent( Intent.ACTION_PACKAGE_NEEDS_VERIFICATION); verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND); verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)), PACKAGE_MIME_TYPE); verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); final List

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

receivers = queryIntentReceivers(verification, PACKAGE_MIME_TYPE, PackageManager.GET_DISABLED_COMPONENTS, 0 /* TODO: Which userId? */); if (DEBUG_VERIFY) { Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent " + verification.toString() + " with " + pkgLite.verifiers.length + " optional verifiers"); } final int verificationId = mPendingVerificationToken++; verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);、 ....... 设置verification的参数 final PackageVerificationState verificationState = new PackageVerificationState( requiredUid, args); mPendingVerification.append(verificationId, verificationState); final ListsufficientVerifiers = matchVerifiers(pkgLite, receivers, verificationState); // Apps installed for "all" users use the device owner to verify the app UserHandle verifierUser = getUser(); if (verifierUser == UserHandle.ALL) { verifierUser = UserHandle.OWNER; } /* * If any sufficient verifiers were listed in the package * manifest, attempt to ask them. */ if (sufficientVerifiers != null) { final int N = sufficientVerifiers.size(); if (N == 0) { Slog.i(TAG, "Additional verifiers required, but none installed."); ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; } else { for (int i = 0; i < N; i++) { final ComponentName verifierComponent = sufficientVerifiers.get(i); final Intent sufficientIntent = new Intent(verification); sufficientIntent.setComponent(verifierComponent); mContext.sendBroadcastAsUser(sufficientIntent, verifierUser); } } } final ComponentName requiredVerifierComponent = matchComponentForVerifier( mRequiredVerifierPackage, receivers); if (ret == PackageManager.INSTALL_SUCCEEDED && mRequiredVerifierPackage != null) { /* * Send the intent to the required verification agent, * but only start the verification timeout after the * target BroadcastReceivers have run. */ verification.setComponent(requiredVerifierComponent); mContext.sendOrderedBroadcastAsUser(verification, verifierUser, android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final Message msg = mHandler .obtainMessage(CHECK_PENDING_VERIFICATION); msg.arg1 = verificationId; mHandler.sendMessageDelayed(msg, getVerificationTimeout()); } }, null, 0, null, null); /* * We don&#39;t want the copy to proceed until verification * succeeds, so null out this field. */ mArgs = null; } } else { /* * No package verification is enabled, so immediately start * the remote call to initiate copy using temporary file. */ ret = args.copyApk(mContainerService, true); } } mRet = ret;}@Overridevoid handleReturnCode() { // If mArgs is null, then MCS couldn&#39;t be reached. When it // reconnects, it will try again to install. At that point, this // will succeed. if (mArgs != null) { processPendingInstall(mArgs, mRet); }}

首先设置installFlags参数,之后设置ret参数与verification参数,之后根据参数创建了InstallArgs参数,根据参数这里实际返回的是AsecInstallArgs类型,如果该包需要被验证,则发送一个广播进行包验证,否则直接拷贝apk,广播的onReceive中发送了一个what为CHECK_PENDING_VERIFICATION的message,参数为verificationId,handleReturnCode中直接调用了processPendingInstall来进行安装处理,我们先来看看CHECK_PENDING_VERIFICATION的处理,这里最终也会调用到processPendingInstall。

case CHECK_PENDING_VERIFICATION: { final int verificationId = msg.arg1; final PackageVerificationState state = mPendingVerification.get(verificationId); if ((state != null) && !state.timeoutExtended()) { final InstallArgs args = state.getInstallArgs(); final Uri originUri = Uri.fromFile(args.origin.resolvedFile); Slog.i(TAG, "Verification timed out for " + originUri); mPendingVerification.remove(verificationId); int ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE; if (getDefaultVerificationResponse() == PackageManager.VERIFICATION_ALLOW) { Slog.i(TAG, "Continuing with installation of " + originUri); state.setVerifierResponse(Binder.getCallingUid(), PackageManager.VERIFICATION_ALLOW_WITHOUT_SUFFICIENT); broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_ALLOW, state.getInstallArgs().getUser()); try { ret = args.copyApk(mContainerService, true); } catch (RemoteException e) { Slog.e(TAG, "Could not contact the ContainerService"); } } else { broadcastPackageVerified(verificationId, originUri, PackageManager.VERIFICATION_REJECT, state.getInstallArgs().getUser()); } processPendingInstall(args, ret); mHandler.sendEmptyMessage(MCS_UNBIND); } break;}

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

验证完成后发送一个验证完成的广播,之后调用InstallArgs的copyApk函数拷贝包,这里的InstallArgs是什么类型呐?就是前面创建的AsecInstallArgs类型,因此执行的是AsecInstallArgs的copyApk函数,执行完成后调用processPendingInstall。copyApk主要是拷贝包到指定的目录下。这里就不详述了。接着看看processPendingInstall函数:

private void processPendingInstall(final InstallArgs args, final int currentStatus) { // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); // Result object to be returned PackageInstalledInfo res = new PackageInstalledInfo(); res.returnCode = currentStatus; res.uid = -1; res.pkg = null; res.removedInfo = new PackageRemovedInfo(); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { args.doPreInstall(res.returnCode); synchronized (mInstallLock) { installPackageLI(args, res); } args.doPostInstall(res.returnCode, res.uid); } // A restore should be performed at this point if (a) the install // succeeded, (b) the operation is not an update, and (c) the new // package has not opted out of backup participation. final boolean update = res.removedInfo.removedPackage != null; final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags; boolean doRestore = !update && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0); // Set up the post-install work request bookkeeping. This will be used // and cleaned up by the post-install event handling regardless of whether // there&#39;s a restore pass performed. Token values are >= 1. int token; if (mNextInstallToken < 0) mNextInstallToken = 1; token = mNextInstallToken++; PostInstallData data = new PostInstallData(args, res); mRunningInstalls.put(token, data); if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) { // Pass responsibility to the Backup Manager. It will perform a // restore if appropriate, then pass responsibility back to the // Package Manager to run the post-install observer callbacks // and broadcasts. IBackupManager bm = IBackupManager.Stub.asInterface( ServiceManager.getService(Context.BACKUP_SERVICE)); if (bm != null) { if (DEBUG_INSTALL) Log.v(TAG, "token " + token + " to BM for possible restore"); try { if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) { bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token); } else { doRestore = false; } } catch (RemoteException e) { // can&#39;t happen; the backup manager is local } catch (Exception e) { Slog.e(TAG, "Exception trying to enqueue restore", e); doRestore = false; } } else { Slog.e(TAG, "Backup Manager not found!"); doRestore = false; } } if (!doRestore) { // No restore possible, or the Backup Manager was mysteriously not // available -- just fire the post-install work request directly. if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token); Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0); mHandler.sendMessage(msg); } } });}

这里post一个Runnable来执行内部的逻辑,主要做了如下操作:
1,锁定后安装包,通过调用installPackageLI来进行的
2,接下来都是执行备份操作,备份是通过BackupManagerService来完成的。备份完成后,通过发送what为POST_INSTALL的message来继续处理
我们先来看看installPackageLI的执行过程:

private void installPackageLI(InstallArgs args, PackageInstalledInfo res) { //...... 初始化参数.... PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setDisplayMetrics(mMetrics); final PackageParser.Package pkg; try { pkg = pp.parsePackage(tmpPackageFile, parseFlags); } catch (PackageParserException e) { res.setError("Failed parse during installPackageLI", e); return; } ................. String oldCodePath = null; boolean systemApp = false; synchronized (mPackages) { // Check if installing already existing package if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { String oldName = mSettings.mRenamedPackages.get(pkgName); if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName) && mPackages.containsKey(oldName)) { // This package is derived from an original package, // and this device has been updating from that original // name. We must continue using the original name, so // rename the new package here. pkg.setPackageName(oldName); pkgName = pkg.packageName; replace = true; if (DEBUG_INSTALL) Slog.d(TAG, "Replacing existing renamed package: oldName=" + oldName + " pkgName=" + pkgName); } else if (mPackages.containsKey(pkgName)) { // This package, under its official name, already exists // on the device; we should replace it. replace = true; if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName); } // Prevent apps opting out from runtime permissions if (replace) { PackageParser.Package oldPackage = mPackages.get(pkgName); final int oldTargetSdk = oldPackage.applicationInfo.targetSdkVersion; final int newTargetSdk = pkg.applicationInfo.targetSdkVersion; if (oldTargetSdk > Build.VERSION_CODES.LOLLIPOP_MR1 && newTargetSdk <= Build.VERSION_CODES.LOLLIPOP_MR1) { res.setError(PackageManager.INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE, "Package " + pkg.packageName + " new target SDK " + newTargetSdk + " doesn&#39;t support runtime permissions but the old" + " target SDK " + oldTargetSdk + " does."); return; } } } ............... // Check whether the newly-scanned package wants to define an already-defined perm int N = pkg.permissions.size(); for (int i = N-1; i >= 0; i--) { PackageParser.Permission perm = pkg.permissions.get(i); BasePermission bp = mSettings.mPermissions.get(perm.info.name); if (bp != null) { // If the defining package is signed with our cert, it&#39;s okay. This // also includes the "updating the same package" case, of course. // "updating same package" could also involve key-rotation. final boolean sigsOk; if (bp.sourcePackage.equals(pkg.packageName) && (bp.packageSetting instanceof PackageSetting) && (shouldCheckUpgradeKeySetLP((PackageSetting) bp.packageSetting, scanFlags))) { sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg); } else { sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; } if (!sigsOk) { // If the owning package is the system itself, we log but allow // install to proceed; we fail the install on all other permission // redefinitions. if (!bp.sourcePackage.equals("android")) { res.setError(INSTALL_FAILED_DUPLICATE_PERMISSION, "Package " + pkg.packageName + " attempting to redeclare permission " + perm.info.name + " already owned by " + bp.sourcePackage); res.origPermission = perm.info.name; res.origPackage = bp.sourcePackage; return; } else { Slog.w(TAG, "Package " + pkg.packageName + " attempting to redeclare system permission " + perm.info.name + "; ignoring new declaration"); pkg.permissions.remove(i); } } } } } if (systemApp && onExternal) { // Disable updates to system apps on sdcard res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION, "Cannot install updates to system apps on sdcard"); return; } if (args.move != null) { // We did an in-place move, so dex is ready to roll scanFlags |= SCAN_NO_DEX; scanFlags |= SCAN_MOVE; synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps == null) { res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Missing settings for moved package " + pkgName); } // We moved the entire application as-is, so bring over the // previously derived ABI information. pkg.applicationInfo.primaryCpuAbi = ps.primaryCpuAbiString; pkg.applicationInfo.secondaryCpuAbi = ps.secondaryCpuAbiString; } } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) { // Enable SCAN_NO_DEX flag to skip dexopt at a later stage scanFlags |= SCAN_NO_DEX; try { derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride, true /* extract libs */); } catch (PackageManagerException pme) { Slog.e(TAG, "Error deriving application ABI", pme); res.setError(INSTALL_FAILED_INTERNAL_ERROR, "Error deriving application ABI"); return; } // Run dexopt before old package gets removed, to minimize time when app is unavailable int result = mPackageDexOptimizer .performDexOpt(pkg, null /* instruction sets */, false /* forceDex */, false /* defer */, false /* inclDependencies */); if (result == PackageDexOptimizer.DEX_OPT_FAILED) { res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath); return; } } if (!args.doRename(res.returnCode, pkg, oldCodePath)) { res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename"); return; } startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg); if (replace) { replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user, installerPackageName, volumeUuid, res); } else { installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, args.user, installerPackageName, volumeUuid, res); } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); } }}

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

主要是执行了以下操作:

1,初始一些参数 2,调用PackageParser来解析包3,判断是否是当前已有应用升级还是全新安装,这两个都需要坚持签名

4,检测权限,检测新扫描的权限是否是已经定义的权限 5,根据条件是否进行derivePackageAbi操作,这个操作的注释为Derive
the ABI of a non-system package located at {@code scanFile}. This
information is derived purely on the basis of the contents of {@code
scanFile} and{@code cpuAbiOverride}. 6,开始intent filter验证

7,根据是否是已有应用进行升级还是全新安装执行不同的操作

这里重要的主要是第2点和第7点,但是由于这里主要讲述android过程,因此对第2点不做详述,之后来详解该内容,我们假设这里是全新安装着调用installNewPackageLI:

/* * Install a non-existing package. */private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags, UserHandle user, String installerPackageName, String volumeUuid, PackageInstalledInfo res) { // Remember this for later, in case we need to rollback this install String pkgName = pkg.packageName; if (DEBUG_INSTALL) Slog.d(TAG, "installNewPackageLI: " + pkg); final boolean dataDirExists = Environment .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_OWNER, pkgName).exists(); ........... try { PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags, System.currentTimeMillis(), user); updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user); // delete the partially installed application. the data directory will have to be // restored if it was already existing if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) { // remove package from internal structures. Note that we want deletePackageX to // delete the package data and cache directories that it created in // scanPackageLocked, unless those directories existed before we even tried to // install. deletePackageLI(pkgName, UserHandle.ALL, false, null, null, dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0, res.removedInfo, true); } } catch (PackageManagerException e) { res.setError("Package couldn&#39;t be installed in " + pkg.codePath, e); }}

主要是调用了scanPackageLI来进行包的安装,之后调用了updateSettingsLI,updateSettingsLI主要是更新了包的PackageSetting对象,主要更新了权限信息与安装完成信息。这里我们继续查看scanPackageLI的执行:

private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { boolean success = false; try { final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags, currentTime, user); success = true; return res; } finally { if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) { removeDataDirsLI(pkg.volumeUuid, pkg.packageName); } }}

scanPackageLI主要调用了scanPackageDirtyLI,如果调用失败则调用removeDataDirsLI来移除安装信息,scanPackageDirtyLI的代码如下:

private PackageParser.Package scanPackageDirtyLI(PackageParser.Package pkg, int parseFlags, int scanFlags, long currentTime, UserHandle user) throws PackageManagerException { final File scanFile = new File(pkg.codePath); ............ if (pkg.packageName.equals("android")) { ............. } ................ // Initialize package source and resource directories File destCodeFile = new File(pkg.applicationInfo.getCodePath()); File destResourceFile = new File(pkg.applicationInfo.getResourcePath()); SharedUserSetting suid = null; PackageSetting pkgSetting = null; // writer synchronized (mPackages) { // Check if we are renaming from an original package name. PackageSetting origPackage = null; String realName = null; if (pkg.mOriginalPackages != null) { // This package may need to be renamed to a previously // installed name. Let&#39;s check on that... final String renamed = mSettings.mRenamedPackages.get(pkg.mRealPackage); if (pkg.mOriginalPackages.contains(renamed)) { // This package had originally been installed as the // original name, and we have already taken care of // transitioning to the new one. Just update the new // one to continue using the old name. realName = pkg.mRealPackage; if (!pkg.packageName.equals(renamed)) { // Callers into this function may have already taken // care of renaming the package; only do it here if // it is not already done. pkg.setPackageName(renamed); } } else { for (int i=pkg.mOriginalPackages.size()-1; i>=0; i--) { if ((origPackage = mSettings.peekPackageLPr( pkg.mOriginalPackages.get(i))) != null) { // We do have the package already installed under its // original name... should we use it? if (!verifyPackageUpdateLPr(origPackage, pkg)) { // New package is not compatible with original. origPackage = null; continue; } else if (origPackage.sharedUser != null) { // Make sure uid is compatible between packages. if (!origPackage.sharedUser.name.equals(pkg.mSharedUserId)) { Slog.w(TAG, "Unable to migrate data from " + origPackage.name + " to " + pkg.packageName + ": old uid " + origPackage.sharedUser.name + " differs from " + pkg.mSharedUserId); origPackage = null; continue; } } else { if (DEBUG_UPGRADE) Log.v(TAG, "Renaming new package " + pkg.packageName + " to old name " + origPackage.name); } break; } } } } ......... // Just create the setting, don&#39;t add it yet. For already existing packages // the PkgSetting exists already and doesn&#39;t have to be created. pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile, destResourceFile, pkg.applicationInfo.nativeLibraryRootDir, pkg.applicationInfo.primaryCpuAbi, pkg.applicationInfo.secondaryCpuAbi, pkg.applicationInfo.flags, pkg.applicationInfo.privateFlags, user, false); ............... if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { // Check all shared libraries and map to their actual file path. // We only do this here for apps not on a system dir, because those // are the only ones that can fail an install due to this. We // will take care of the system apps by updating all of their // library paths after the scan is done. updateSharedLibrariesLPw(pkg, null); } pkg.applicationInfo.uid = pkgSetting.appId; pkg.mExtras = pkgSetting; if (shouldCheckUpgradeKeySetLP(pkgSetting, scanFlags)) { if (checkUpgradeKeySetLP(pkgSetting, pkg)) { // We just determined the app is signed correctly, so bring // over the latest parsed certs. pkgSetting.signatures.mSignatures = pkg.mSignatures; } else { if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) { throw new PackageManagerException(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + pkg.packageName + " upgrade keys do not match the " + "previously installed version"); } else { pkgSetting.signatures.mSignatures = pkg.mSignatures; String msg = "System package " + pkg.packageName + " signature changed; retaining data."; reportSettingsProblem(Log.WARN, msg); } } } else { try { verifySignaturesLP(pkgSetting, pkg); // We just determined the app is signed correctly, so bring // over the latest parsed certs. pkgSetting.signatures.mSignatures = pkg.mSignatures; } catch (PackageManagerException e) { } } // Verify that this new package doesn&#39;t have any content providers // that conflict with existing packages. Only do this if the // package isn&#39;t already installed, since we don&#39;t want to break // things that are installed. if ((scanFlags & SCAN_NEW_INSTALL) != 0) { final int N = pkg.providers.size(); int i; for (i=0; i

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

这里主要做了以下操作:

1,检测路径代码路径是否存在不存在抛出异常

2,设置Package的applicationInfo信息

3,设置ResolverActivity信息

4,如果是系统程序则更改ResolverActivity信息

5,如果我们只安装以及存在的包,则判断他的PackageSetting信息,如果路径不一致,测抛出异常

6,初始化包的代码与资源目录

7,检测我们是否需要重命名一个原始包

8,检测所有共享的libraries并且映射到真实的路径

9,如个是升级包则检测签名,如果新安装包则验证签名

10,检测新包不含有与已经存在包冲突的provider

11,检测当前包对于其他包所拥有的权限

12,创建包data目录,并且重新调整uid,调用createDataDirsLI进行包的安装

13,设置包的本地的Library路径

14,创建包的用户数据,调用createUserData

15,对包进行opt操作,调用performDexOpt,最终调用的还是Install的dexopt函数

16,如果是已存在的包,则调用ActivityManager杀死该进程

17,解析包的provider,并添加到ProviderIntentResolver,解析包的service,并添加到ServiceIntentResolver,解析包的receiver,并添加到ActivityIntentResolver,解析包的activity,并添加到ActivityIntentResolver,解析包的权利组与权限。最后解析instrumentation这个是测试用的,上述的解析主要是为了在应用中调用getPackageManager().resolveActivity等方法使用的。

上面主要是调用了createDataDirsLI来进行包的安装:

private int createDataDirsLI(String volumeUuid, String packageName, int uid, String seinfo) { int[] users = sUserManager.getUserIds(); int res = mInstaller.install(volumeUuid, packageName, uid, uid, seinfo); if (res < 0) { return res; } for (int user : users) { if (user != 0) { res = mInstaller.createUserData(volumeUuid, packageName, UserHandle.getUid(user, uid), user, seinfo); if (res < 0) { return res; } } } return res;}

这里最终调用了mInstaller的intall函数,mInstaller是一个InstallerConnection,InstallerConnection里面是通过输入输出流与一个LocalSocket进行安装操作的,所以这里最终调用的InstallerConnection的intall函数,执行完成后如果user不为空,创建用户数据。

包的安装过程到此就结束了,我们再回头看看POST_INSTALL进行了什么操作?

case POST_INSTALL: { if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1); PostInstallData data = mRunningInstalls.get(msg.arg1); mRunningInstalls.delete(msg.arg1); boolean deleteOld = false; if (data != null) { InstallArgs args = data.args; PackageInstalledInfo res = data.res; if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { final String packageName = res.pkg.applicationInfo.packageName; res.removedInfo.sendBroadcast(false, true, false); Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, res.uid); // Now that we successfully installed the package, grant runtime // permissions if requested before broadcasting the install. if ((args.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) { grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(), args.installGrantPermissions); } // Determine the set of users who are adding this // package for the first time vs. those who are seeing // an update. int[] firstUsers; int[] updateUsers = new int[0]; if (res.origUsers == null || res.origUsers.length == 0) { firstUsers = res.newUsers; } else { firstUsers = new int[0]; for (int i=0; iAVAILABLE"); } int[] uidArray = new int[] { res.pkg.applicationInfo.uid }; ArrayListpkgList = new ArrayList(1); pkgList.add(packageName); sendResourcesChangedBroadcast(true, true, pkgList,uidArray, null); } } if (res.removedInfo.args != null) { // Remove the replaced package&#39;s older resources safely now deleteOld = true; } // If this app is a browser and it&#39;s newly-installed for some // users, clear any default-browser state in those users if (firstUsers.length > 0) { // the app&#39;s nature doesn&#39;t depend on the user, so we can just // check its browser nature in any user and generalize. if (packageIsBrowser(packageName, firstUsers[0])) { synchronized (mPackages) { for (int userId : firstUsers) { mSettings.setDefaultBrowserPackageNameLPw(null, userId); } } } } // Log current value of "unknown sources" setting EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED, getUnknownSourcesSettings()); } // Force a gc to clear up things Runtime.getRuntime().gc(); // We delete after a gc for applications on sdcard. if (deleteOld) { synchronized (mInstallLock) { res.removedInfo.args.doPostDeleteLI(true); } } if (args.observer != null) { try { Bundle extras = extrasForInstallResult(res); args.observer.onPackageInstalled(res.name, res.returnCode, res.returnMsg, extras); } catch (RemoteException e) { Slog.i(TAG, "Observer no longer exists."); } } } else { Slog.e(TAG, "Bogus post-install token " + msg.arg1); }} break;

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

万里长征最后一步,这里主要先将安装信息从安装列表中移除,这也是在之前processPendingInstall中添加的,包安装成功之后,在发送安装成功广播之前先获取运行时权限,获取权限后发送ACTION_PACKAGE_ADDED广播,如果是更新包再发送ACTION_PACKAGE_REPLACED和ACTION_MY_PACKAGE_REPLACED广播来通知其他应用,安装的广播发送完成后发送一个资源更改的广播通知其他应用,如果该应用是一个浏览器,则先清除默认的浏览器设置,重新检查浏览器设置。

上诉几步调用完成之后,强制调用gc,来触发jvm进行垃圾回收操作。gc调用后删除旧的安装信息,如果初始传入的IPackageInstallObserver2不为空,这回调调用方安装包安装完成。

总结

到此大致分析了整个安装过程,还有很多细节可以分析,比如parsePackage,之后可以在进行解析,整篇文字可能有理解错误的地方,望指出。

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

四 : Android 应用程序基础

Android应用程序是用Java编程语言编写的。Android SDK工具把应用程序的代码、数据和资源文件一起编译到一个Android程序包中(这个程序包是以.apk为后缀的归档文件),一个Android应用程序就是一个单独.apk文件中的所有内容,并且Android设备使用这个文件来安装应用程序。

安装在设备上的每个Android应用程序都生活在它们自己的安全沙箱中:

1. Android操作系统一个多用户的Linux系统,在这个系统中每个应用程序都是一个不同的用户。

2. 默认情况下,系统给每个应用程序分配一个唯一的Linux用户ID(这个ID只能被系统使用,并且对于应用程序是未知的)。系统给应用程序中的所有文件设置权限,以便只有跟用户ID匹配的应用程序能够访问他们。

3. 每个进程都有它们自己的虚拟机,因此应用程序的运行是彼此隔离。

4. 默认情况下,每个应用程序运行在它们自己的Linux进程中,当任何应用程序需要被执行时,Android启动这个进程,在不再需要的时候或系统必须为其他应用程序恢复内存时这个进程被关闭。

Android系统实现了最小特权原则,也就是说,默认情况下,每个应用程序只能访问支持它工作的必须的组件。这样就创建了一个安全的环境,在这个环境中应用程序不能访问系统没有给它授权的部分。

但是,还有一些应用程序间共享数据和应用程序访问系统服务的方法:

1. 两个应用程序共享相同的Linux用户ID是可能的,这样它们就能够访问彼此的文件。为了节省系统资源,拥有相同用户ID的应用程序也可以运行在相同的Linux进程中,并且共享相同的虚拟机(应用程序必有拥有相同的数字签名)

2. 应用程序能够请求访问设备数据的权限(如用户的通讯录、短信、可安装的存储设备、照相机、蓝牙等等),所有的应用程序的权限都必须在按照时被用户授予。

在这篇文章的剩余部分会介绍Android应用程序是如何存在与系统中的基础知识:

1. 定义应用程序的核心框架组件;

2. 在manifest文件中为应用程序声明组件、请求设备功能;

3. 把应用程序的代码与资源分离,并且允许应用程序使用各种设备配置优化它们的行为。

应用程序组件

应用程序组件是Android应用程序的重要基石,每个组件都是系统进入应用程序的不同入口,对于用户来说,不是所有的组件都是实际的入口,并且有一些是彼此依赖的,但是每一个组件都存在它们自己的实体,并且扮演着特殊的角色---它们都是帮助定义应用程序整体行为的唯一的模块。

应用程序有四种不同类型的组件,每种类型服务一个不同的目的,并且有一个定义组件如何创建和摧毁不同的生命周期。

以下是应用程序组件的四种类型:

Activites

一个Activity代表一个和用户接口的单独屏幕。例如,一个email应用程序可以有一个显示新邮件列表的Activity,一个写邮件的Activity和一个读邮件的Activity。在邮件应用程序中虽然这些Activity一起工作,从而形成统一的用户体验,但是它们是彼此独立的。这样,一个不同的应用程序能够启动这些Activity中的任何一个(如果邮件应用程序允许)。例如,一个相机应用程序为了给用户共享一张图片,可以启动邮件程序中编写新邮件的Activity。

一个活动是作为一个Activity子类实现的,可以在Activities开发者指南中学到更多的内容。

Services

Service是一个运行在后台的组件,用于执行长时操作或执行远程处理工作。Service不提供用户接口。例如,一个Service可能在后台播放音乐而用户却在使用另一个不同的应用程序,也可以是一个不带有Activity用户接口的从网络上获取数据的Service。

Content providers

Content provider管理一个共享的应用程序数据集,可以在文件系统(一个SQLite数据库、在网络上、或者其他的应用程序能够访问的本地的持久化的存储介质)中保存数据,通过content provider,其他的应用程序能够查询或编辑(如果content provider允许)数据。例如,Android系统提供了一个管理用户通讯录的content provider。这样,任何带有适当授权的应用程序都能够查询由content provider读/写的数据。

Content provider也可用于读写应用程序的私有数据而不是共享,例如Note Pad程序就是用一个content provider来保存注释。

Content provider是做为ContentProvider的一个子类来实现的,并且必须实现一组标准的使其他应用程序能够执行事务的API。

Broadcast receivers(广播接收器)

Broadcast receivers是一个响应全系统广播通知的组件。系统有很多广播源,例如屏幕关闭的一个广播通知、电池电量不足通知、抓图通知等。应用程序也能启动广播,例如让其他的应用程序知道某些数据已经下载到设备上,并且应用可以有效使用。虽然Broadcast receivers不显示用户接口,但是他们可以创建状态栏通知,提醒用户广播事件发生了。更常见的是,Broadcast receivers只是作为其他组件的一个网关,并且做很少的工作。例如,它可以启动一个执行某些基于事件来工作的服务。

Broadcast receiver是做为BroadcastReceiver的一个子类来实现的,并且每个广播作为一个Intent对象来分发。

Android系统设计独特一面是任何应用程序都能够启动另外应用程序的组件。例如,如果想要用户使用相机设备拍照,就有可能在一个应用程序中拍照而在另外一个应用程序中使用照片,而不需要在自己的应用程序中开发一个用于拍照的Activity。你不需要合并或事件联接来自相机应用的代码,而是简单启动照相机应用中用于拍照的Activity。当拍照完成,这个事件就返回到你的应用程序以便你能够使用照片。对于用户来说,照相机应用程序就好像你的应用的一部分。

系统启动一个组件时,它会启动那个应用的进程(如果应用未运行),并且初始化组件需要的类。例如,如果你的应用程序启动照相机应用中用于拍照的Activity,这个Activity运行在属于照相机应用的进程中,而不是你自己的进程中。因此,跟大多数其他系统不一样,Android应用程序没有一个单独的进入点(例如,没有main()方法)。

由于系统在一个独立的限制访问其他应用文件的进程中运行每个应用程序,所以应用程序不能直接激活来自其他应用程序的组件。但是,Android系统可以做到,因此要激活另一个应用程序中的一个组件,就必须发一个消息给系统,指定打算启动的特定的组件,然后系统为你激活这个组件。

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

激活组件

四种类型组件中的三种(Activities、Services、Broadcast receivers)是通过被叫做Intent的异步消息激活的。Intents在运行时绑定彼此独立的组件(可以把它想象成一个来自其他组件的请求一个动作信使),而不关注组件是属于应用自己还是其他的应用程序。

一个Intent就是一个Intent对象,它定义了一个消息,既可以激活指定的组件也可以激活指定的组件类型,一个Intent既可以是明确的也可以是隐含的。

对于Activities和Services,一个Intent定义了要执行的动作(例如:浏览或发送等事情),并且可以指定数据位置(对于正在启动的组件可能需要了解这些信息)。例如,一个Intent可能传达一个请求,要一个Activity显示一张图片或打开一个Web网页。在某些场合,还可以启动一个Activity来接收一个结果,这个Activity也返回Intent中的结果(例如:你可以发出一个Intent,让用户选择一个个人的联系方式,并且要它返回给你,返回的Intent包括一个指向选择的联系方式的位置。)。

对于广播接收器,Intent简单的定义了广播的通知(例如,一个指示设备电量不足的广播,只包含了一个已知行为的字符串:电池电量不足.)。

另一个组件类型—Content Provider不是用Intents来激活的,它是在来自一个ContentResolver对象请求目标时被激活。内容解析器使用Content Provider处理所有的直接事务,以便组件不必在用Content Provider来执行事务,而是调用ContentResolver对象上的方法。这样就在内容提供者和组件请求信息之间形成了一个抽象层。

每种类型组件都有独立的方法来进行激活:

1. 通过传递一个Intent给startActivity()或startActivityForResult()(需要Activity返回一个结果时调用这个方法)方法能够启动一个Activity(或者让这个Activity做一些新的事情。)

2. 通过传递一个Intent给startService()方法来启动一个Service(或者给一个运行的Service发送新的指令),也可以通过传递一个Intent给bindService()方法绑定Service

3. 通过传递一个Intent给sendBroadcast()、sendOrderedBroadcast()、sendStickyBroadcast()这样的方法可以启动一个广播;

4. 通过调用ContentResolver上的query()方法,可以执行一个对Content Provider的查询。

关于使用Intents的更多信息,可以看目的和目的过滤器(Intents and Intent Filters)。关于激活特定组件的更多信息在下列文档中也提供了:Activities、Services、BroadcastReceiver和Content Providers。

清单文件

Android系统在启动一个应用程序组件之前,必须通过阅读应用程序的AndroidManifest.xml文件来了解组件的存在情况。应用程序必须在这个文件中声明所有的它用到的组件,这个文件必须在应用项目目录根目录。

清单文件除了声明应用程序组件之外,还做了许多其他的事情,如:

1. 标识应用程序需要的用户权限,如:互联网访问或读取用户联系人的权限等;

2. 声明应用程序需要的最小API级别,也就是要说明应用程序使用的是基于那个级别的Android API;

3. 声明应用程序使用或需要的硬件和软件的功能,例如照相机、蓝牙服务、或多点触摸;

4. 应用程序需要的API库链接(Android框架API除外),如Google地图类库。

声明组件

清单(Manifest)的主要任务是通知系统应用程序的组件构成,例如,清单文件能够像下例那样声明一个Activity:



android:label="@string/example_label" ...>

...

在元素中,android:icon属性指明了标识应用程序的一个图标。

在元素中,android:name属性指定了Activity子类的完整类名,android:label属性给Activity指定了一个用于显示给用户的字符串标签。

必须使用以下方法声明所有的应用程序组件:

1.元素用于声明Activities

2.元素用于声明Services

3.元素用于声明Broadcast receivers

4.元素用于声明 Content Providers

在源代码中包含的Activities、Services、和Content Providers没有在清单文件中声明,对系统就是不可见的,所以就不能运行。但是Broadcast Receivers既可以在清单文件中声明也可以在代码中动态的创建(如BroadcastReceiver对象),然后通过调用registerReceiver()方法在系统中注册。

关于应用程序的清单文件结构的更多内容,可以查看The AndroidManifest.xml File文档。

声明组件的能力

像Activating Components一节中讨论的那样,可以使用一个Intent来启动Activities、Services和Broadcast Receivers。通过明确的指定在Intent中的目标组件的名字(使用组件的类名)就可以做这件事。但是Intent的实际能力在于Intent行为的概念。使用Intent行为,可以简单的描述想要执行的行为的类型(并且可选择型的描述在希望执行行为之上需要的数据),并且允许系统在设备中查找能够执行行为的组件并启动它,如果有多个能够执行被Intent描述的行为的组件,那么用户可以选择使用其中之一。

系统通过比较设备上其他应用程序的清单文件中提供的Intent过滤器收到的Intent来识别组件能够响应的一个Intent。

在应用程序清单中声明一个组件时,能够选择性的包含具有声明组件能力的Intent过滤器,以便能够响应来自其他的应用程序的Intent。通过在组件的声明元素中添加一个子元素,可以给组件声明一个Intent过滤器。

例如,用一个Activity来写信邮件的邮件应用程序,可能在它的清单中声明一个Intent过滤器,以便进入响应“发送“的Intents(为了发送邮件),然后,应用程序中的一个Activity能够创建一个带有“发送”行为的Intent,在你用startActivity()方法调用Intent是,系统会匹配邮件应用程序的“发送”Activity,并且加载它。

关于创建Intent过滤器的更多信息,可查看Intents and Intent Filters文档。

声明应用成的要求。

Android支持很多设备,并且所有这些设备所提供的功能都不尽相同。为了防止应用程序被安装在那些缺少应用程序所需要的功能的硬件设备上,可以在应用的清单文件中声明设备和软件要求,以便清晰的定义一个应用程序所支持的设备类型的简介。这些声明的大多数都只是信息式的,系统不会读取它们,但是另外的服务(如Android Market)为了给用户提供过滤服务,会在用户的设备上搜索这些信息。

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

例如,如果应用程序需要使用照相机和Android2.1中APIs(API Level 7),就应该在应用程序的清单文件中声明这些需求,这样,没有照相机并且Android版本低于2.1的设备就不会动Android Market上安装这个应用程序。

虽然你可以声明你的应用程序使用了照相机,但这不是必须的,如果没有声明,你的应用程序就必须在运行时执行一些检查,判断设备是否有照相机功能,如果照相机功能无效,那么应用程序中一些相关功能就需要禁用。

一下是在设计和开发应用程序时应该重点考虑的一些设备特征:

屏幕大小和分辨率

为了通过屏幕类型区分设备,Android为每个设备定义了两个特征:屏幕大小(屏幕的物理尺寸)和屏幕分辨率(在屏幕上点的物理密度,或dpi---每英寸的点数)。为了简化所有的不同屏幕类型的配置,Android系统把它们归纳到更容易定位的选择组。

屏幕的大小是:小、普通、大、超大;

屏幕的分辨率是:低分辨率、中分辨率、高分辨率和超高分辨率。

默认情况,应用程序会兼容所有的屏幕大小和分辨率,因为Android系统会对你的UI布局和图片资源进行相应的调整。但是,对于确定的屏幕尺寸,你应该创建特定的布局,并且对于确定的分辨率要提供特定的图片,也可以使用布局资源,并且在应用程序的清单中使用元素明确的声明应用程序支持的屏幕尺寸。

更多信息,请看Supporting Multiple Screens文档。

输入配置

很多设备提供了不同的用户输入机制,如键盘、轨迹球、或五向导航板。如果你的应用程序需要特殊的输入硬件类型,那么就应该在清单的元素中声明。但是,应用程序还是很少需要明确的输入配置。

设备功能

在给定的Android设备上有许多硬件和软件功能存在或不存在,如照相机、亮度传感器、蓝牙、OpenGL的某个版本、或触屏的精度等。你不应该假设某个功能在所有的Android设备上都是有效的(标准的Android类库除外),因此你应该用元素声明应用程序使用的所有功能。

平台版本

不同的Android设备经常运行着不同的Android平台的版本,如Android1.6或Android2.3.每个后续的版本经常会包括以前版本中额外的无效的APIs。为了指明APIs集合的有效性,每个平台版本都指定了一个API级别(例如,Android1.0的API级别是1,Android2.3的API级别是9)。如果你使用任何在1.0版本以后添加的APIs,你应该使用元素来声明最小的API级别。

为你的应用程序声明所有这些要求是至关重要的,当你把应用程序发布到Android Market时,Market使用这些声明来过滤在每个设备上有效的应用程序。正因如此,你的应用程序应用只对所有的满足你的应用程序要求的设备有效。

关于Android Market基于这些要求过滤应用程序的更多信息,请看Market Filters文档。

应用程序资源

一个Android应用程序只是由代码组成的,它需要独立与代码之外的资源,如图片、音频文件、以及任何有关应用程序的视觉表现部分等等。例如,你应该使用XML文件来定义用户界面的动画、菜单、样式、颜色、以及Activity的布局等。使用应用程序资源使更新应用程序的各种功能更加容易,而不必编辑代码,通过提高可选的资源集。能够使你针对各种设备配置优化应用程序(例如不同的语言和屏幕尺寸)。

对于在你Android项目中包含的每种资源,SDK编译工具都给定义了一个唯一的数字ID,你能够使用XML文件中定义的这个ID在应用程序的代码或其他的资源中引用这个资源。如,如果应用程序中包含了一个名叫logo.png的图片文件(保存在res/drawable/目录中),SDK工具就会生成一个叫做R.drawable.logo的资源ID,你能够通过这个ID来引用这个图片,并把它插入到用户界面中。

把资源与代码分离的重要原因之一是给不同的设备配置提供可选资源的能力。例如,通过在XML文件中定义UI字符串,你能够把这些字符串翻译成其他的语言,并且把它们保存到一个独立的文件中,然后基于一个语言预选器(你追加的资源目录名,如res/values-fr/目录中保存法语字符串)和用户的语言设置,Android系统就会在UI中使用适当的语言字符串。

Android支持很多不同选择资源的预选器,预选器是一个包含在资源目录名中的简短的字符串,以便定义设备配置应该使用哪些资源。如,根据设备屏幕的取向和尺寸,针对Activity应该创建不同的布局,当设备屏幕纵向高的时候,你可能想要按钮垂直布局,而当屏幕横向宽的时候,你可能想要按钮水平布局。要改名依赖取向的布局,你可以定义两个不同的布局并且用每个布局的目录名作为相应的预选器,然后系统就会根据当前屏幕的取向,自动的使用相应的布局了。

关于应用程序中能够包含的资源种类以及如何为不同设备配置创建可选资源,请看Application Resource开发指南。

扩展:android应用程序结构 / android应用程序框架 / android应用程序开发

五 : Android应用程序消息处理机制(Looper、Handler)分析(2)

在Android应用程序进程启动过程的源代码分析一文中,我们分析了Android应用程序进程的启动过程。

Android应用程序进程在启动的时候, 会在进程中加载ActivityThread类,并且执行这个类的main函数。

应用程序的消息循环过程就是在这个main函数里面实现的。

我们来看看这个函数的实现,它定义在frameworks/base/core/java/android/app/ActivityThread.java文件中:

  1. [java]viewplaincopypublicfinalclassActivityThread{
  2. ......
  3. publicstaticfinalvoidmain(String[]args){
  4. ......
  5. Looper.prepareMainLooper();
  6. ......
  7. ActivityThreadthread=newActivityThread();
  8. thread.attach(false);
  9. ......
  10. Looper.loop();
  11. ......
  12. thread.detach();
  13. ......
  14. }
  15. }

这个函数做了两件事情,一是在主线程中创建了一个ActivityThread实例,二是通过Looper类使主线程进入消息循环中,这里我们只关注后者。

首先看Looper.prepareMainLooper函数的实现,这是一个静态成员函数,定义在frameworks/base/core/java/android/os/Looper.java文件中:

  1. [java]viewplaincopypublicclassLooper{
  2. ......
  3. privatestaticfinalThreadLocalsThreadLocal=newThreadLocal();
  4. finalMessageQueuemQueue;
  5. ......
  6. /**Initializethecurrentthreadasalooper.
  7. *Thisgivesyouachancetocreatehandlersthatthenreference
  8. *thislooper,beforeactuallystartingtheloop.Besuretocall
  9. *{@link#loop()}aftercallingthismethod,andenditbycalling
  10. *{@link#quit()}.
  11. */
  12. publicstaticfinalvoidprepare(){
  13. if(sThreadLocal.get()!=null){
  14. thrownewRuntimeException("OnlyoneLoopermaybecreatedper
  15. thread");
  16. }
  17. sThreadLocal.set(newLooper());
  18. }
  19. /**Initializethecurrentthreadasalooper,markingitasan
  20. application'smain
  21. *looper.ThemainlooperforyourapplicationiscreatedbytheAndroid
  22. environment,
  23. *soyoushouldneverneedtocallthisfunctionyourself.
  24. *{@link#prepare()}
  25. */
  26. publicstaticfinalvoidprepareMainLooper(){
  27. prepare();
  28. setMainLooper(myLooper());
  29. if(Process.supportsProcesses()){
  30. myLooper().mQueue.mQuitAllowed=false;
  31. }
  32. }
  33. privatesynchronizedstaticvoidsetMainLooper(Looperlooper){
  34. mMainLooper=looper;
  35. }
  36. /**
  37. *ReturntheLooperobjectassociatedwiththecurrentthread.Returns
  38. *nullifthecallingthreadisnotassociatedwithaLooper.
  39. */
  40. publicstaticfinalLoopermyLooper(){
  41. return(Looper)sThreadLocal.get();
  42. }
  43. privateLooper(){
  44. mQueue=newMessageQueue();
  45. mRun=true;
  46. mThread=Thread.currentThread();
  47. }
  48. ......
  49. }
本文标题:android应用程序-Android应用程序 --- WakeLock 保持后台唤醒状态
本文地址: http://www.61k.com/1148756.html

61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1