編輯:關於Android編程
Activity最為android開發者最熟悉的組件,由ActivityManagerService服務進行調度管理,本文基於其對Activity的啟動過程進行分析,同時也將分析AMS對Activity的調度管理。
啟動模式實際上就是為了控制Activity與Task之間的關系,目前共有一下四種模式:standard、singleTop、singleTask以及singleInstance,默認的模式為standard模式。
注意:建議一般的開發者不要輕易使用後兩種模式,因為不同的模式,其出棧的順序不同,可能會導致用戶按下返回鍵時,會有不一樣的用戶體驗,對於啟動模式的分析。這篇文章使用圖文並用的方式,很好的分析了Activity各種模式的區別,本文不再分析啟動模式。
Activity作為android非常重要的組件,那麼我們會在哪些時機啟動它呢?
通常有如下幾種情形:
1. android AM命令
android系統為開發者提供了adb工具,而在adb的基礎上執行adb shell就可以從PC上對手機側執行shell命令。如啟動浏覽器:am start -n com.android.browser/com.android.browser.BrowserActivity。
2. 啟動AMS服務時
在啟動AMS服務時,在最後調用AMS的systemReady的最後階段會啟動Home界面,此時就會啟動一個Activity。
3. 調用startActivity()和startActivityForResult()方法時
這是我們最常用也最熟悉的啟動Activity的方式。
在adb中可以使用AM命令,同時在應用程序中,也可以通過AM命令來啟動指定的Activity,AM是一個腳本命令,它還可以啟動Service,發送廣播等。
adb執行Am命令後,最終會執行Am.java的main方法,其代碼如下:
//Am.java
public static void main(String[] args){
(new Am()).run(args);
}
它調用的是抽象父類BaseCommand類的run方法:
//BaseCommand.java
public void run(String[] args){
if(args.length<1){
//顯示命令使用說明
onShowUsage(System.out);
return;
}
mArgs = args;
mNextArg = 0;
mCurArgData = null;
try{
onRun();
}catch(...){...}
}
跟其他命令腳本類似,如果參數為空,顯示使用說明,否則調用onRun()方法,它是一個抽象方法,在Am.java中有實現:
//Am.java
@Override
public void onRun() throws Exception{
//獲取AMS
mAm = ActivityManagerNative.getDefault();
...
String op = nextArgRequired();
if(op.equals("start")){
//啟動Activity
runStart();
}else if(op.equals("startService")){
//啟動服務
runStartService();
}else if(op.equals("force-stop")){
//強制停止
runForceStop();
}else if(op.equals("kill")){
//殺死進程
runKill();
}else if{
...
}else{
...
}
}
命令參數不同,其執行的操作不同,就本節來說,其參數為start,所以將會執行runStart()方法來啟動Activity:
//Am.java
private void runStart() throws Exception{
Intent intent = makeIntent(UserHandle.USER_CURRENT);
...
do{
//獲取包信息
...
//設置intent的標志位FLAG_ACTIVITY_NEW_TASK,即將啟動一個新任務
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ParcelFileDescriptor fd = null;
ProfilerInfo profilerInfo = null;
if(mProfileFile != null){//處理-P選項,統計性能
try{
fd = openForSystemServer(new File(mProfileFile),ParcelFileDescriptior.MODE_CREATE|
ParcelFileDescriptor.MODE_TRUNCATE|ParcelFileDescriptor.MODE_READ_WRITE);
}catch(...){...}
profilerInfo = new ProfilerInfo(mProfileFile,fd,mSamplingInterval,mAutoStop);
}
...
int res;
...
if(mWaitOption){//控制是否等待啟動結果,如果有-W選項,則該值為true
result = mAm.startActivityAndWait(null,null,intent,mimeType,null,null,0,mStartFlags,
profilerInfo,null,mUserId);
res = result.result;
}else{
res = mAm.startActivityAsUser(null,null,intent,mimeType,null,null,0,mStartFlags,
profilerInfo,null,mUserId);
}
//處理一些返回結果
...
}while(mRepeat > 1);
}
startActivityAsUser()最後也是調用AMS的startActivityAndWait()方法,所以Am命令框架層的調用結束,其調用時序如下:
Am命令將啟動Activity的工作交給了AMS來進行,首先看startActivityAndWait()方法,它直接調用StackSupervisor類的startActivityMayWait()方法,其代碼如下:
//StackSupervisor.java
final int startActivityMayWait(...){
...
//為了不改變用戶的intent
intent = new Intent(intent);
//收集目標intent的信息,在resolveActivity方法中與PKMS交互獲得
ActivityInfo aInfo = resolveActivity(intent,resolvedType,startFlags,profilerInfo,userId);
ActivityContainer container = (ActivityContainer)iContainer;
synchronized(mService){
...
final ActivityStack stack;
//取得需要啟動的ActivityStack棧
if(container == null || container.mStack.isOnHomeDisplay()){
stack = mFocusedStack;
}else{
stack = container.mStack;
}
...
if(aInfo!=null&&(aInfo.applicationInfo.privateFlags&
ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE)!=0){
//Androidmanifest.xml中的Application標簽可以聲明一個cantSaveState屬性,若設置,其將不享受系統提
//供的狀態保存/恢復功能。主要是為了保證用戶體驗的連續性
...
}
//啟動Activity
int res = startActivityLocked(caller,intent,resolvedType,aInfo,voiceSession,
voiceInteractor,resultTo,resultWho,requestCode,callingPid,callingUid,callingPackage,
realCallingPid,realCallingUid,startFlags,options,ignoreTargetSecurity,
componentSpecified,null,container,inTask);
...
//如果配置Configration發生變化,則調用AMS的updateConfigurationLocked進行處理
if(stack.mConfigWillChange){
mService.enforceCallingPermission(...);
mConfigWillChange = false;
mService.updateConfigurationLocked(config,null,false);
}
if(outResult != null){
outResult.result = res;
if(res == IActivityManager.START_SUCCESS){
//將結果放入mWaitingActivityLaunced中保存
mWaitingActivityLaunched.add(outResult);
do{
try{
//等待啟動結果
mService.wait();
}
}while(!outResult.timeout&&outResult.who == null);
}else if{
...
}
...
}
return res;
}
}
首先通過PKMS查找匹配的ActivityInfo,然後獲取調用者的pid,uid等信息,由於本文的caller為空,得到的是Am所在進程的pid和uid。接著調用startActivityLocked()方法啟動Activity,最後再調用mService.wait()方法來等待啟動結果,並對返回值進行一些處理,此處的等待主要是Am參數中有一個-W選項,若無此選項,則不會有這個等待,最後再將啟動結果res返回。
繼續分析startActivityLocked()方法:
//StackSupervisor.java
final int startActivityLocked(...){
int err = ActivityManager.START_SUCCESS;
ProcessRecord callerApp = null;
if (caller != null) {//如果caller不為空,從AMS中找到它所屬的ProcessRecord即進程,本文此處為null
callerApp = mService.getRecordForAppLocked(caller);
if (callerApp != null) {
callingPid = callerApp.pid;
callingUid = callerApp.info.uid;
} else {
err = ActivityManager.START_PERMISSION_DENIED;
}
}
...
//sourceRecord用於描述啟動目標Activity的那個Activity
ActivityRecord sourceRecord = null;
//resultRecord用於描述接收啟動結果的Activity,即onActivityResult將被調用以通知啟動結果
ActivityRecord resultRecord = null;
...
//獲取Intent設置的啟動標志
final int launchFlags = intent.getFlags();
//此部分與LaunchMode類似,它用於控制Activity啟動結果的通知
if ((launchFlags & Intent.FLAG_ACTIVITY_FORWARD_RESULT) != 0 && sourceRecord != null) {
...
}
...
//檢查權限
final int startAnyPerm = mService.checkPermission(START_ANY_ACTIVITY, callingPid, callingUid);
...
//可為AMS設置IActivityController類型的監聽者,AMS有任何動靜都將回調該監聽者
if (mService.mController != null) {
try {
Intent watchIntent = intent.cloneFilter();
//交給回調對象處理,由它判斷能否繼續後面的行程
abort |= !mService.mController.activityStarting(watchIntent,
aInfo.applicationInfo.packageName);
} catch (RemoteException e) {
mService.mController = null;
}
}
if (abort) {//通知resultRecord
if (resultRecord != null) {
resultStack.sendActivityResultLocked(-1, resultRecord, resultWho, requestCode,
Activity.RESULT_CANCELED, null);
}
ActivityOptions.abort(options);
return ActivityManager.START_SUCCESS;
}
//創建一個ActivityRecord對象
ActivityRecord r = new ActivityRecord(mService,this,callerApp,callingUid,...);
...
final ActivityStack stack = mFocusedStack;
if (voiceSession == null && (stack.mResumedActivity == null
|| stack.mResumedActivity.info.applicationInfo.uid != callingUid)) {
//判斷是否有權限切換Application
if (!mService.checkAppSwitchAllowedLocked(callingPid, callingUid,realCallingPid,
realCallingUid, "Activity start")) {
PendingActivityLaunch pal =new PendingActivityLaunch(r, sourceRecord,startFlags,
stack);
//將Pending的請求保存到AMS的mPendingActivityLaunches變量中
mPendingActivityLaunches.add(pal);
ActivityOptions.abort(options);
return ActivityManager.START_SWITCHES_CANCELED;
}
}
if (mService.mDidAppSwitch) {
mService.mAppSwitchesAllowedTime = 0;
} else {
mService.mDidAppSwitch = true;
}
//啟動處於Pending狀態的Activity
doPendingActivityLaunchesLocked(false);
//調用此函數繼續啟動Activity
err = startActivityUncheckedLocked(r, sourceRecord, voiceSession, voiceInteractor,startFlags,
true, options, inTask);
//做一些結果處理
...
return err;
}
此段代碼的主要工作就是處理sourceRecord和resultRecord,然後再處理app switch屬性,如果當前禁止app switch,則將本次啟動保存起來,等允許app switch時再處理,最後再調用startActivityUncheckedLocked()方法處理本次Activity啟動請求:
//StackSupervisor.java
final int startActivityUncheckedLocked(...) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
...
//獲取啟動標志
int launchFlags = intent.getFlags();
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_DOCUMENT) != 0 &&(launchSingleInstance
|| launchSingleTask)) {
//如果Intent和AndroidManifest.xml的啟動模式有沖突,以AndroidManifest.xml為准
launchFlags &=~(Intent.FLAG_ACTIVITY_NEW_DOCUMENT | Intent.FLAG_ACTIVITY_MULTIPLE_TASK);
} else {
...
}
...
if (sourceRecord != null) {//如果請求的發起者為空,則需要創建一個新的Task
if (sourceRecord.finishing) {
if ((launchFlags & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
launchFlags |= Intent.FLAG_ACTIVITY_NEW_TASK;
newTaskInfo = sourceRecord.info;
newTaskIntent = sourceRecord.task.intent;
}
sourceRecord = null;
sourceStack = null;
} else {
sourceStack = sourceRecord.task.stack;
}
} else {
sourceStack = null;
}
}
本節將對我們熟悉的調用startActivity的啟動方式進行分析,同樣基於的源碼版本是android 6.0,這種情況下,應用進程已經存在,不需要進行應用進程的創建啟動(區別於AMS啟動Home桌面等啟動新應用的方式),首先看startActivity()代碼:
@Override
public void startActivity(Intent intent,@Nullable Bundle options){
if(options != null){
startActivityForResult(intent,-1,options);
}else{
startActivityForResult(intent,-1);
}
}
由代碼可知,startActivity()最終只是對startActivityForResult()方法進行簡單調用,所以只需對startActivityForResult()繼續分析:
public void startActivityForResult(Intent intent,int requestCode,@Nullable Bundle options){
if(mParent == null){
//執行Activity啟動,並返回啟動結果
Instrumentation.ActivityResult ar = mInstrumentation.execStartActivity(this,
mMainThread.getApplicationThread(),mToken,this,intent,requestCode,options);
if(ar != null){
//向主線程發送啟動結果
mMainThread.sendActivityResult(mToken,mEmbeddedID,requestCode,ar.getResultCode(),
ar.getResultData());
}
if(requestCode > 0){
//啟動成功
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
}else{
//如果mParent不為空,此時最後還是會調用Instrumentation的execStartActivity()方法
if(options != null){
mParent.startActivityFromChild(this,intent,requestCode,options);
}else{
mParent.startActivityFromChild(this,intent,requestCode);
}
}
}
最後還是會調用Instrumentation的execStartActivity()方法來啟動Activity,並將啟動的結果返回給該應用的主線程,繼續分析execStartActivity()方法:
public ActivityResult execStartActivity(Context who,IBinder contextThread,IBinder token,String
target,Intent intent,int requestCode,Bundle options){
//獲取通信存根類,它派發消息給ActivityThread主線程,而主線程的mH的handler會進行相應處理
IApplicationThread whoThread = (IApplicationThread)contextThread;
...
try{
intent.migrateExtraStreanToClipData();
intent.prepareToLeaveProcess();
//調用AMS的startActivity()方法,即進入AMS的調度管理
int result = ActivityManagerNative.getDefault().startActivity(whoThread,
who.getBasePackageName(),intent,intent.resolveTypeIfNeeded(
who.getContentResolver()),token,target,requestCode,0,null,options);
//檢查啟動結果
checkStartActivityResult(result,intent);
}catch(RemoteException e){
throw new RuntimeException("Failure from system",e);
}
return null;
}
首先獲得IApplicationThread對象,它用於與主線程進行通信,ActivityManagerNative.getDefault()獲取的是AMS對象,所以此處將啟動任務交給了AMS進行調度管理。startActivity方法會調用startActivityAsUser()方法:
@Override
public final int startActivityAsUser(...){
userId = handleIncomingUser(Binder.getCallingPid(),Binder.getCallingUid(),userId,false,
ALLOW_FULL_ONLY,"startActivity",null);
return mStackSupervisor.startActivityMayWait(caller,-1,callingPackage,intent,resolvedType,
null,null,resultTo,resultWho,requestCode,startFlags,profierInfo,null,null,options,false,
userId,null,null);
}
至此,它的分析同第3節中的過程一致,此處不再做分析,只是本節的啟動不需要啟動進程,所以它的執行分支不一致。
寫在前面:作為一個剛半只腳踏入android開發的新手,在使用eclipse開發了兩個自我感覺不甚成熟的商城類app之後,遇到了一些問題,總結為如下:1,代碼復用性。fi
一、適配器模式介紹適配器在平常在生活中是經常會用到的,特別是電子產品。像手機、電腦、家用電器都會用到適配器來轉換電壓的大小,以提供合適的電壓。適配器就是把原來不符合要求的
在網上搜怎麼加載Reveal的,好多都是利用Reveal.frameWork來拖進工程裡面,這樣很不方便,每次更換新工程都要這樣做,這裡給大家講解一個利用配置一個全局斷點
ZXing是谷歌的一個開源庫,可以用來生成二維碼、掃描二維碼。本文所介紹的是第一部分。首先上效果圖:ZXing相關各種文件官方下載地址:https://github.co