Android-架构组件的最新进展真香!

Android-架构组件的最新进展真香!,第1张

概述前言之前老是看着搞Java的朋友炫耀他的核心知识点笔记,真的,我内心毫无波澜,只有一点点酸其实Android开发也有很多知识点,我一直以来就想要一份Android核心知识点笔记来帮助自己查漏补缺,后来想想,近两年大家一直都在说互联网寒冬怎么怎么,还老是谣传Android开发马上要凉了,我想我 前言

之前老是看着搞Java的朋友炫耀他的核心知识点笔记,真的,我内心毫无波澜,只有一点点酸

其实AndroID开发也有很多知识点,我一直以来就想要一份AndroID核心知识点笔记来帮助自己查漏补缺,后来想想,近两年大家一直都在说互联网寒冬怎么怎么,还老是谣传AndroID开发马上要凉了,我想我等不到别人发给我核心笔记了,这种大环境下还可能会有人做吗……

我不能等着别人把资料送上门来

于是乎,今年年初开始,我就开始利用身边的资源(朋友)在下班时间请了一位阿里P8大牛吃了顿饭(我不会告诉你,当时我的内心是多么激动)过了几个月终于拿到了这份来之不易的AndroID开发核心知识点笔记……

这份资料我花了1个月看完了,不得不感叹一句,大牛就是大牛。里面的知识非常系统全面,这份资料一方面可以帮助我们巩固一下知识,一方面还可以有助于知识体系的打造与完善,同时也可以拿出来跟大家一起交流探讨,最后希望能互相学习,共同进步!

前言

分享一道之前面试头条的面试题:startService 和 bindService 有什么不同?为什么 bindService 能和 Activity 的生命周期联动?

前一个问题可以很快回答出来:生命周期不同,结束方式不同,交互方式不同。

后一个问题也能很快想到应该是 Activity 在销毁的时候顺带把 Service 销毁了。那么为什么 startService 不行呢?具体是怎么实现的呢?如果不对源码研究一番,似乎无法给出让人信服的回答,于是就有了这篇文章。

启动和绑定的区别

无论是启动 Activity,还是 Service,基本的流程都是 Context -> ActivtityManagerService -> 某些中间类(Activity 是 ActivityStarter、ActivityStack 等,Service 是 ActiveServices) -> ActivityThread。具体的代码流程比较长,而且很多和本文要探讨的主题无关,因此这里不会详细分析启动或绑定的流程,而只会保留与本文相关的部分源码。

startService

从 ContextImpl 的 startService 方法开始说起:

class ContextImpl extends Context {    @OverrIDe    public Componentname startService(Intent service) {        warnIfCallingFromSystemProcess();        return startServiceCommon(service, false, mUser);    }    private Componentname startServiceCommon(Intent service, boolean requireForeground,            UserHandle user) {        try {            // 检查 Intent            valIDateServiceIntent(service);            service.preparetoLeaveProcess(this);            // 启动 Service            Componentname cn = ActivityManager.getService().startService(                mMainThread.getApplicationThread(), service, service.resolveTypeIfNeeded(                            getContentResolver()), requireForeground,                            getopPackagename(), user.getIDentifIEr());            // 检查结果            if (cn != null) {                ...            }            return cn;        } catch (remoteexception e) {            throw e.rethrowFromSystemServer();        }    }}

ActivityManager.getService() 返回的就是 AMS 本身,而 AMS 只起到一个中转的作用,除了一些参数判断之外,AMS 直接调用了 ActiveServices 的 startServiceLocked:

public final class ActiveServices {    final ActivityManagerService mAm;    Componentname startServiceLocked(IApplicationThread caller, Intent service, String resolvedType,            int callingPID, int callingUID, boolean fgrequired, String callingPackage, final int userID)            throws TransactionToolargeException {        // 和 Activity 同样有一个 Record 记录对应的组件        ServiceRecord r = res.record;        ... // 主要是检查,发现错误则抛出异常,或返回 null 等结果        // 设置 ServiceRecord 的数据域        r.lastActivity = SystemClock.uptimeMillis();        r.startRequested = true;        r.delayedStop = false;        r.fgrequired = fgrequired;        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartID(),                service, neededGrants, callingUID));        ... // 主要是检查,发现错误则抛出异常,或返回 null 等结果        // 启动        Componentname cmp = startServiceInnerLocked(smap, service, r, callerFg, addToStarting);        return cmp;    }}

好了,startService 的流程暂时分析到这里,后面也没什么特别的,最后还是会由 ActivityThread 来创建 Service 对象,回调相关的生命周期方法等。

bindService

下面看 bindService 的实现:

class ContextImpl extends Context {    final @NonNull LoadedApk mPackageInfo;    private final @Nullable IBinder mActivityToken;    @OverrIDe    public boolean bindService(Intent service, ServiceConnection conn,            int flags) {        warnIfCallingFromSystemProcess();        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),                Process.myUserHandle());    }    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler            handler, UserHandle user) {        ...        IServiceConnection sd;        if (mPackageInfo != null) {            // 注意这里            sd = mPackageInfo.getServicedispatcher(conn, getouterContext(), handler, flags);        } else {            throw new RuntimeException("Not supported in system context");        }        try {            // 这个 token 是 Activity 启动时创建的,对应于 Activity 的 mToken 成员            IBinder token = getActivityToken();            ...            // 执行绑定流程            int res = ActivityManager.getService().bindService(                mMainThread.getApplicationThread(), getActivityToken(), service,                service.resolveTypeIfNeeded(getContentResolver()),                sd, flags, getopPackagename(), user.getIDentifIEr());            ...        } catch (remoteexception e) {            ...        }    }    @OverrIDe    public IBinder getActivityToken() {        return mActivityToken;    }}

可以看到,相比 startService,bindService 还在 ContextImpl 执行的时候就已经显示出了它的不同,除了会获取 Activity 的 token 之外,还有一个很关键的调用是 LoadedApk 的 getServicedispatcher 方法:

public final class LoadedApk {    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.Servicedispatcher>> mServices        = new ArrayMap<>();    public final IServiceConnection getServicedispatcher(ServiceConnection c,            Context context, Handler handler, int flags) {        synchronized (mServices) {            LoadedApk.Servicedispatcher sd = null;            ArrayMap<ServiceConnection, LoadedApk.Servicedispatcher> map = mServices.get(context);            if (map != null) {                sd = map.get(c);            }            if (sd == null) {                sd = new Servicedispatcher(c, context, handler, flags);                if (map == null) {                    map = new ArrayMap<>();                    mServices.put(context, map); // 记录                }                map.put(c, sd); // 记录 ServiceConnection            } else {                sd.valIDate(context, handler);            }            return sd.getIServiceConnection();        }    }}

Servicedispatcher 可以忽略,主要关注 mServices 这个成员,它记录了即将绑定到 Activity 的 ServiceConnection。

接着看后面的绑定流程,AMS 同样跳过,直接看 ActiveServices 的实现:

public final class ActiveServices {    final ActivityManagerService mAm;    final ArrayMap<IBinder, ArrayList<ConnectionRecord>> mServiceConnections = new ArrayMap<>();    int bindServiceLocked(IApplicationThread caller, IBinder token, Intent service,            String resolvedType, final IServiceConnection connection, int flags,            String callingPackage, final int userID) throws TransactionToolargeException {        // 获取应用进程的信息        final ProcessRecord callerApp = mAm.getRecordForAppLocked(caller);        if (callerApp == null) {            throw new SecurityException(                    "Unable to find app for caller " + caller                    + " (pID=" + Binder.getCallingPID()                    + ") when binding service " + service);        }        // 获取绑定的 Activity 信息        ActivityRecord activity = null;        if (token != null) {            activity = ActivityRecord.isInStackLocked(token);            if (activity == null) {                Slog.w(TAG, "Binding with unkNown activity: " + token);                return 0;            }        }        // 获取 ServiceRecord        ServiceRecord s = res.record;        boolean permissionsRevIEwrequired = false;        // 启动 Activity,成功启动后再启动 Service        if (mAm.mPermissionRevIEwrequired) {            if (mAm.getPackageManagerInternalLocked().isPermissionsRevIEwrequired(                    s.packagename, s.userID)) {                RemoteCallback callback = new RemoteCallback(                        new RemoteCallback.OnResultListener() {                    @OverrIDe                    public voID onResult(Bundle result) {                        synchronized(mAm) {                            final long IDentity = Binder.clearCallingIDentity();                            try {                                ...                                if (...) {                                    try {                                        // 启动 Service                                        bringUpServiceLocked(...);                                    } catch (remoteexception e) {                                        /* ignore - local call */                                    }                                } else {                                    ...                                }                            } finally {                                ...                            }                        }                    }                });                final Intent intent = new Intent(Intent.ACTION_REVIEW_PERMISSIONS);                // 注意 callback                intent.putExtra(Intent.EXTRA_REMOTE_CALLBACK, callback);                // 启动 Activity,成功启动后回调 callback                 mAm.mHandler.post(new Runnable() {                    @OverrIDe                    public voID run() {                        mAm.mContext.startActivityAsUser(intent, new UserHandle(userID));                    }                });            }        }        final long origID = Binder.clearCallingIDentity();        try {            ...            // 注意参数 activity(ActivityRecord)            ConnectionRecord c = new ConnectionRecord(b, activity,                    connection, flags, clIEntLabel, clIEntIntent);            // connection 类型为 IServiceConnection            IBinder binder = connection.asBinder();            // 让 ActivityRecord 记录 connections 信息            if (activity != null) {                if (activity.connections == null) {                    activity.connections = new HashSet<ConnectionRecord>();                }                activity.connections.add(c);            }            // 让 ServiceRecord 记录 connections 信息            ArrayList<ConnectionRecord> cList = s.connections.get(binder);            if (cList == null) {                cList = new ArrayList<ConnectionRecord>();                s.connections.put(binder, cList);            }            cList.add(c);            // 让自身的成员变量 mServiceConnections 记录 connections 信息            cList = mServiceConnections.get(binder);            if (cList == null) {                cList = new ArrayList<ConnectionRecord>();                mServiceConnections.put(binder, cList);            }            cList.add(c);            ...            if ((flags&Context.BIND_auto_CREATE) != 0) { // 如果设置了绑定后自动启动                s.lastActivity = SystemClock.uptimeMillis();                // 启动 Service                if (bringUpServiceLocked(s, service.getFlags(), callerFg, false,                        permissionsRevIEwrequired) != null) {                    return 0;                }            }            if (s.app != null && b.intent.received) { // Service 已经在运行中,直接回调 onServiceConnected 即可                // Service is already running, so we can immediately                // publish the connection.                try {                    // 回调 onServiceConnected                    c.conn.connected(s.name, b.intent.binder, false);                } catch (Exception e) {                    ...                }                ...            } else if (!b.intent.requested) {                 // 回调 onBind,内部调用了 scheduleBindService                requestServiceBindingLocked(s, b.intent, callerFg, false);            }        } finally {            Binder.restoreCallingIDentity(origID);        }        return 1;    }}

可以看到,相比 startService,bindService 在启动 Service 之前做了一些额外的工作:

通知 LoadedApk 记录 ServiceConnection根据 ActivityToken 获取 ActivityRecord添加 ConnectionRecord 到 ActivityRecord 及 ServiceRecord 上

上面就是 startService 和 bindService 在源码实现上的主要区别了,下面开始分析 Activity 的 finish 方法,看看 Service 是怎么随着 Activity 的销毁而销毁的。

Service 是怎么随着 Activity 的销毁而销毁的?

和启动流程类似,finish 的执行流程为 Activity -> ActivityManager -> ActivitiyStack -> ActivityThread,因为代码量有些大,而且前三步和本文关系不大,因此这里直接看 ActivityThread 的实现即可:

public final class ActivityThread {    final ArrayMap<IBinder, ActivityClIEntRecord> mActivitIEs = new ArrayMap<>();    private voID handleDestroyActivity(IBinder token, boolean finishing,            int configChanges, boolean getNonConfigInstance) {        // 回调生命周期方法        ActivityClIEntRecord r = performDestroyActivity(token, finishing,                configChanges, getNonConfigInstance);        if (r != null) {            // 清理 window 资源            cleanUpPendingRemovewindows(r, finishing);            // 删除 DecorVIEw            WindowManager wm = r.activity.getwindowManager();            VIEw v = r.activity.mDecor;            if (v != null) {                IBinder wtoken = v.getwindowToken();                if (r.activity.mWindowAdded) {                    if (r.mPreserveWindow) {                        r.window.clearContentVIEw();                    } else {                        wm.removeVIEwImmediate(v);                    }                }                // 清除记录,这个记录可以参考 VIEwRootImpl 的 setVIEw 方法                if (wtoken != null && r.mPendingRemoveWindow == null) {                    WindowManagerGlobal.getInstance().closeAll(wtoken,                            r.activity.getClass().getname(), "Activity");                } else if (r.mPendingRemoveWindow != null) {                    WindowManagerGlobal.getInstance().closeAllExceptVIEw(token, v,                            r.activity.getClass().getname(), "Activity");                }                r.activity.mDecor = null;            }            if (r.mPendingRemoveWindow == null) {                WindowManagerGlobal.getInstance().closeAll(token,                        r.activity.getClass().getname(), "Activity");            }            // 使用 Base Context 执行最后的清理步骤            Context c = r.activity.getBaseContext();            if (c instanceof androID.app.ContextImpl) {                ((ContextImpl) c).scheduleFinalCleanup(                        r.activity.getClass().getname(), "Activity");            }        }        // 通知 AMS        if (finishing) {            try {                ActivityManager.getService().activityDestroyed(token);            } catch (remoteexception ex) {                throw ex.rethrowFromSystemServer();            }        }        mSomeActivitIEsChanged = true;    }}

可以看到,在 ActivityThread 中,Activity 的销毁流程共有 4 步:

回调 onPause、onStop、onDestroy 等生命周期方法关闭 Window、移除 DecorVIEw、清理 WindowManager 的记录调用 ContextImpl 执行最后的清理步骤通知 AMS Activity 已被销毁

Service 的解绑逻辑就隐藏在 ContextImpl 里面,下面看它的实现:

class ContextImpl extends Context {    final @NonNull ActivityThread mMainThread;    final @NonNull LoadedApk mPackageInfo;    // 回调 ActivityThread    final voID scheduleFinalCleanup(String who, String what) {        mMainThread.scheduleContextCleanup(this, who, what);    }    // ActivityThread 最终又回调了该方法    final voID performFinalCleanup(String who, String what) {        //Log.i(TAG, "Cleanup up context: " + this);        mPackageInfo.removeContextRegistrations(getouterContext(), who, what);    }}

可以看到,ContextImpl 只是起到了一个中转的作用,最终是交给 LoadedApk 执行的。从方法 removeContextRegistrations 的名字可以推测出,它的作用是清理注册到 Context 上的资源:

public final class LoadedApk {    private final ArrayMap<Context, ArrayMap<broadcastReceiver, Receiverdispatcher>> mReceivers        = new ArrayMap<>();    private final ArrayMap<Context, ArrayMap<broadcastReceiver, LoadedApk.Receiverdispatcher>> mUnregisteredReceivers        = new ArrayMap<>();    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.Servicedispatcher>> mServices        = new ArrayMap<>();    private final ArrayMap<Context, ArrayMap<ServiceConnection, LoadedApk.Servicedispatcher>> mUnboundServices        = new ArrayMap<>();    public voID removeContextRegistrations(Context context,            String who, String what) {        final boolean reportRegistrationLeaks = StrictMode.vmRegistrationLeaksEnabled();        synchronized (mReceivers) {            // 获取注册到 Context 上的 broadcastReceiver            ArrayMap<broadcastReceiver, LoadedApk.Receiverdispatcher> rmap =                    mReceivers.remove(context);            if (rmap != null) {                // 遍历,逐个注销                for (int i = 0; i < rmap.size(); i++) {                    LoadedApk.Receiverdispatcher rd = rmap.valueAt(i);                    // 打印异常信息                    IntentReceiverLeaked leak = new IntentReceiverLeaked(                            what + " " + who + " has leaked IntentReceiver "                            + rd.getIntentReceiver() + " that was " +                            "originally registered here. Are you missing a " +                            "call to unregisterReceiver()?");                    leak.setStackTrace(rd.getLocation().getStackTrace());                    Slog.e(androID.app.ActivityThread.TAG, leak.getMessage(), leak);                    if (reportRegistrationLeaks) {                        StrictMode.onIntentReceiverLeaked(leak);                    }                    // 通知 AMS 注销 broadcastReceiver                    try {                        ActivityManager.getService().unregisterReceiver(                                rd.getIIntentReceiver());                    } catch (remoteexception e) {                        throw e.rethrowFromSystemServer();                    }                }            }            mUnregisteredReceivers.remove(context);        }        synchronized (mServices) {            // 获取绑定到 Context 上的 ServiceConnection            ArrayMap<ServiceConnection, LoadedApk.Servicedispatcher> smap =                    mServices.remove(context);            if (smap != null) {                // 遍历,逐个解绑                for (int i = 0; i < smap.size(); i++) {                    LoadedApk.Servicedispatcher sd = smap.valueAt(i);                    // 打印异常信息                    ServiceConnectionLeaked leak = new ServiceConnectionLeaked(                            what + " " + who + " has leaked ServiceConnection "                            + sd.getServiceConnection() + " that was originally bound here");                    leak.setStackTrace(sd.getLocation().getStackTrace());                    Slog.e(androID.app.ActivityThread.TAG, leak.getMessage(), leak);                    if (reportRegistrationLeaks) {                        StrictMode.onServiceConnectionLeaked(leak);                    }                    // 通知 AMS 解绑 ServiceConnection                    try {                        ActivityManager.getService().unbindService(                                sd.getIServiceConnection());                    } catch (remoteexception e) {                        throw e.rethrowFromSystemServer();                    }                    sd.doForget();                }            }            mUnboundServices.remove(context);        }    }}

果然,removeContextRegistrations 的作用就是把注册/绑定到 Context 上的 broadcastReceiver、ServiceConnection 给注销/解绑,并抛出异常信息,告诉用户应该主动地注销/解绑。unbindService、unregisterReceiver 的流程忽略,无非还是从相关的列表中删除一些记录(比如 activity.connections),并通知 ActivityThread 执行最后的注销逻辑。

总结

分析完上面的代码后,现在可以自信地给出这道面试题的答案了:

bindService 方法执行时,LoadedApk 会记录 ServiceConnection 信息Activity 执行 finish 方法时,会通过 LoadedApk 检查 Activity 是否存在未注销/解绑的 broadcastReceiver 和 ServiceConnection,如果有,那么会通知 AMS 注销/解绑对应的 broadcastReceiver 和 Service,并打印异常信息,告诉用户应该主动执行注销/解绑的 *** 作
更新面试复习笔记:

这份资料我从春招开始,就会将各博客、论坛。网站上等优质的AndroID开发中高级面试题收集起来,然后全网寻找最优的解答方案。每一道面试题都是百分百的大厂面经真题+最优解答。包知识脉络 + 诸多细节。
节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
给文章留个小赞,就可以免费领取啦~

戳我领取:Android对线暴打面试指南、超硬核Android面试知识笔记、3000页Android开发者架构师核心知识笔记

《960页AndroID开发笔记》

《1307页AndroID开发面试宝典》

包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

《507页AndroID开发相关源码解析》

只要是程序员,不管是Java还是AndroID,如果不去阅读源码,只看api文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

最后送福利了,现在关注我并且加入群聊可以获取包含源码解析,自定义view,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,欢迎加群探讨,有Flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿
点击这里前往我的腾讯文档领取

联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。

[外链图片转存中…(img-cg1e5pta-1623417553059)]

《507页AndroID开发相关源码解析》

只要是程序员,不管是Java还是AndroID,如果不去阅读源码,只看api文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。

真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。

[外链图片转存中…(img-DGoAK9Ru-1623417553060)]

最后送福利了,现在关注我并且加入群聊可以获取包含源码解析,自定义view,动画实现,架构分享等。
内容难度适中,篇幅精炼,每天只需花上十几分钟阅读即可。
大家可以跟我一起探讨,欢迎加群探讨,有Flutter—底层开发—性能优化—移动架构—资深UI工程师 —NDK相关专业人员和视频教学资料,还有更多面试题等你来拿
点击这里前往我的腾讯文档领取
[外链图片转存中…(img-Q4T1HAoh-1623417553062)]

总结

以上是内存溢出为你收集整理的Android-架构组件的最新进展真香!全部内容,希望文章能够帮你解决Android-架构组件的最新进展真香!所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1086402.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-27
下一篇 2022-05-27

发表评论

登录后才能评论

评论列表(0条)

保存