国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

手把手帶你搞懂AMS啟動原理

這篇具有很好參考價值的文章主要介紹了手把手帶你搞懂AMS啟動原理。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

徹底搞懂AMS即ActivityManagerService,看這一篇就夠了


前言

最近那么多教學視頻(特別是搞車載的)都在講AMS,可能這也跟要快速啟動一個app(甚至是提高安卓系統啟動速度有關),畢竟作為安卓系統的核心系統服務之一,AMS以及PMS都是很重要的,而我之前在 應用的開端–PackageManagerService(PMS) 已經很詳細地講解過PMS,大家有興趣的可以去看看。

我們都知道當手機開機時,或者是安裝某個apk時,PMS會去掃描加載/data/app目錄和system/app里每個apk里的所有類信息并且進行解析,然后緩存到PMS它自己里面定義好的集合里,供AMS使用,AMS即ActivityManagerService就能根據PMS里面的這些類信息來進行創(chuàng)建入口Activity等四大組件類并啟動。下面將詳細講解AMS是怎樣工作的。


提示:以下是本篇文章正文內容

一、回顧PackageManagerService

講AMS之前,還是得回顧一下PMS的工作流程,PMS會把每個apk進行掃描,然后分別把每個apk里的信息都緩存在mPackages集合里:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

它是PackageParser.Package類型,所以我們看看這個類里的內容:

public final static class Package implements Parcelable {

        @UnsupportedAppUsage
        public String packageName;

        // The package name declared in the manifest as the package can be
        // renamed, for example static shared libs use synthetic package names.
        public String manifestPackageName;
        ...
		
		// For now we only support one application per package.
        @UnsupportedAppUsage
        public ApplicationInfo applicationInfo = new ApplicationInfo();

        @UnsupportedAppUsage
        public final ArrayList<Permission> permissions = new ArrayList<Permission>(0);
        @UnsupportedAppUsage
        public final ArrayList<PermissionGroup> permissionGroups = new ArrayList<PermissionGroup>(0);
        @UnsupportedAppUsage
        public final ArrayList<Activity> activities = new ArrayList<Activity>(0);
        @UnsupportedAppUsage
        public final ArrayList<Activity> receivers = new ArrayList<Activity>(0);
        @UnsupportedAppUsage
        public final ArrayList<Provider> providers = new ArrayList<Provider>(0);
        @UnsupportedAppUsage
        public final ArrayList<Service> services = new ArrayList<Service>(0);
        @UnsupportedAppUsage
        public final ArrayList<Instrumentation> instrumentation = new ArrayList<Instrumentation>(0);

        @UnsupportedAppUsage
        public final ArrayList<String> requestedPermissions = new ArrayList<String>();
        ...
}

明顯可以看到這些集合都是分別緩存四大組件以及一些權限以及跳轉意圖等信息,每個apk對應一個Package:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

Package是在PMS里,而PMS又是作為一個對象被SystemServer所擁有,SystemServer進程是被zygote進程所開啟的,而
zygote進程又是給init進程孵化的:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

init進程是安卓手機開機后第一個啟動的進程:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試
我們可以從系統源碼中看看它的配置腳本文件init.rc寫了什么內容:

# Copyright (C) 2012 The Android Open Source Project
#
# IMPORTANT: Do not create world writable files or directories.
# This is a common source of Android security bugs.
#

import /init.environ.rc
import /init.usb.rc
import /init.${ro.hardware}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /init.usb.configfs.rc
import /init.${ro.zygote}.rc

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_score_adj -1000

    # Disable sysrq from keyboard
    write /proc/sys/kernel/sysrq 0

    # Set the security context of /adb_keys if present.
    restorecon /adb_keys

    # Shouldn't be necessary, but sdcard won't start without it. http://b/22568628.
    mkdir /mnt 0775 root system

    # Set the security context of /postinstall if present.
    restorecon /postinstall

    start ueventd

on init
    sysclktz 0

    # Mix device-specific information into the entropy pool
    copy /proc/cmdline /dev/urandom
    copy /default.prop /dev/urandom

    # Backward compatibility.
    symlink /system/etc /etc
    symlink /sys/kernel/debug /d

    # Link /vendor to /system/vendor for devices without a vendor partition.
    symlink /system/vendor /vendor

    # Mount cgroup mount point for cpu accounting
    mount cgroup none /acct cpuacct
    mkdir /acct/uid

    # Create energy-aware scheduler tuning nodes
    mkdir /dev/stune
    mount cgroup none /dev/stune schedtune
    mkdir /dev/stune/foreground
    mkdir /dev/stune/background
    mkdir /dev/stune/top-app
    ...

它里面是配置服務的邏輯,很多手機廠商在這里設置它們專屬的一些常駐服務的地方,就是在這里改動的,其中可以看到這里

import /init.${ro.zygote}.rc

配置了zygote進程,引入了zygote.rc文件,我們來看看32位下的zygote.rc文件:

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
    class main
    priority -20
    user root
    group root readproc
    socket zygote stream 660 root system
    onrestart write /sys/android_power/request_state wake
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

代碼很少,但可以看到,這里面它啟動了zygote進程服務的main方法。

那么再來總結一下整個系統啟動流程:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

SystemServer的啟動服務、核心服務以及其他服務如下源碼所示啟動,啟動服務就包括PMS和AMS等這些服務:

...
		// Start services.
        try {
            traceBeginAndSlog("StartServices");
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
            SystemServerInitThreadPool.shutdown();
        } catch (Throwable ex) {
            Slog.e("System", "******************************************");
            Slog.e("System", "************ Failure starting system services", ex);
            throw ex;
        } finally {
            traceEnd();
        }
        ...

startBootstrapServices()方法里就是啟動AMS和PMS等:

		...
        // Activity manager runs the show.
        traceBeginAndSlog("StartActivityManager");
        // TODO: Might need to move after migration to WM.
        ActivityTaskManagerService atm = mSystemServiceManager.startService(
                ActivityTaskManagerService.Lifecycle.class).getService();
        mActivityManagerService = ActivityManagerService.Lifecycle.startService(
                mSystemServiceManager, atm);
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);
        mWindowManagerGlobalLock = atm.getGlobalLock();
        traceEnd();
		...
		
		traceBeginAndSlog("StartPackageManagerService");
        try {
            Watchdog.getInstance().pauseWatchingCurrentThread("packagemanagermain");
            mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        } finally {
            Watchdog.getInstance().resumeWatchingCurrentThread("packagemanagermain");
        }
        mFirstBoot = mPackageManagerService.isFirstBoot();
        mPackageManager = mSystemContext.getPackageManager();
        traceEnd();
        ...


但這里要注意的是PMS和AMS它們是作為一個對象被SystemServer所持有的:

public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
        // Self-check for initial settings.
        PackageManagerServiceCompilerMapping.checkProperties();

        PackageManagerService m = new PackageManagerService(context, installer,
                factoryTest, onlyCore);
        m.enableSystemUserPackages();
        ServiceManager.addService("package", m);
        final PackageManagerNative pmn = m.new PackageManagerNative();
        ServiceManager.addService("package_native", pmn);
        return m;
    }

main()方法只是一個普通的構造PackageManagerService它自己對象的方法,它是直接new的,并沒有開啟單獨的進程,AMS也是如此:

public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            final String name = serviceClass.getName();
            Slog.i(TAG, "Starting " + name);
            Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);

            // Create the service.
            if (!SystemService.class.isAssignableFrom(serviceClass)) {
                throw new RuntimeException("Failed to create " + name
                        + ": service must extend " + SystemService.class.getName());
            }
            final T service;
            try {
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            }
            ...
            startService(service);
            return service;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }

它也只是通過反射去實例化自己,而且都是在SystemServer里進行的。以下這些服務對象都被SystemServer所持有:

    // TODO: remove all of these references by improving dependency resolution and boot phases
    private PowerManagerService mPowerManagerService;
    private ActivityManagerService mActivityManagerService;
    private WindowManagerGlobalLock mWindowManagerGlobalLock;
    private WebViewUpdateService mWebViewUpdateService;
    private DisplayManagerService mDisplayManagerService;
    private PackageManagerService mPackageManagerService;
    private PackageManager mPackageManager;
    ...

既然我們都知道PMS會去掃描Apk文件,那么它的掃描方法就是使用Packageparser掃描的,最終得到Package:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

所以我們完全可以通過反射PackageParser,然后調用它的parsePackage方法,傳apk路徑進去,然后得到Package對象,拿到這個Package對象后,你就可以通過DexClassLoader配合著Package對象去反射構造你想要的apk里的類對象,這樣一種思路是可以被用到很多應用場景的,比如熱修復、插件化以及換膚功能等。

	DexClassLoader dexClassLoader = new DexClassLoader("apk路徑","apk被緩存后的dex文件路徑"
							,null,classLoader類加載器 );
	Class clazz = dexClassLoader.loadClass("要反射的類全類名路徑");	
	...					

這樣就已經相當于把一個apk文件里的某個類文件對象給找出來了,當然還有很多細節(jié)以及具體用法就靠大家去自己琢磨了。

所以,總的來說,PMS它只是把apk里也就是清單文件里定義的四大組件以及一些權限、意圖信息等緩存在它里面的Package類里,然后供AMS去迅速定位到某一個類,然后創(chuàng)建它和啟動它,這樣就能確保我們啟動App時不會耗時太長。

二、ActivityManagerService

當我們安卓手機開機成功后,就處在桌面上,我們點擊某個app的圖標時,Launcher進程就會請求SystemServer進程里的AMS去創(chuàng)建這個app的入口(啟動)Activity,這時AMS就會請求zygote進程去孵化出該app應用進程:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

只要是跟zygote進程通信的都采用socket方式之外,其他進程互相通信都是采用binder方式。使用過binder來進行進程間通信都知道,binder相當于強引用,客戶端能直接獲取到服務端引用,從而可直接接觸到服務端進程對象及其方法,從安全方面考慮這樣是不安全的;另外,zygote進程一旦掛掉,會影響到整個系統重啟,因此不能讓其他進程能輕易影響到zygote進程,本身Socket就相當于弱引用,只通過參數進行通信,安全性就較強,因此就采用socket方式來跟zygote進程通信。

我們來看看zygote進程的main方法:

@UnsupportedAppUsage
    public static void main(String argv[]) {
        ZygoteServer zygoteServer = null;

        // Mark zygote start. This ensures that thread creation will throw
        // an error.
        ZygoteHooks.startZygoteNoThreadCreation();

        // Zygote goes into its own process group.
        try {
            Os.setpgid(0, 0);
        } catch (ErrnoException ex) {
            throw new RuntimeException("Failed to setpgid(0,0)", ex);
        }

        Runnable caller;
        ...

ZygoteServer里可以看到有一個叫LocalServerSocket變量:

/**
Server socket class for zygote processes. 
Provides functions to wait for commands on a UNIX domain socket, 
and fork off child processes that inherit the initial state of the VM.% 
Please see ZygoteArguments for documentation on the client protocol.
*/
class ZygoteServer {
    // TODO (chriswailes): Change this so it is set with Zygote or ZygoteSecondary as appropriate
    public static final String TAG = "ZygoteServer";

    ...

    /** The default value used for the USAP_POOL_SIZE_MIN device property */
    private static final String USAP_POOL_SIZE_MIN_DEFAULT = "1";

   ...
     */
    private LocalServerSocket mZygoteSocket;

    /**
     * The name of the unspecialized app process pool socket to use if the USAP pool is enabled.
     */
    private LocalServerSocket mUsapPoolSocket;

    ...

LocalServerSocket就是Socket對象:


package android.net;

...

/**
 * Non-standard class for creating an inbound UNIX-domain socket
 * in the Linux abstract namespace.
 */
public class LocalServerSocket implements Closeable {
    private final LocalSocketImpl impl;
    private final LocalSocketAddress localAddress;

    /** 50 seems a bit much, but it's what was here */
    private static final int LISTEN_BACKLOG = 50;

    /**
     * Creates a new server socket listening at specified name.
     * On the Android platform, the name is created in the Linux
     * abstract namespace (instead of on the filesystem).
     * 
     * @param name address for socket
     * @throws IOException
     */
    public LocalServerSocket(String name) throws IOException
    {
    	...
    }
    ...

Launcher進程請求AMS創(chuàng)建該app進程的入口Activity時,AMS會請求Zygote進程孵化出該app應用進程先,然后AMS就會通過binder方式去跟該app應用進程進行通信,也就是在app應用進程里通過ActivityThead去創(chuàng)建入口Activity并啟動:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

這里要注意的是app進程是不能逆向跟zygote通信,zygote是單向孵化出app進程,所以它們不是雙向的。只有一些系統服務進程才能直接跟zygoet通信。

三、源碼

接下來我們將從源碼的角度去分析整個啟動過程,首先來看整個過程的時序圖:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

Launcher首先會去調用啟動Activity方法,由于第一次點擊啟動活動肯定是為空(就算是不為空),最終還是會去調用Instrumentation的execStartActivity()方法:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
            @Nullable Bundle options) {
        if (mParent == null) {
            options = transferSpringboardActivityOptions(options);
            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) {
                // If this start is requesting a result, we can avoid making
                // the activity visible until the result is received.  Setting
                // this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
                // activity hidden during this time, to avoid flickering.
                // This can only be done when a result is requested because
                // that guarantees we will get information back when the
                // activity is finished, no matter what happens to it.
                mStartedActivity = true;
            }

            cancelInputsAndStartExitTransition(options);
            // TODO Consider clearing/flushing other event sources and events for child windows.
        } else {
            if (options != null) {
                mParent.startActivityFromChild(this, intent, requestCode, options);
            } else {
                // Note we want to go through this method for compatibility with
                // existing applications that may have overridden it.
                mParent.startActivityFromChild(this, intent, requestCode);
            }
        }
    }

點進去看該方法execStartActivity(),里面也是調用了一個ActivityTaskManager.getService()的startActivity()方法:

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
            
       IApplicationThread whoThread = (IApplicationThread) contextThread;
        Uri referrer = target != null ? target.onProvideReferrer() : null;
        if (referrer != null) {
            intent.putExtra(Intent.EXTRA_REFERRER, referrer);
        }
      
        ...
       try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess(who);
            int result = ActivityManager.getService()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

而這里的getService返回的是IActivityManager對象,它其實是Binder接口對象,通過它可以拿到AMS對象:

public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

private static final Singleton<IActivityManager> IActivityManagerSingleton =
            new Singleton<IActivityManager>() {
                @Override
                protected IActivityManager create() {
                    final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
                    final IActivityManager am = IActivityManager.Stub.asInterface(b);
                    return am;
                }
};

所以現在就可以調用AMS的startActivity方法:

	@Override
    public final int startActivity(IApplicationThread caller, String callingPackage,
            Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
            int startFlags, ProfilerInfo profilerInfo, Bundle bOptions) {
        return startActivityAsUser(caller, callingPackage, intent, resolvedType, resultTo,
                resultWho, requestCode, startFlags, profilerInfo, bOptions,
                UserHandle.getCallingUserId());
    }

系統中的所有應用進程都是由Zygote進程fork出來的,SystemServer進程是系統進程,它里面有很多系統服務,例如ActivityManagerService、PackageManagerService、WindowManagerService等等都是存在SystemServer進程被創(chuàng)建后啟動;ActivityManagerServices(AMS)是一個服務端對象,負責所有的Activity的生命周期,AMS通過Binder機制與App應用進程通信,那就代表App應用進程是能夠拿到AMS對象,而AMS也就能調度App進程去創(chuàng)建Activity等這些組件并且啟動它們。

而ActivityThread是App應用進程里的類,是UI線程/主線程,它的main()方法是APP的真正入口;ApplicationThread是一個實現了IBinder接口的ActivityThread內部類,它跟ActivityThread之間是通過Handler機制通信的,這樣ApplicationThread就能讓ActivityThread和AMS的所在進程間通信(也就是應用進程與SystemServer進程通信);Instrumentation可以理解為ActivityThread的一個工具類,在ActivityThread中初始化,一個進程只存在一個Instrumentation對象,負責調用Activity的生命周期方法:

public void callActivityOnCreate(Activity activity, Bundle icicle,
            PersistableBundle persistentState) {
        prePerformCreate(activity);
        activity.performCreate(icicle, persistentState);
        postPerformCreate(activity);
    }

activity隨后就執(zhí)行它的生命周期方法:

final void performCreate(Bundle icicle, PersistableBundle persistentState) {
        ...
        if (persistentState != null) {
            onCreate(icicle, persistentState);
        } else {
            onCreate(icicle);
        }
       ...
    }

說到這里,我們也可以去看看29以下的版本的源碼是怎樣的,比如23里的Instrumentation調用的execsStartActivity方法:

public ActivityResult execStartActivity(
            Context who, IBinder contextThread, IBinder token, Activity target,
            Intent intent, int requestCode, Bundle options) {
        ...
        try {
            intent.migrateExtraStreamToClipData();
            intent.prepareToLeaveProcess();
            int result = ActivityManagerNative.getDefault()
                .startActivity(whoThread, who.getBasePackageName(), intent,
                        intent.resolveTypeIfNeeded(who.getContentResolver()),
                        token, target != null ? target.mEmbeddedID : null,
                        requestCode, 0, null, options);
            checkStartActivityResult(result, intent);
        } catch (RemoteException e) {
            throw new RuntimeException("Failure from system", e);
        }
        return null;
    }

它的調用是由ActivityManagerNative.getDefault()來執(zhí)行,那么來跟蹤一下getDefault()方法:

//Retrieve the system's default/global activity manager.
static public IActivityManager getDefault() {
        return gDefault.get();
    }

這個gDefault是一個內部類,而且是static修飾的:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

我們點進去看它的詳情:

private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
        protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }
    };

它是一個抽象類Singleton,繼續(xù)看這個類的詳情:

public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

看它的get方法,就可以推測到,它實際上就是一個單例,只不過是由實現它之后子類自己實現的create方法來決定具體邏輯,那么就來看實現了它的IActivityManager的create方法:

protected IActivityManager create() {
            IBinder b = ServiceManager.getService("activity");
            if (false) {
                Log.v("ActivityManager", "default service binder = " + b);
            }
            IActivityManager am = asInterface(b);
            if (false) {
                Log.v("ActivityManager", "default service = " + am);
            }
            return am;
        }

可以看到,里面是調用了ServiceManager.getService()方法。那么現在來看,當我們的App應用進程調用gDafault.get()方法時,返回的是一個單例對象Singleton,而這個單例對象對應的也就是IActivityManager對象(因為IActivityManager實現了Singleton),它在這里構造時如果是空的,則會調用抽象方法create方法生成,而具體的create實現方法里調用了ServiceManager的getService方法,這樣就通過IBinder擁有了AMS的引用了。

值得一提的是,作為app應用開發(fā)者,我們可以使用反射去獲取Singleton的mInstance變量,因為mInstance它就是AMS,而且它是單例的,即只有一份:

public abstract class Singleton<T> {
    private T mInstance;
   ...
}

四、Hook

既然我們上面說到了可以去反射Singleton的mInstance變量,從而得到AMS,這個過程實際上就是常說的Hook技術。Hook技術其實是在A與B交流的過程中,在中途把消息給攔截,然后再做一些自定義操作,最后再把消息給傳去給B:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

不過記住的是,hook之后,不能影響到后續(xù)流程,也就是說你的hook處理怎么處理都行,但不能影響到整個流程,否則就會導致整個流程錯亂而跑不動了,因為這是一個一環(huán)扣一環(huán)的流程,你現在是從中間hook進來,做了一些額外的處理,勢必會影響到流程,因此你的處理流程里必須要讓hook之后回到原來的流程順序,從而保證程序能繼續(xù)運行。

而hook一般是hook系統進程,這才有意義:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

一般來說,自己開發(fā)的應用不需要hook,因為源碼就是我們自己寫的,直接使用就可以,而一些系統應用因為安全性是不直接開放給開發(fā)者的,但有時又確實是需要使用它里面的一些功能,那這時候就可以去hook它們,因此hook可以分類為:

1)根據Hook的API語言劃分(Hook Java、Hook Native)
2)根據Hook的進程劃分(應用進程、全局)
3)根據Hook的實現方式劃分

去hook java相對來說會比較容易,使用反射和代理實現,從而修改SDK代碼的執(zhí)行流程。

我們來看看怎么去hook一下AMS啟動應用的入口Activity流程,我們現在假設找到了系統的源碼,其中ActivityManager類如下(一段偽代碼):

//系統層
public class ActivityManager {
    public IActivityManager activityManager = new IActivityManager();

    class IActivityManager{

        private String managerName;
        ...
    }
    ...
}

它已經在我們應用中開始前就在系統里運行,該IActivityManager對象已經在內存中存在,而現在我們應用層里無論怎么new它,或者使用反射去構造它,都不是原來那個運行著的系統對象,我們構造的只是一個在應用層里全新的一個對象,因此這樣是不能夠hook AMS啟動應用的入口Activity的流程,也就是沒有hook點。那這種情況下要怎么去hook呢?答案是用static去修飾IActivityManager:

//系統層
public class ActivityManager {
    public static IActivityManager activityManager = new IActivityManager();

    class IActivityManager{

        private String managerName;
        ...
    }
}

這時應用層里,可以通過反射去獲取這個靜態(tài)對象,因為它是static修飾,只有一份,因此不管你是不是一早就在內存里運行還是后來構造,整個內存都是只此一份,它也就是系統進程里獨有的那個對象了。

所以hook點往往就是去hook那些static修飾的對象,因為只有一份,所以可以使用反射去獲取它,同理我們也可以這樣使用hook去獲取AMS對象。

我們在上文分析源碼時知道,gDefalut里的mInstance是AMS對象,是單例的,只有一份,因此可以反射來獲取它:

 Field gDefault = null;
 Class<?> ActivityManagerNativecls = Class.forName("android.app.ActivityManagerNative");
 gDefault = ActivityManagerNativecls.getDeclaredField("gDefault");
 gDefault.setAccessible(true);
 Object defaultValue = gDefault.get(null);
 Class<?> SingletonClass = Class.forName("android.util.Singleton");
 Field mInstance = SingletonClass.getDeclaredField("mInstance");
 mInstance.setAccessible(true);

先獲取gDefalut變量,然后再獲取Singleton變量,最后獲取它的mInstance變量對象即ActivityManager對象:

Object iActivityManagerObject = mInstance.get(defaultValue);

這樣也就獲得了AMS對象了。不過這是在安卓23版本的情況下的hook邏輯,當回到安卓28版本的時候,這時源碼有變動了,變成:

public static IActivityManager getService() {
        return IActivityManagerSingleton.get();
    }

此時gDefalut變成了IActivityManagerSingleton,因此反射時就要做版本適配了,根據版本號的不同來反射對應的變量,進而取出它那唯一的AMS變量:

		Field gDefault = null;
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
            Class<?> ActivityManagerNativecls = Class.forName("android.app.ActivityManagerNative");
            gDefault = ActivityManagerNativecls.getDeclaredField("gDefault");
        } else if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
            Class<?> activityManager = Class.forName("android.app.ActivityManager");
            gDefault = activityManager.getDeclaredField("IActivityManagerSingleton");
        } else {
        	//其他版本就交給大家自行去研究源碼了
        }
        ...

繼續(xù)上面講到的拿到AMS對象后,我們就可以使用動態(tài)代理(之后會更新一篇專門講動態(tài)代理的文章,感興趣的大家可繼續(xù)期待)的方式來處理AMS對象了:

		...
		Object iActivityManagerObject = mInstance.get(defaultValue);
		Class<?> IActivityManagerIntercept = Class.forName("android.app.IActivityManager");
        Object proxy = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),
                new Class[]{IActivityManagerIntercept}, new AmsInvocationHandler(iActivityManagerObject));
        mInstance.set(defaultValue, proxy);
    
    	class AmsInvocationHandler implements InvocationHandler {
    	
			private Object iActivityManagerObject;
			public AmsInvocationHandler(Object iActivityManagerObject) {
                this.iActivityManagerObject = iActivityManagerObject;
            }
            
			@Override
            public Object invoke(Object o, Method method, Object[] args) throws Throwable {
				//Hook邏輯
				return method.invoke(iActivityManagerObject,args);
			}
		}	

使用動態(tài)代理是為了不影響到AMS對象,把處理過程都放在處理器來處理,起到隔離作用,還能監(jiān)聽AMS對象所執(zhí)行的方法。當啟動程序后,調用了hook方法后,系統使用AMS調用的方法都一一交由invoke方法去監(jiān)聽,然后我們返回method的invoke()方法讓它繼續(xù)執(zhí)行,不斷監(jiān)聽。

PMS同樣也是可以hook的,因為它也是static修飾的,只有一份:

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;
    }

手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

反射方式去獲取PMS對象跟AMS一樣:

			// 獲取全局的ActivityThread對象
            Class<?> activityThreadClass = Class.forName("android.app.ActivityThread");
            Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
            Object currentActivityThread = currentActivityThreadMethod.invoke(null);//得到ActivityThread對象
 	
 			// 獲取ActivityThread里面原始的 sPackageManager
            Field sPackageManagerField = activityThreadClass.getDeclaredField("sPackageManager");
            sPackageManagerField.setAccessible(true);
            Object sPackageManager = sPackageManagerField.get(currentActivityThread);
            ...
            //使用動態(tài)代理處理sPackageManager

然后同樣使用動態(tài)代理去接收與處理PMS對象:

class HookPmsHandler implements InvocationHandler {

        private Object iPmsObject;

        public HookHandler(Object iPmsObject) {
            this.iPmsObject= iPmsObject;
        }

        @Override
        public Object invoke(Object o, Method method, Object[] args) throws Throwable {
            Log.i("david","----------------pms------------   "+method.getName());
            return method.invoke(iPmsObject,args);
        }
    }

這樣就能監(jiān)聽到PMS的執(zhí)行方法了。如果想獲取完整的hook代碼可以關我公號Pingred

現在我們通過動態(tài)代理來監(jiān)聽它整個啟動流程會執(zhí)行的各種方法,那其中肯定也包括startActivity方法,那我們自然就能利用它來修改Activity之間跳轉的邏輯,比如現在當我從MainActivity跳轉到TwoActivity時根據是否登錄狀態(tài)來決定要跳轉到登錄頁面還是直接跳轉到TwoActivity,那么上面AMS的動態(tài)代理方法就會觸發(fā),而我們就監(jiān)聽startActivity的方法,因為AMS肯定是會調用這個方法的,然后再判斷此時登錄狀態(tài)來進行對應的跳轉邏輯。

這樣就可以僅僅在一個地方進行管理和判斷所有Activity的登錄狀態(tài),然后實現是否要跳轉的集中式登錄判斷功能,這是非常好用的,這樣也就不需要在每個Activity上都要進行判斷,不過要注意的是我們上面已經講到了hook的原則,就是處理了你要處理的邏輯之后,還要讓它的后續(xù)流程能順利執(zhí)行下去,所以上面例子要修改跳轉到哪個Activity上可以通過修改它的Intent意圖參數來實現。還有很多這樣的場景就交由讀者自己去琢磨了。

五、總結

整個app啟動過程,所有參與的類以及它們的關系流程:
手把手帶你搞懂AMS啟動原理,移動互聯網,android,ActivityManagerService,java,android,面試

當點擊應用圖標的那刻起,Launcher進程會去發(fā)送請求AMS要啟動Activity(通過Binder方式通信),AMS這時會通過Socket方式請求Zygote去創(chuàng)建應用進程,應用進程被Zygote孵化后,應用進程就會通過binder方式去請求AMS通信,然后AMS就開始從PMS里去獲取需要啟動的Activity里的信息,然后把這些信息都通過binder方式發(fā)送給應用進程,應用進程里的ApplicationThread接受到后,就會通過Handler方式發(fā)送給ActivityThread去創(chuàng)建與啟動Activity,之后的具體實現就是由ActivityThread的Instrumentation去實現,進而啟動Activity。文章來源地址http://www.zghlxwxcb.cn/news/detail-529644.html

到了這里,關于手把手帶你搞懂AMS啟動原理的文章就介紹完了。如果您還想了解更多內容,請在右上角搜索TOY模板網以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。如若轉載,請注明出處: 如若內容造成侵權/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經查實,立即刪除!

領支付寶紅包贊助服務器費用

相關文章

  • 從0手把手帶你搭建pytorch深度學習

    從0手把手帶你搭建pytorch深度學習

    目錄 一、查看電腦有NVIDIA顯卡沒 二、更新電腦驅動 三、安裝CUDA ToolKit和CUDNN 1、查看顯卡驅動版本 2、查看合適的CUDA版本 3、下載CUDA ToolKit 4、安裝CUDA 5、查看是否安裝成功 6、安裝CUDNN 7、CUDNN配置 四、安裝anaconda 五、安裝pycharm 六、搭建pytorch深度學習環(huán)境 1、進入Anaconda Pr

    2024年02月07日
    瀏覽(95)
  • 手把手帶你配置一個DHCP服務器

    最近部門內部成立一個網絡興趣小組,初衷是通過網絡知識學習,在遇到網絡問題時能夠承擔起一個與網絡側同學有效溝通的“連接人”的角色,求學這么多年其實也陸續(xù)學了不少的網絡相關課程,本科的計算機網絡、碩士的高等計網等,不過當時大多都停留在理論層面,趁

    2024年02月05日
    瀏覽(232)
  • 手把手帶你做一套畢業(yè)設計-征程開啟

    手把手帶你做一套畢業(yè)設計-征程開啟

    ?本文是《Vue + SpringBoot前后端分離項目實戰(zhàn)》專欄的開篇,文本將會包含我們創(chuàng)作這個專欄的初衷,專欄的主體內容,以及我們專欄的后續(xù)規(guī)劃。關于這套畢業(yè)設計的作者呢前端部分由狗哥負責,服務端部分則由天哥操刀。我們力求畢業(yè)生或者新手通過學完本專欄,可以開心

    2023年04月10日
    瀏覽(900)
  • 實戰(zhàn)項目:手把手帶你實現一個高并發(fā)內存池

    實戰(zhàn)項目:手把手帶你實現一個高并發(fā)內存池

    1.這個項目做的是什么? 當前項目是實現一個高并發(fā)的內存池,他的原型是google的一個開源項目tcmalloc,tcmalloc全稱Thread-Caching Malloc,即線程緩存的malloc,實現了高效的多線程內存管理,用于替代系統的內存分配相關的函數(malloc、free)。 2.項目目標 模擬實現出一個自己的高

    2023年04月26日
    瀏覽(58)
  • 手把手帶你啃透比特幣白皮書-摘要

    很多人雖然了解了區(qū)塊鏈,也可能參與了一些項目,但是可能沒有見過比特幣白皮書,也沒有讀過。我接下來就要和大家聊一聊,什么是白皮書,尤其是來給大家精讀一下比特幣的白皮書。 通過比特幣白皮書,你能夠 了解到真正的白皮書應該是什么樣形式的 。因為很多人可

    2024年02月02日
    瀏覽(147)
  • 真手把手帶你跑r3live

    真手把手帶你跑r3live

    實驗室來了臺機器人,上面的設備是依據r3live的設備選的,因為R3LIVE的效果太好了,特別感謝大佬的開源精神。這幾天把車子跑起來,就打算寫個博客記錄一下。 本人能力有限,可能某些地方會有些問題,若發(fā)現問題,還請指正。 效果如下: 在多傳感器融合slam中,由于會集

    2024年02月09日
    瀏覽(360)
  • 【神秘海域】[動圖] 結合題目-手把手帶你剖析 “帶環(huán)鏈表”

    【神秘海域】[動圖] 結合題目-手把手帶你剖析 “帶環(huán)鏈表”

    ?? 上一篇 【神秘海域】數據結構與算法 內容是 單鏈表及其接口 而本篇內容是對單鏈表的一個 非常重要 的補充: 帶環(huán)單鏈表 。它,是大廠面試時可能會提問的內容,非常的重要! 本篇就是要結合題目來 詳細分析一下 單鏈表的帶環(huán)問題 ?? 在詳細分析 帶環(huán)單鏈表 之前,

    2023年04月08日
    瀏覽(83)
  • 四步帶你爬蟲入門,手把手教學爬取電影數據

    四步帶你爬蟲入門,手把手教學爬取電影數據

    本文內容是通過Pycharm來進行實操 創(chuàng)建項目的虛擬環(huán)境,目的是為了不讓其他的環(huán)境資源干擾到當前的項目 本文將以豆瓣作為手把手學習參考,網址:https://movie.douban.com/top250, 1.?進入Terminal終端,安裝我們需要的scrapy模塊 pip install scrapy 2. 通過pycharm進入Terminal終端,輸入我們

    2024年02月22日
    瀏覽(361)
  • 云原生應用開發(fā),通過一個案例手把手帶你入門

    針對云勢所趨的市場發(fā)展。云計算和云原生應用已經成為主流技術趨勢,學習這類技能有遠見。可以開發(fā)出符合云原生運營模式的應用,滿足企業(yè)業(yè)務發(fā)展需要。 實現資源的高效利用和彈性擴展。通過微服務架構、容器技術、彈性計算等手段,構建出計算資源利用高、擴展靈活的

    2024年02月06日
    瀏覽(242)
  • 手把手教你 ,帶你徹底掌握八大排序算法【數據結構】

    手把手教你 ,帶你徹底掌握八大排序算法【數據結構】

    直接插入排序是一種簡單的插入排序法,其基本思想:是把待排序的記錄按其關鍵碼值的大小逐個插入到一個已經排好序的有序序列中,直到所有的記錄插入完為止,得到一個新的有序序列 可以理解為一遍摸撲克牌,一邊進行排序 在待排序的元素中,假設前面n-1(其中n=2)個數

    2024年02月02日
    瀏覽(105)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領取紅包,優(yōu)惠每天領

二維碼1

領取紅包

二維碼2

領紅包