Android 四大组件的一些总结

享受孤独

1.Activity

1.1 Activity的启动流程

先来安利一些知识点:

  • 1.zygote 1、Android是基于Linux系统的,而在Linux中,所有的进程都是由init进程直接或者是间接fork出来的,zygote进程也不例外 2、Android系统开启新进程的方式,是通过fork第一个zygote进程实现的。除了第一个zygote进程,其他应用所在的进程都是zygote的子进程

  • 2.SystemServer SystemServer也是一个进程,而且是由zygote进程fork出来的,fork过程,在zygote开启的时候,会调用ZygoteInit.main()进行初始化 ZygoteInit.java 的路径:/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

ZygoteInit的main()方法,我们可以看到:

try{
    ...
    /* Request to fork the system server process */
                pid = Zygote.forkSystemServer(
                        parsedArgs.mUid, parsedArgs.mGid,
                        parsedArgs.mGids,
                        parsedArgs.mRuntimeFlags,
                        null,
                        parsedArgs.mPermittedCapabilities,
                        parsedArgs.mEffectiveCapabilities);
            } catch (IllegalArgumentException ex) {
                throw new RuntimeException(ex);
            }

SystemServer.java 的路径:/frameworks/base/services/java/com/android/server/SystemServer.java

我们查看SystemServer的main()方法

   new SystemServer().run();

run()方法中,ActivityThread在这里初始化了,同时会启动android多个核心服务

private void createSystemContext() {
        ActivityThread activityThread = ActivityThread.systemMain();
        mSystemContext = activityThread.getSystemContext();
        mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);

        final Context systemUiContext = activityThread.getSystemUiContext();
        systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
    }


private void startBootstrapServices() {
    ...
    //初始化ActivityManagerService
    mActivityManagerService = mSystemServiceManager.startService(
        ActivityManagerService.Lifecycle.class).getService();
    mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
    //初始化电源管理服务
    mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
    
    // Now that the power manager has been started, let the activity manager
    // initialize power management features.
    traceBeginAndSlog("InitPowerManagement");
    mActivityManagerService.initPowerManagement();
     
     //初始化应用包管理服务   
    mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
          mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
          mFirstBoot = mPackageManagerService.isFirstBoot();
          mPackageManager = mSystemContext.getPackageManager();
    ...
    }

ActivityMangerService(属于SystemServer进程) 管理所有activity的生命周期,在Activity生命管理中AMS作为一种服务端对象, ,某个activity需要打开另一个activity,需要把对应activity包名类名通过Binder进行IPC传给ActivityMangerService, AMS控制当前activity 调用onPause()、onStop(),并且通过socket进行IPC来通知Zygote进程fork 出一个新进程(主要是ActivityThread) 供新app运行,每个Activity的启动过程则由其所属的进程具体来完成

ActivityThread 对外与AMS进程通过Binder 进行通信,比如告知AMS需启动另一个程序,接受来自AMS的指令暂停当前activity, 对内(app进程内),初始化了一个Instrumentation来具体控制调用activity 的生命周期方法

我们来看一个ActivityThread 如何与AMS通信,启动另一个activity

从 startActivity()一路进来,我们会跟到Activity类中这样一段代码:

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());
        }
        ...
        }

我们进到Instrumentation类中

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    ...
    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;
}

而ActivityManager.getService() 获得的正是实现了IActivityManager接口(aidl文件)的 ActivityManagerService 的引用

/**
 * @hide
 */
@UnsupportedAppUsage
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

@UnsupportedAppUsage
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()方法,通过aidl完成通信。

android 7.1 及以前版本(API<26) ActivityThread 通过与AMS的远程代理接口ActivityManagerProxy实现与AMS的进行IPC Instrumentation执行完execStartActivity时,是这样调用的

public ActivityResult execStartActivity(
        Context who, IBinder contextThread, IBinder token, Activity target,
        Intent intent, int requestCode, Bundle options) {
    ...
    try {
        intent.migrateExtraStreamToClipData();
        intent.prepareToLeaveProcess(who);
        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返回的是ActivityManagerService的远程接口,即ActivityManagerProxy AMS、ActivityManagerProxy都实现了同一个接口——IActivityManager

public abstract class ActivityManagerNative extends Binder implements IActivityManager
{

//从类声明上,我们可以看到ActivityManagerNative是Binder的一个子类,而且实现了IActivityManager接口
static public IActivityManager getDefault() {
    return gDefault.get();
}

//通过单例模式获取一个IActivityManager对象,这个对象通过asInterface(b)获得
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;
    }
};
}

所以对AMS 的访问就变成了对ActivityManagerProxy的访问

继续进行,调用到AMS的startActivity()之后一顿调用,到了

public final int startActivityAsUser(...) {
    ...
    // TODO: Switch to user app stacks here.
    return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
            ...
            .setMayWait(userId)
            .execute();

mActivityStartController.obtainStarter(),返回一个ActivityStarter,执行execute()的代码是这样的

int execute() {
    try {
        // TODO(b/64750076): Look into passing request directly to these methods to allow
        // for transactional diffs and preprocessing.
        if (mRequest.mayWait) {
            return startActivityMayWait(mRequest.caller, mRequest.callingUid,
                    ...
        } else {
            return startActivity(mRequest.caller, mRequest.intent, mRequest.ephemeralIntent,
                    ...
        }
    } finally {
        onExecutionComplete();
    }
}

继续一顿操作....(API<26)的源码之后是调用到 ActivityStack.startActivityLocked(),之后会调用 ActivityStack.resumeTopActivityInnerLocked() ,之后是 startPausingLocked,最终调用ActivityThread.performPauseActivity()暂停指定Activity

1.2 onConfigurationChanged()

通常activity在AndroidManifest文件中不不进行ConfigChange配置,当屏幕方向发生改变时,Activity会被销毁重建,如果有配置configChange="orientation|screenSize",则Activity不会被销毁重建,而是调用onConfigurationChanged方法
在上面的配置的情况下,如果语言改变了,Acitivyt会销毁重建,且不会调用onConfigurationChanged方法
扩展:
当用户接入一个外设键盘时,默认软键盘会自动隐藏,系统自动使用外设键盘.这个过程Activity的销毁和隐藏执行了两次.
并且onConfigurationChanged()周期不会调用.
其中一次的销毁重建可以肯定是因为外设键盘的插入和拔出.当设置android:configChanges="keyboardHidden|keyboard"之后.就不会销毁重建,而是调用onConfigurationChanged()方法
但是还有一次销毁重建一直存在,因为使用外设键盘,触摸屏不能使用了,除了键盘类型的改变,触摸屏也发生了变化,所以会多调用一次

1.3 onSaveInstanceState()和onRestoreInstanceState()
  • 1.如果是用户自动按下返回键,或程序调用finish()退出程序,是不会触发onSaveInstanceState()和onRestoreInstanceState()的.
  • 2.每次用户旋转屏幕时,您的Activity将被破坏并重新创建.当屏幕改变方向时,系统会破坏并重新创建前台Activity,因为屏幕配置已更改,
    您的Activity可能需要加载替代资源(例如布局).即会执行onSaveInstanceState()和onRestoreInstanceState()的.
    也就是说,当系统开始停止您的Activity时,它会调用onSaveInstanceState()(1),以便您可以指定要保存的其他状态数据,
    以防Activity必须重新创建实例.如果Activity被破坏并且必须重新创建相同的实例,则系统将(1)中定义的状态数据传递给onCreate()方法(2)和onRestoreInstanceState()方法(3), 在oncreate ()中恢复数据需要判断 // 检查是否正在重新创建一个以前销毁的实例 if (savedInstanceState != null) {} 然后才能在bundle中取数据
1.4 setResult()应该在什么时候调用
方式1.按BACK键从一个Activity退出来的,一按BACK,android就会自动调用Activity的finish()方法,
方法:重写onBackPressed()方法,捕获BACK事件,捕获到之后先setResult(), 然后super.onBackPressed()
方式2:按点击事件中显式的调用finish(),
方法:setResult(RESULT_OK);
        finish();

2.Service

  • 1.Service的onCreate,onStartCommand,onDestory等全部生命周期方法都运行在UI线程,ServiceConnection里面的回调方法也是运行在UI线程

  • 2.startService 启动的服务在启动后与activity就没有关联了,不受影响,独立运行

  • 3.bindService启动的服务开启后与activity 有关联,activity退出时,也必须调用unbindService来停止服务
    我们注意到activity 实现的ServiceConnection 类中重写的 onServiceConnected方法的第二个参数也是IBinder类型的,不难猜测onBind()方法返回的对象被传递到了这里.也就是说我们可以在onServiceConnected方法里拿到了MyService服务的内部类MyBinder的对象(继承自Binder),通过这个内部类对象,只要强转一下,我们可以调用这个内部类的非私有成员对象和方法,因为内部类持有外部类的引用,我们也可以拿到Service 的属性和方法.

  • 4.最后还有一点,同一个服务可以用两种方式一同开启,没有先后顺序的要求,MyService的onCreate只会执行一次.
    关闭服务需要stopService和unbindService都被调用,也没有先后顺序的影响

3.ContentProvider

介绍
ContentProvider 是Android中提供的专门用于不同应用进行数据共享的方式,它是一种进程间的通信,底层是用Binder实现的.
ContentProvider是系统为我们封装的,使得我们无须关心底层细节即可轻松实现IPC

    1.系统给我们提供了许多ContentProvider,比如通信录,日程表信息等,要跨进程访问这些信息,只需要通过ContentResolver的
    query,update,insert和delete方法即可. 使用游标cursor 进行操作时记得及时关闭

    2.ContentProvider的生命周期
    自定义一个ContentProvider只需继承ContentProvider类并实现六个抽象方法即可:onCreate,query,update,insert,delete和
    getType.onCreate代表ContentProvider的创建,一般来说我们需要做一些初始化工作,android ContentProvider
    的onCreate早于Application的onCreate而执行.

    3.ContentProvider的onCreate和CRUD运行在哪个线程及线程安全问题
    根据Binder的工作原理,我们知道这六个方法均运行在ContentProvider的进程中,
    除了onCreate由系统回调并运行在主线程中,其他五个方法均由外界回调并运行在Binder线程池中
    不是线程安全的,而如果在同一个进程访问ContentProvider,根据Binder的原理,同进程的Binder调用就是直接的对象调用,
    这个时候CRUD运行在调用者的线程中.另外,ContentProvider的内部存储不一定是sqlite,它可以是任意数据.比如使用普通文件

    自定义contentProvider:
    <provider
           android:name=".xxx.xxxProvider"
                   android:authorities="com.cwx.test.xxxProvider"
                   android:permission="com.cwx.provider"  //申明外界访问需要的权限
                   android:process=":provider"            //表面运行在独立进程
                   >
    </provider>

    外界访问方式:
    Uri uri = Uri.parse("content://com.cwx.test.xxxProvider");
    getContentResolver().query(uri,null,null,null,null);

    注意点
    1.当通过增删改方法导致ContentProvider数据发生变化时需要通过ContentResolver的notifyChange方法来通知外界数据发生改变.
    2.可以调用ContentResolver的registerContentObserver方法来注册观察者,通过unregisterContentObserver方法来反注册观察者.
    3.query,update,insert,delete存在多线程并发访问,需要做好线程同步.

4.BoradCastReciver 广播

应用场景:

    1.同一app内部的同一组件内的消息通信(单个或多个线程之间);

    2.同一app内部的不同组件之间的消息通信(单个进程);

    3.同一app具有多个进程的不同组件之间的消息通信;

    4.不同app之间的组件之间消息通信;

    5.Android系统在特定情况下与App之间的消息通信.

注意:
    1.在Android 8.0的平台上,应用不能对大部分的广播进行静态注册,也就是说,不能在AndroidManifest文件对有些广播进
      行静态注册,这里必须强调是有些广播还是能够注册的.比如,接收Android 开机的广播通过静态注册还是能够正常接收的
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,607评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,047评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,496评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,405评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,400评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,479评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,883评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,535评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,743评论 1 295
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,544评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,612评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,309评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,881评论 3 306
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,891评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,136评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,783评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,316评论 2 342

推荐阅读更多精彩内容