Activity那些事

自打学Android起,最先接触的一定是Activity,那是看待Activity很简单,当成servlet使用。确实年少无知啊,两者是完全不同的东西。今日再来认识Activity,却有别样风采啊。

Activity的作用主要是将界面呈现在屏幕上,并且是可交互的,用户可以直接操作。 这个里面就设计到了很多的东西了。首先是startActivity,这个是毫无疑问的,这个是最根本的,你得先知道如何启动activity的,知道这个activity是怎么来的,才能做下面的操作;然后就是呢,怎么将界面给呈现出来的;再者就是这个界面的交互上的事情了。这三个方面就是本次文章的总体概要了。下面容我细细道来~


activity有四大启动模式 standard   singleTop  singleTask  singleInstance.

standard:标准模式,这是系统默认的。当要启动一个Activity的时候,就会创建一个Activity实例,比如说,activity A 启动了activity B 无论当前栈中是否有 B这个activity实例,都会重新创建一个出来,会走Activity的生命周期。onCreate -> onStart -> onResume 这样。这里有一个问题,如果你使用了非Activity 类型的Context(如ApplicationContext) 来启动activity的话,会报错 Calling startActivity() from outside of an Activity  context requires the FLAG_ACTIVITY_NEW_TASK flag 除非你指定了 FLAG_ACTIVITY_NEW_TASK标记位,这样会重新创建一个栈给activity. 原因是在默认情况下,会直接进入activity所在的那个栈,可是非Activity 类型的Context 是 没有所谓的任务栈的。(摘自艺术探索)


singleTop :这个模式的意思就是,如果你启动了一个Activity ,然后次Activity 位于任务栈的栈顶,那么是不会重新去创建这个实例的,也不会走onCreate ,onStart 这些方法。但是onNewIntent方法会被调用,可以在这个回调里面拿到请求的信息。其实这个场景还是蛮少的,我知道有一个地方可以使用,就是在请求的过程中token失效的时候,由于一个页面内可能有多个并发请求,就是说有可能会出现多个接口遇到token失效的情况,在token失效的时候,我们需要让用户重新登录来获取新的token。设置了这个模式,在你遇到多个接口的返回都是token的时候,就可以只创建了一个LoginActivity实例。


singleTask:这个模式的意思是栈内复用,就是说在你statactivity 的时候,系统会去查找是否有此activity,如果有,就不会去重新创建一个实例,而是会复用它,因为启动一个Activity必定会处于栈顶的,所以这个模式下会把这个activity以上的所有activity都出栈。


singleInstance :这个模式下,会为Activity 重新创建一个任务栈,以后再启动这个Activity 的时候,就不会重新创建这个实例,而是复用。


四种启动模式简要的说了一下,就不过多讲解了,因为这不是主要讲的内容,想要了解的更详细的,请看艺术探索。

但是,这里有个坑要注意下,就是在singleTask模式下,如果使用startActivityForResult这个方法,5.0以下是不会回调onActivityResult方法的,5.0及以上是没毛病的,这里注意下就行了。

ok,要进入正题了,好开心啊~

这里就不说具体流程了,想知道具体流程请看艺术探索第九章的内容。

startActivity过程是一个进程内交互,这里大家可能会很意外,我同一个进程为毛要这样干。这个其实是基于Binder的IPC机制,进程内跟进程之间(ps:注意用词,自己体会)都会涉及到Binder通信,Binder通信我自己理解的也不是很清楚,所以大家请自行google。不卖关子了,这里最终是会交给ActivityManagerService(简称AMS)去处理的。那么是怎么到那一步的呢。

startActivity 会 调用startActivityForResult方法,然后这个方法又会调用Instrumentation的execStartActivity,这个方面里面做了什么呢?请看

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);
        }
        if (mActivityMonitors != null) {
            synchronized (mSync) {
                final int N = mActivityMonitors.size();
                for (int i=0; i<N; i++) {
                    final ActivityMonitor am = mActivityMonitors.get(i);
                    if (am.match(who, null, intent)) {
                        am.mHits++;
                        if (am.isBlocking()) {
                            return requestCode >= 0 ? am.getResult() : null;
                        }
                        break;
                    }
                }
            }
        }
        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;
    }

看到标红的地方了吗,这里会交给AMS来管理!!!是不是很疑惑,好,那我们再看


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;
        }
    };
看到没有,这个gDefault是一个单例,我们再看这个get方法返回的是什么
public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }

会返回create()方法返回的对象,所以上面返回的是am,那我们再看看这个是怎么来的,重点看这两句代码

IBinder b = ServiceManager.getService("activity");

 IActivityManager am = asInterface(b);

先说第一个。ServiceManager是管理着很多service的一个类,每一个service都是一个Binder对象,这个类里面有一个map集合来存储这些Binder对象,在系统初始化的时候会
把系统级的service存储起来,像键盘啊、剪切板啊这些service都会存储在这个类里面。

public void setSystemProcess() {
        try {
            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
            ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(this));
            ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
            ServiceManager.addService("dbinfo", new DbBinder(this));
            if (MONITOR_CPU_USAGE) {
                ServiceManager.addService("cpuinfo", new CpuBinder(this));
            }
            ServiceManager.addService("permission", new PermissionController(this));
            ServiceManager.addService("processinfo", new ProcessInfoService(this));

            ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                    "android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
            mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());

            synchronized (this) {
                ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
                app.persistent = true;
                app.pid = MY_PID;
                app.maxAdj = ProcessList.SYSTEM_ADJ;
                app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
                synchronized (mPidsSelfLocked) {
                    mPidsSelfLocked.put(app.pid, app);
                }
                updateLruProcessLocked(app, false, null);
                updateOomAdjLocked();
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(
                    "Unable to find android system package", e);
        }
    }
这段代码是我从AMS里面找到的,可以看到ServiceManager是干什么的了吧。我们这里只要看try语句里面的第一行代码。这里会把AMS 给存储起来,Context.ACTIVITY_SERVICE这里的值是"activity",这下知道
IBinder b = ServiceManager.getService("activity");
这段代码是什么意思了吧。这里返回的就是一个AMS对象。

我们再看上面所说的第二段代码,我们看下asInterface方法会返回什么

static public IActivityManager asInterface(IBinder obj) {
        if (obj == null) {
            return null;
        }
        IActivityManager in =
            (IActivityManager)obj.queryLocalInterface(descriptor);
        if (in != null) {
            return in;
        }

        return new ActivityManagerProxy(obj);
    }
这里会通过obj.queryLocalInterface去查看是进程内通信,还是进程之间通信。然后返回一个看上去像是一个代理类,没错这里是AMS的代理类AMP。所以真正跟客户端通信的是AMP。这里不去讲述这些类的具体细节,这里我主要讲的是怎样把这些东西给串起来。

到了AMS这里会经历好几个类的处理,然后到AcitvityThread这个类里面。这个类里面的main(String[] args)就是整个应用的入口了。刚才说到startActivity会到这个类里面,就是下面这段代码拉,

 public final void scheduleLaunchActivity(Intent intent, IBinder token, int ident,
                ActivityInfo info, Configuration curConfig, Configuration overrideConfig,
                CompatibilityInfo compatInfo, String referrer, IVoiceInteractor voiceInteractor,
                int procState, Bundle state, PersistableBundle persistentState,
                List<ResultInfo> pendingResults, List<ReferrerIntent> pendingNewIntents,
                boolean notResumed, boolean isForward, ProfilerInfo profilerInfo) {

            updateProcessState(procState, false);

            ActivityClientRecord r = new ActivityClientRecord();

            r.token = token;
            r.ident = ident;
            r.intent = intent;
            r.referrer = referrer;
            r.voiceInteractor = voiceInteractor;
            r.activityInfo = info;
            r.compatInfo = compatInfo;
            r.state = state;
            r.persistentState = persistentState;

            r.pendingResults = pendingResults;
            r.pendingIntents = pendingNewIntents;

            r.startsNotResumed = notResumed;
            r.isForward = isForward;

            r.profilerInfo = profilerInfo;

            r.overrideConfig = overrideConfig;
            updatePendingConfiguration(curConfig);

            sendMessage(H.LAUNCH_ACTIVITY, r);
        }
这里通过sendMessage方法会交给 H这个内部类,这个类是继承自Handler,所以这里会执行到ActivityThread里面的handleLaunchActivity方法

private void handleLaunchActivity(ActivityClientRecord r, Intent customIntent, String reason) {
        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        if (r.profilerInfo != null) {
            mProfiler.setProfiler(r.profilerInfo);
            mProfiler.startProfiling();
        }

        // Make sure we are running with the most recent config.
        handleConfigurationChanged(null, null);

        if (localLOGV) Slog.v(
            TAG, "Handling launch of " + r);

        // Initialize before creating the activity
        WindowManagerGlobal.initialize();

        Activity a = performLaunchActivity(r, customIntent);

        if (a != null) {
            r.createdConfig = new Configuration(mConfiguration);
            reportSizeConfigurations(r);
            Bundle oldState = r.state;
            handleResumeActivity(r.token, false, r.isForward,
                    !r.activity.mFinished && !r.startsNotResumed, r.lastProcessedSeq, reason);

            if (!r.activity.mFinished && r.startsNotResumed) {
                // The activity manager actually wants this one to start out paused, because it
                // needs to be visible but isn't in the foreground. We accomplish this by going
                // through the normal startup (because activities expect to go through onResume()
                // the first time they run, before their window is displayed), and then pausing it.
                // However, in this case we do -not- need to do the full pause cycle (of freezing
                // and such) because the activity manager assumes it can just retain the current
                // state it has.
                performPauseActivityIfNeeded(r, reason);

                // We need to keep around the original state, in case we need to be created again.
                // But we only do this for pre-Honeycomb apps, which always save their state when
                // pausing, so we can not have them save their state when restarting from a paused
                // state. For HC and later, we want to (and can) let the state be saved as the
                // normal part of stopping the activity.
                if (r.isPreHoneycomb()) {
                    r.state = oldState;
                }
            }
        } else {
            // If there was an error, for any reason, tell the activity manager to stop us.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(r.token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }
在这段代码里面可以看到调用了performLaunchActivity函数,这个函数主要是用来创建Activity,并且调用onCreate,onStart函数,再之后是handleResumeActivity函数,

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume, int seq, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        if (!checkAndUpdateLifecycleSeq(seq, r, "resumeActivity")) {
            return;
        }

        // If we are getting ready to gc after going to the background, well
        // we are back active so skip it.
        unscheduleGcIdler();
        mSomeActivitiesChanged = true;

        // TODO Push resumeArgs into the activity for consideration
        r = performResumeActivity(token, clearHide, reason);

        if (r != null) {
            final Activity a = r.activity;

            if (localLOGV) Slog.v(
                TAG, "Resume " + r + " started activity: " +
                a.mStartedActivity + ", hideForNow: " + r.hideForNow
                + ", finished: " + a.mFinished);

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

            // If the window hasn't yet been added to the window manager,
            // and this guy didn't finish itself or start another activity,
            // then go ahead and add the window.
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                r.window = r.activity.getWindow();
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                ViewManager wm = a.getWindowManager();
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                l.softInputMode |= forwardBit;
                if (r.mPreserveWindow) {
                    a.mWindowAdded = true;
                    r.mPreserveWindow = false;
                    // Normally the ViewRoot sets up callbacks with the Activity
                    // in addView->ViewRootImpl#setView. If we are instead reusing
                    // the decor view we have to notify the view root that the
                    // callbacks may have changed.
                    ViewRootImpl impl = decor.getViewRootImpl();
                    if (impl != null) {
                        impl.notifyChildRebuilt();
                    }
                }
                if (a.mVisibleFromClient && !a.mWindowAdded) {
                    a.mWindowAdded = true;
                    wm.addView(decor, l);
                }

            // If the window has already been added, but during resume
            // we started another activity, then don't yet make the
            // window visible.
            } else if (!willBeVisible) {
                if (localLOGV) Slog.v(
                    TAG, "Launch " + r + " mStartedActivity set");
                r.hideForNow = true;
            }

            // Get rid of anything left hanging around.
            cleanUpPendingRemoveWindows(r, false /* force */);

            // The window is now visible if it has been added, we are not
            // simply finishing, and we are not starting another activity.
            if (!r.activity.mFinished && willBeVisible
                    && r.activity.mDecor != null && !r.hideForNow) {
                if (r.newConfig != null) {
                    performConfigurationChangedForActivity(r, r.newConfig, REPORT_TO_ACTIVITY);
                    if (DEBUG_CONFIGURATION) Slog.v(TAG, "Resuming activity "
                            + r.activityInfo.name + " with newConfig " + r.activity.mCurrentConfig);
                    r.newConfig = null;
                }
                if (localLOGV) Slog.v(TAG, "Resuming " + r + " with isForward="
                        + isForward);
                WindowManager.LayoutParams l = r.window.getAttributes();
                if ((l.softInputMode
                        & WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION)
                        != forwardBit) {
                    l.softInputMode = (l.softInputMode
                            & (~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION))
                            | forwardBit;
                    if (r.activity.mVisibleFromClient) {
                        ViewManager wm = a.getWindowManager();
                        View decor = r.window.getDecorView();
                        wm.updateViewLayout(decor, l);
                    }
                }
                r.activity.mVisibleFromServer = true;
                mNumVisibleActivities++;
                if (r.activity.mVisibleFromClient) {
                    r.activity.makeVisible();
                }
            }

            if (!r.onlyLocalRequest) {
                r.nextIdle = mNewActivities;
                mNewActivities = r;
                if (localLOGV) Slog.v(
                    TAG, "Scheduling idle handler for " + r);
                Looper.myQueue().addIdleHandler(new Idler());
            }
            r.onlyLocalRequest = false;

            // Tell the activity manager we have resumed.
            if (reallyResume) {
                try {
                    ActivityManagerNative.getDefault().activityResumed(token);
                } catch (RemoteException ex) {
                    throw ex.rethrowFromSystemServer();
                }
            }

        } else {
            // If an exception was thrown when trying to resume, then
            // just end this activity.
            try {
                ActivityManagerNative.getDefault()
                    .finishActivity(token, Activity.RESULT_CANCELED, null,
                            Activity.DONT_FINISH_TASK_WITH_ACTIVITY);
            } catch (RemoteException ex) {
                throw ex.rethrowFromSystemServer();
            }
        }
    }

这里通过windowmanager.addView把decorview添加进来,然后再通知AMS,该Activity已变为resume状态,至此,Activity的视图就会显示在手机上了。

在我们重写onCreate()方法的时候通过setContentView(int laytouResID)来将我们自己的布局添加进去,这个位置是系统给我们预留的,就是说本来这个布局是存在的,其实这个布局就是DecorView,这个里面会有title、actionbar这些。 在setContentView这个方法里面,DecorView会通过addView这个方法把我们的布局添加进去。至此,这个布局的显示就完工了。

至此已经完成了两个问题,startActivity和布局的显示,还有一个就是交互问题。我简单说下逻辑,就不深入去剖析了。

首先我们的事件分发是先到Activity的,然后交由内部的Window来完成,Window再传递给DecorView。然后事件就会传递给我们的ViewGroup了,这里会调用ViewGroup的dispatchTouchEvent方法,如果ViewGroup拦截事件onInterceptTouchEvent返回true,则事件由ViewGroup 处理,这时,如果ViewGroup的mOnTouchListener被设置,则onTouch会被调用,否则onTouchEvent会被调用,也就是说,如果都提供的话,onTouch会屏蔽掉onTouchEvent。在onTouchEvent中,如果设置了mOnClickListener,则onClick会被调用。如果顶级ViewGroup不拦截事件,则事件会传递给它所在的点击事件链上的子View,这时的子View 的dispatchTouchEvent会被调用,就这样一层一层分发。但是这里有个问题就是说,如果一个事件分发到了子View,但是子View不消费咋办,这样就会再交给父View去处理。至于这些事件从哪里来的,请看这篇文章

http://blog.csdn.net/aigestudio/article/details/44746625

到这里,就结束了,这就是我所说的将Activity的创建、布局的显示、事件的分发串起来。如果大家有觉得不妥的地方,请给出建议。欢迎吐槽~





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值