1. 程式人生 > >Android 4.4 AMS 學習筆記(一)

Android 4.4 AMS 學習筆記(一)

對比著4.4 和書上講的4.0原始碼,大概看了看,原始碼中變化還是很大的。

【插一句,其實所謂的AMS,PKMS,以及WMS等都是執行在system_server這個程序中的執行緒】

首先看main函式,大體內容都一樣,

重要的資料結構:
1. AThread thr =new AThread(); //這個AThread來完成AMS的部分初始化工作。

AThread執行緒跟main函式相互等待確認。

題外話:順便說下wait跟sleep函式的區別:

1. 來自不同的類

   sleep是Thread類的靜態方法,誰呼叫誰去睡覺。sleep是佔用cpu去睡覺,而wait是放棄cpu去睡覺

2. sleep沒有釋放鎖,而wait釋放了鎖,sleep不會讓出cpu資源,wait是進入執行緒池等待,一般wait是不會使用時間引數,他必須等待別人notify他才會進入就緒隊中。而sleep只要時間到了,就會自動進入就緒佇列。如果等不及了,只能通過interrupt來強項打斷。

3. wait,notify以及notifyall只能在同步控制方法或者同步控制塊中使用,而sleep可是在任何地方使用

 synchronized(x){
      x.notify()
     //或者wait()
   }

4. sleep必須捕獲異常,而其他3個則不需要

AThread這個執行緒跟4.0差不多,沒太大區別。

回到AMS的建構函式。

這個貌似多了些東西:

mFgBroadcastQueue = new BroadcastQueue(this, "foreground", BROADCAST_FG_TIMEOUT, false);
        mBgBroadcastQueue = new BroadcastQueue(this, "background", BROADCAST_BG_TIMEOUT, true);
        mBroadcastQueues[0] = mFgBroadcastQueue;
        mBroadcastQueues[1] = mBgBroadcastQueue;

        mServices = new ActiveServices(this);
        mProviderMap = new ProviderMap(this);

 final ActiveServices mServices;
final ProviderMap mProviderMap;

不知道這4個變數是幹嘛用的,看名字,前2個是廣播訊息佇列,後兩個嘛,暫時還不知道???連個註釋都沒給

然後建立system目錄,建立BBS和USS

建立USS之前,多了一個:

 /**
     * Tracking long-term execution of processes to look for abuse and other
     * bad app behavior.
     */
final ProcessStatsService mProcessStats;
mProcessStats = new ProcessStatsService(this, new File(systemDir, "procstats"));
根據註釋,是一個程序來統計不用的app以及不好行為的app的資訊
/**
     * Information about and control over application operations
     */
    final AppOpsService mAppOpsService;
 mAppOpsService = new AppOpsService(new File(systemDir, "appops.xml"));

 /**
     * File storing persisted {@link #mGrantedUriPermissions}.
     */
    private final AtomicFile mGrantFile;
 mGrantFile = new AtomicFile(new File(systemDir, "urigrants.xml"));

  mHeadless = "1".equals(SystemProperties.get("ro.config.headless", "0"));

        // User 0 is the first and only user that runs at boot.
        mStartedUsers.put(0, new UserStartedState(new UserHandle(0), true));
        mUserLru.add(Integer.valueOf(0));
        updateStartedUserArrayLocked();

多了這麼多變數,其中AppOpsService根據註釋應該是用來記錄PP的控制行為資訊的。

mGrantFile。。。不甚懂。

不過mHeadless這個bool變數,只在attachApplication,handleAppCrashLocked,updateConfigurationLocked和startHomeActivityLocked這4個函式用做一些判斷使用,沒有註釋啊。。。看起來跟系統配置相關。

不過4.4中對資訊的統計可以大費周章,原來的4.0中使用的是mPrecessStats.init()來讀取/proc/stat檔案內容,統計kernel以及system執行時資訊,可以看到在4.4的時候,統計資訊變成了mProcessCpuTracker.init(),用來收集當顯示未響應對話方塊是的程序狀態,由mProcessCpuThread執行緒守護。


然後新增Watchdog監控。啟動mProcessCpuThread執行緒。這個執行緒的主要工作就是計時統計。

【一句話,就是把mProcessStatsThread換成了mProcessCpuThread】

2. AMS:ActivityThread中的systemMain

跟4.0中的一模一樣。

其中attach函式的引數用來描述是否為系統程序。

在attach函式中遇到了大名鼎鼎的Instrumentation類,做專案是的時候,修改了啟動activity的流程,在中間的某些步驟直接返回一個錯誤的值,導致在home介面上無法啟動activity,當時的除錯跟蹤結果為,這個錯誤的值由Instrumenttation物件接受並丟擲異常,我測試的時候發現是丟擲的一個securityException,但是不知道為什麼luncher的設計者處理該異常的方法是僅僅是彈出一個toast,內容為app not install。

根據文件中的解釋,Instrumenttation類的功能是:他是一個工具類,當被啟用時,系統先建立它,再通過它來建立其他元件。另外元件跟系統間的互動也通過這個類來傳遞,這樣他就能檢測系統和這些元件的互動情況了。

這個函式執行完之後,得到了:
1 一個ActivityThread物件,他代表應用程序的主執行緒。

2 一個Context物件,他只想的Application環境與framework-res.apk有關。

其目的就是為了system_server程序搭建一個和應用程序一樣的Android執行環境。

3.AMS呼叫ActivityThread.getSystemContext,得到一個context物件

總之,AMS的main函式的主要目的是:1 建立按AMS物件,2.建立Android執行時環境,這個執行時環境包含2個成員變數ActivityThread和ContextImpl。

第一步完成之後,system_server會接著呼叫AMS的setSystemProcess函式,目的是system_server程序可以加入到AMS中。

來看下這個函式,

4. AMS的setSystemProcess函式:

  public static void setSystemProcess() {
        try {
            ActivityManagerService m = mSelf;

            ServiceManager.addService(Context.ACTIVITY_SERVICE, m, true);
            ServiceManager.addService(ProcessStats.SERVICE_NAME, m.mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(m));
            ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
            ServiceManager.addService("dbinfo", new DbBinder(m));
            if (MONITOR_CPU_USAGE) {
                ServiceManager.addService("cpuinfo", new CpuBinder(m));
            }
            ServiceManager.addService("permission", new PermissionController(m));

            ApplicationInfo info =
                mSelf.mContext.getPackageManager().getApplicationInfo(
                            "android", STOCK_PM_FLAGS);
            mSystemThread.installSystemApplicationInfo(info);

            synchronized (mSelf) {
                ProcessRecord app = mSelf.newProcessRecordLocked(info,
                        info.processName, false);
                app.persistent = true;
                app.pid = MY_PID;
                app.maxAdj = ProcessList.SYSTEM_ADJ;
                app.makeActive(mSystemThread.getApplicationThread(), mSelf.mProcessStats);
                mSelf.mProcessNames.put(app.processName, app.uid, app);
                synchronized (mSelf.mPidsSelfLocked) {
                    mSelf.mPidsSelfLocked.put(app.pid, app);
                }
                mSelf.updateLruProcessLocked(app, true, false);
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(
                    "Unable to find android system package", e);
        }
    }


首先向SystemManager註冊幾個服務:

            ServiceManager.addService(Context.ACTIVITY_SERVICE, m, true);
            ServiceManager.addService(ProcessStats.SERVICE_NAME, m.mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(m));
            ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
            ServiceManager.addService("dbinfo", new DbBinder(m));
            if (MONITOR_CPU_USAGE) {
                ServiceManager.addService("cpuinfo", new CpuBinder(m));
            }
            ServiceManager.addService("permission", new PermissionController(m));

比4.0多了一個dbinfo,資料庫相關的服務。

然後通過呼叫函式向PKMS查詢名稱為android的Application,但是這裡邊AMS和PKMS之間的互動是通過context來完成的。根據書中的解釋,是為了從設計上以及後期擴充套件性上來這麼做的。

再然後就是:

 mSystemThread.installSystemApplicationInfo(info);

這裡的mSystemThread就是ActivityThread。

在這個函式中:

    public void installSystemApplicationInfo(ApplicationInfo info) {
        synchronized (this) {
            ContextImpl context = getSystemContext();
            context.init(new LoadedApk(this, "android", context, info,
                    CompatibilityInfo.DEFAULT_COMPATIBILITY_INFO), null, this);

            // give ourselves a default profiler
            mProfiler = new Profiler();
        }
    }

這裡主要是context.init這個程式碼,因為在AMS的main函式中,已經使用了ActivityThread的getSystemContext函數了,這是個單例模式:

建立一個contextImpl物件,這裡邊使用了context.init函式,不過這裡因為new LoadedApk時,第4個引數為null,也就是沒有完全建立好Android的上下文環境,而在installSystemApplicationInfo這個函式中,new LoadedApk的第四個引數為ApplicationInfo的一個物件。對應的建構函式如下:

   public LoadedApk(ActivityThread activityThread, String name,
            Context systemContext, ApplicationInfo info, CompatibilityInfo compatInfo) {
        mActivityThread = activityThread;
        mApplicationInfo = info != null ? info : new ApplicationInfo();
        mApplicationInfo.packageName = name;
        mPackageName = name;
        mAppDir = null;
        mResDir = null;
        mSharedLibraries = null;
        mDataDir = null;
        mDataDirFile = null;
        mLibDir = null;
        mBaseClassLoader = null;
        mSecurityViolation = false;
        mIncludeCode = true;
        mClassLoader = systemContext.getClassLoader();
        mResources = systemContext.getResources();
        mDisplayAdjustments.setCompatibilityInfo(compatInfo);
    }

要注意一下,AMS只是一個執行緒,但是它需要跟其他程序中的activity通訊(例如啟動其他位於另外一個程序中的activity)。因此必須要跨程序通訊。很明顯需要通過Binder來進行通訊。

這裡需要說一下:

ApplicationThreadNative類繼承與Binder,UML圖如下所示:

可以看到介面IApplicationThread定義了AMS和應用進行通訊的互動函式,因此IApplicationThread的Binder服務端在應用程序中,因為AMS還需要監聽應用程序的死亡通知。可以看下圖中關於介面的說明,如果AMS想要停止一個Activity,那麼就會呼叫應用程序IApplicationThread Binder客戶端的scheduleStopActivity函式,該函式服務端實現的就是想ActivityThread所線上程傳送一個訊息。在應用程序中,ActivityThread執行在主執行緒中,所以這個訊息最終在主執行緒中被處理。

回到AMS的setSystemProcess這個函式中。

插一句,installsystemapplicationinfo這個函式載入framework-res.apk檔案,這個檔案也需要一個正確的android執行環境,因此context.init才會被初始化2次。

往下走:

 ProcessRecord app = mSelf.newProcessRecordLocked(info,info.processName, false);
看名字貌似是建立一個新的程序,因為ProcessRecord就是描述一個程序的。

4.4中的程式碼如下:

final ProcessRecord newProcessRecordLocked(ApplicationInfo info, String customProcess,
            boolean isolated) {
        String proc = customProcess != null ? customProcess : info.processName;
        BatteryStatsImpl.Uid.Proc ps = null;
        BatteryStatsImpl stats = mBatteryStatsService.getActiveStatistics();
        int uid = info.uid;
        if (isolated) {
            int userId = UserHandle.getUserId(uid);
            int stepsLeft = Process.LAST_ISOLATED_UID - Process.FIRST_ISOLATED_UID + 1;
            while (true) {
                if (mNextIsolatedProcessUid < Process.FIRST_ISOLATED_UID
                        || mNextIsolatedProcessUid > Process.LAST_ISOLATED_UID) {
                    mNextIsolatedProcessUid = Process.FIRST_ISOLATED_UID;
                }
                uid = UserHandle.getUid(userId, mNextIsolatedProcessUid);
                mNextIsolatedProcessUid++;
                if (mIsolatedProcesses.indexOfKey(uid) < 0) {
                    // No process for this uid, use it.
                    break;
                }
                stepsLeft--;
                if (stepsLeft <= 0) {
                    return null;
                }
            }
        }
        synchronized (stats) {
            ps = stats.getProcessStatsLocked(info.uid, proc);
        }
        return new ProcessRecord(ps, info, proc, uid);
    }

其中紅色程式碼是4.0沒有的,根據註釋。Process.LAST_ISOLATED_UID:99999,是一個完全隔離的沙盒程序中所使用的最後一個程序號,中間紅色的程式碼意思就是給分配一個可用的程序號  。

看看ProcessRecord的建構函式,其中有一個變數叫做 pkgList,在4.4版本中型別變為了:ArrayMap,我去對java不怎麼了解,反正他的目的就是因為一個程序中可以執行多個package。程式碼如下:是String ProcessState鍵值對,其中ProcessState是ProcessStats的內部類。

 final ArrayMap<String, ProcessStats.ProcessState> pkgList
在網上大概找了找,這種所謂的ArrayMap:ArrayMap 由一個ArrayList後推得到的Map。對反覆的順序提供了精確的控制。面向非常小的Map設計,特別是那些需要經常建立和刪除的。對於非常小的Map,建立和反覆所付出的代價要比HashMap低得多。但在Map變大以後,效能也會相應地大幅度降低。

應該是種優化吧。

這個函式返回APP之後,在setSystemProcess函式中,給它的一些成員變數賦特殊值,這樣針對system_server的ProcessRecord就建立完畢了。

AMS中還有一個成員變數mProcessNames,原型是:

final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>();
返回去看看這個所謂的ProcessMap,內部在4.4版本中使用的是ArrayMap這種資料結構,而在4.0的版本中使用的是hashmap。看來在Android中大量使用了ArrayMap這種資料結構。也是優化的一個方面。

函式setSystemProcess總結:

1.註冊一些服務

2.根據PKMS返回的applicationinfo初始化Android執行環境,並建立一個代表system_server程序的ProcessRecord。也就是AMS可以管理system_server【插一句,話說用一個執行緒管理程序,這個有點。。。】


5. 現在回到system_server中去,到了installSystemProviders函式中了:

這個主要是載入setting資料庫。

也就是將SettingsProvider,apk載入到system_server中去。

    public static final void installSystemProviders() {
        List<ProviderInfo> providers;
        synchronized (mSelf) {
            ProcessRecord app = mSelf.mProcessNames.get("system", Process.SYSTEM_UID);
            providers = mSelf.generateApplicationProvidersLocked(app);
            if (providers != null) {
                for (int i=providers.size()-1; i>=0; i--) {
                    ProviderInfo pi = (ProviderInfo)providers.get(i);
                    if ((pi.applicationInfo.flags&ApplicationInfo.FLAG_SYSTEM) == 0) {
                        Slog.w(TAG, "Not installing system proc provider " + pi.name
                                + ": not system .apk");
                        providers.remove(i);
                    }
                }
            }
        }
        if (providers != null) {
            mSystemThread.installSystemProviders(providers);
        }

        mSelf.mCoreSettingsObserver = new CoreSettingsObserver(mSelf);

        mSelf.mUsageStatsService.monitorPackages();
    }

首先拿回之前在installSystemApplication中建立的那個ProcessRecord,它代表System_server程序。

然後看那個關鍵的呼叫:

providers = mSelf.generateApplicationProvidersLocked(app);
這個函式中第一步是想PKMS查詢滿足要求的ProviderInfo,然後將他們分別儲存的AMS和ProcessRecord中的對應的資料結構中。

AMS儲存ContentProvider的原因是他要管理ContentProvider,

ProcessRecord儲存的原因是COntentProvider最終會落實到一個程序中,其實也是為了方便AMS管理,如果程序一旦推退出,AMS需要把其中的ContentProvider資訊從系統中除去。
得到Provider資訊之後,下一步工作就是呼叫ActivityThread的installSystemProvider函式來建立ContentProvider例項,即SettingProvider物件。

這個函式內部的呼叫InstallContentProvider函式(是所有ContentProvider產生的必經之路)

這個函式的程式碼如下:

 private void installContentProviders(
            Context context, List<ProviderInfo> providers) {
        final ArrayList<IActivityManager.ContentProviderHolder> results =
            new ArrayList<IActivityManager.ContentProviderHolder>();

        for (ProviderInfo cpi : providers) {
            if (DEBUG_PROVIDER) {
                StringBuilder buf = new StringBuilder(128);
                buf.append("Pub ");
                buf.append(cpi.authority);
                buf.append(": ");
                buf.append(cpi.name);
                Log.i(TAG, buf.toString());
            }
            IActivityManager.ContentProviderHolder cph = installProvider(context, null, cpi,
                    false /*noisy*/, true /*noReleaseNeeded*/, true /*stable*/);
            if (cph != null) {
                cph.noReleaseNeeded = true;
                results.add(cph);
            }
        }

        try {
            ActivityManagerNative.getDefault().publishContentProviders(
                getApplicationThread(), results);
        } catch (RemoteException ex) {
        }
    }

在4.0中使用的搜尋是Iterator,在4.4中改為了for,功能上應該都差不多。都是通過installProvider函式來火得一個IContentProvider物件。然後向AMS釋出這個ContentProvider例項,這樣之後,一個APK中宣告的ContentProvider才能發揮其該有的作用。

【先建立+後註冊】

ActivityThread中的intallProvider函式在後半部分跟4.0變化特別大,不過暫時我還看不大懂是什麼,暫且先放下吧。

然後就是釋出,工作流程跟4.0一樣:但是儲存的資料結構變了。

現根據呼叫者的PID找到對應的ProcessRecord,該ProcessRecord的pubProvider中儲存了ContentProviderRecord資訊,該資訊由前面的AMS的generateApplicationProvidersLocked函式根據Package本身的資訊生成,如果判斷返回成功,則將改ContentProvider以及對應的authority加入到mProviderMap中

在4.0中使用的是mProvidersByClass,4.4中使用的是mProviderMap,他的原型是就是一個ProviderMap,裡邊把4.0中使用的Map全部扔到一個類中。應該也是方便管理吧,再者根據這些變動的程式碼,可以明顯的看出Android以後一定會是一個多使用者的系統,這個類中也添加了關於User的東西。

mLaunchingProviders和最後的notifyAll函式用於通知那些等待ContentProvider所在程序啟動的客戶端程序。

例如: 程序A要查詢一個數據庫,需要通過程序B中的某個ContentProvider來實施,如果B還沒有啟動,那麼AMS需要先啟動B,這段時間內,A需要等待B啟動並註冊對應的ContentProvider,一旦B註冊完成,就需要告知A退出等待以繼續後續的查詢工作。

現在一個SettingProvider就算是在系統中正式掛牌並註冊了。

這個InstallSystemProvider函式其實就是用於啟動SettingProvider。

6. 最後是systemReady

同樣的,在4.0中的第一階段的工作上,作為最後一個廣播的接收者註冊一個回撥通知,當他處理完廣播之後,會呼叫該回調,而在4.4中的物件變為了最後一個使用者的最後一個廣播接收者。其他的也太無變化。

第二階段:殺死那些在AMS還未啟動完畢就先啟動的應用程序,然後從Settings資料庫獲取配置資訊。

第三節階段:呼叫SystemReady設定的回撥物件goingCallback的run函式,啟動那些聲明瞭persistent的APK,啟動桌面。

goingCallback函式會啟動SystemUI,呼叫其他服務的systemReady,啟動watchDog。

SystemUIService由SystemUi.apk提供,實現了系統狀態列。

通過resumeTopActivityLocked函式啟動Home介面,也就是所謂的Launcher。

最後傳送ACTION_BOOT_COMPLETED廣播。當HomeAcitvity啟動後,ActivityStack的activityIdleInternal被呼叫,在最後的程式碼中會呼叫mService.finishBooting函式。

至此AMS就啟動完畢了:

首先AMS的Main函式,穿件AMS例項,建立Android執行環境,得到一個ActivityThread和一個Context物件,

然後呼叫AMS的setSystemProcess函式,該函式註冊AMS和meminfo服務等到ServiceManager中,另外,為system_server建立一個ProcessRecord物件。此時,system_server也被AMS所管理。

再然後呼叫AMS的installSystemProvider函式,為system_server載入SettingProvider 。

最後呼叫AMS的systemReady函式,做系統啟動完畢前最後一些掃尾工作,該函式呼叫完畢後,Home將會出現在使用者面前。