【打卡考勤源码】【offsetof源码】【opencpn 源码】contextimpl源码

来源:牛股起爆点源码

1.Android N 四大组件的工作原理
2.OpenHarmony 代码学习4:Ability子系统 源码解析(更新太快,跟不上步伐了)
3.如何解决android 5.0中出现的警告service intent must be expl
4.Android UI线程

contextimpl源码

Android N 四大组件的工作原理

       æœ¬æ–‡ä¾§é‡è®²è§£android N 系统中四大组件的工作原理,不同系统原理略有差别。通过分析四大组件的工作流程加深对Android Framework的理解,也为插件化开发打下基础。

        Activity

        展示一个界面并和用户交互,它扮演的是一个前台界面的角色。

        Service

        计算型组件,用于后台执行一系列计算任务,工作在主线程,耗时操作需要另起线程, 分为启动状态和绑定状态。

        BroadcastReceiver

        消息型组件,主要用于不同组件或者不同应用之间的消息传递,它工作在系统内部,不适合执行耗时操作,操作超过5s,会出现ANR。

        ContentProvider

        数据共享型组件,用于向其他组件或者应用共享数据,主要执行CURD操作。

        我们启动一个activity有两种方法,

        第一种(Activity直接启动方式):

        Intent intent = new Intent(this, MainActivity.class);

        startActivity(intent);

        第二种(Context启动方式)

        Intent intent = new Intent(this, MainActivity.class);

        getApplicationContext().startActivity(intent);

        不同的启动方式Activity的工作流程有点差别。

        两种启动都会调用到Instrumentation类中的execStartActivity的方法,系统最终是通过ActivityThread中的performLaunchActivity完成Activity的创建和启动。

        performLaunchActivity方法主要完成以下工作:

        1、通过ActivityClientRecord对象获取启动activity的组件信息

        2、通过mInstrumentation对象的newActivity方法调用classloader完成activity的创建

        3、通过r.packageInfo(LoadedApk 对象)的makeApplication方法尝试创建Application对象

        4、创建ContextImpl对象并调用Activity的attach方法完成一些数据的初始化

        5、调用Activity的onCreate方法

        在Activity启动的过程中,App进程会频繁地与AMS进程进行通信:

        App进程会委托AMS进程完成Activity生命周期的管理以及任务栈的管理;这个通信过程AMS是Server端,App进程通过持有AMS的client代理IActivityManager完成通信过程;

        AMS进程完成生命周期管理以及任务栈管理后,会把控制权交给App进程,让App进程完成Activity类对象的创建,以及生命周期回调;这个通信过程也是通过Binder完成的,App所在server端的Binder对象存在于ActivityThread的内部类ApplicationThread;AMS所在client通过持有IApplicationThread的代理对象完成对于App进程的通信。

        Service有两种启动方式,startService()和bindService(),两种状态可以并存:

        startService流程

        bindService流程

        BroadcastReceiver的工作过程主要包括广播的注册、发送和接收:

        动态注册过程:

        发送过程

        静态注册是由PackageManagerService(PMS)在应用安装的时候完成整个注册过程的,除广播以外,其他三大组件也都是在应用安装时由PMS解析并注册的。

        每个进程的入口都是ActivityThead.main(),App的启动流程如下:

        从源码中可以看出:

        应用启动的入口为ActivityThread的main方法,main方法会创建ActivityThread实例并创建主线程消息队列。

        attach方法中远程调用AMS的attachApplication方法,并提供ApplicationThread用于和AMS的通信。

        attachApplication方法会通过bindApplication方法和H来调回ActivityThread的handleBindApplication,这个方法会先创建Application,再加载ContentProvider,然后才会回调Application的onCreate方法。

        由上图可以看出,在ContentProvider的启动过程中伴随着app进程的启动。

        ContentProvider的其他CURD操作如insert,delete,update跟query的流程类似。

OpenHarmony 代码学习4:Ability子系统 源码解析(更新太快,跟不上步伐了)

       深入探讨OpenHarmony代码学习中关于Ability子系统的源码解析,重点关注基于monthly_的打卡考勤源码代码架构与配置。

       在源码解析中,SystemAbility的配置sa_profile至关重要,它确保了以c++实现的SA在加载注册逻辑时能够完成SA的注册,反之,未配置profile的System Ability将不会完成注册。可见abilitymgr等系统服务SA以特定方式运行,如.xml所示,offsetof源码ams的libabilityms.z.so在foundation进程中启动,并在启动后即向samgr组件注册SystemAbility,实现本地跨IPC访问。

       进一步,分析AbilityManagerService作为SystemAbility的opencpn 源码管理器,提供管理Ability生命周期的管理能力。以AbilityManagerService::StartAbility为起点,此方法支持4种Startability,其中IRemoteObject属于分布式软总线子系统的ipc组件,负责进程间通信。monetdb源码理解IPC与RPC机制,IPC与RPC在实现跨进程通信中扮演重要角色,IPC使用Binder驱动,适合设备内跨进程通信,而RPC采用软总线驱动,typroxy源码适用于跨设备跨进程通信。客户端与服务器通过客户端-服务器模型进行通信,通过代理获取服务提供方的接口进行数据交互。三方应用通过FA提供的接口绑定服务提供方的Ability,获取代理,实现通信。

       在StartAbility中,callerToken由AbilityRuntime::AbilityContextImpl::StartAbility传入的AbilityContextImpl成员变量token_决定,通常指要启动的Ability。此调用链将在后续应用启动流程中总结,具体路径可参考官网介绍。

       继续深入代码分析,观察StartAbility中的调用链,最终向BMS调用StartAbilityInner方法。根据ability类型的不同,启动方式也不同,已在代码段中进行了标注。在OpenHarmony代码学习中,PageAbility作为具备ArkUI实现的Ability,是最具直观性的用户可见并可交互的实例,通常由missionListManager启动。

如何解决android 5.0中出现的警告service intent must be expl

       æœ‰äº›æ—¶å€™æˆ‘们使用Service的时需要采用隐私启动的方式,但是Android 5.0一出来后,其中有个特性就是Service Intent must be explitict,也就是说从Lollipop开始,service服务必须采用显示方式启动。

       è€Œandroid源码是这样写的(源码位置:sdk/sources/android-/android/app/ContextImpl.java):

       private void validateServiceIntent(Intent service) {

        if (service.getComponent() == null && service.getPackage() == null) {

        if (getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {

        IllegalArgumentException ex = new IllegalArgumentException(

        "Service Intent must be explicit: " + service);

        throw ex;

        } else {

        Log.w(TAG, "Implicit intents with startService are not safe: " + service

        + " " + Debug.getCallers(2, 3));

        }

        }

        }

       å¤åˆ¶ä»£ç 

       æ—¢ç„¶ï¼Œæºç é‡Œæ˜¯è¿™æ ·å†™çš„,那么这里有两种解决方法:

       1、设置Action和packageName:

       å‚考代码如下:

       Intent mIntent = new Intent();

       mIntent.setAction("XXX.XXX.XXX");//你定义的service的action

       mIntent.setPackage(getPackageName());//这里你需要设置你应用的包名

       context.startService(mIntent);

       å¤åˆ¶ä»£ç 

       æ­¤æ–¹å¼æ˜¯google官方推荐使用的解决方法。

       2、将隐式启动转换为显示启动:

       public static Intent getExplicitIntent(Context context, Intent implicitIntent) {

        // Retrieve all services that can match the given intent

        PackageManager pm = context.getPackageManager();

        List<ResolveInfo> resolveInfo = pm.queryIntentServices(implicitIntent, 0);

        // Make sure only one match was found

        if (resolveInfo == null || resolveInfo.size() != 1) {

        return null;

        }

        // Get component info and create ComponentName

        ResolveInfo serviceInfo = resolveInfo.get(0);

        String packageName = serviceInfo.serviceInfo.packageName;

        String className = serviceInfo.serviceInfo.name;

        ComponentName component = new ComponentName(packageName, className);

        // Create a new intent. Use the old one for extras and such reuse

        Intent explicitIntent = new Intent(implicitIntent);

        // Set the component to be explicit

        explicitIntent.setComponent(component);

        return explicitIntent;

        }

       å¤åˆ¶ä»£ç 

       è°ƒç”¨æ–¹å¼å¦‚下:

       Intent mIntent = new Intent();

       mIntent.setAction("XXX.XXX.XXX");

       Intent eintent = new Intent(getExplicitIntent(mContext,mIntent));

       context.startService(eintent);

Android UI线程

        思考:

        先必须了解下面2个问题

        1.顾名思义 UI线程 就是刷新UI 所在线程

        2.UI是单线程刷新

        1.对Activity 来说 UI线程就是其主线程

        2.对View来说 UI线程就是创建ViewRootImpl所在的线程

        可以通过 WindowManager 内部会创建ViewRootImpl对象

        好了,进入主题。我们来慢慢揭开面纱。

        我们可以分别从几个方面切入

        我们可能都有使用过 runOnUiThread 现在来看看的源码实现。

        可以从上面的源码 看到

        不是UI线程 就用Handler切到Handler所在的线程中,如果是UI线程直接就调用run方法。

        Activity的创建:

        1.Activity创建:mInstrumentation.newActivity

        2.创建Context :ContextImpl appContextcreateBaseContextForActivity(r)

        我们经常用这个方法干的事情就是,要么在onCreate中获取View宽高的值。要么就是在子线程中做一些耗时操作 ,然后post切到对应View所在的线程 来绘制UI操作。那么这个对应的线程就是UI线程了。

        那么这个UI线程就一定是主线程吗?

        接来继续来看。它的源码View:post

        mAttachInfo 在dispatchAttachedToWindow 中被赋值 ,也就是在ViewRootImpl创建的时候,所以是创建ViewRootImpl所在的线程。

        attachInfo 上面时候为null 呢?在ViewRootImpl 还没来得及创建的时候,ViewRootImpl 创建是在 “onResume" 之后。所以在 Activity 的 onCreate 去View.post 那么AttachInfo 是为null 。

        当 AttachInfo == null 那么会调用 getRunQueue().post(action) 。

        最终这个Runnable 被 缓存到 HandlerActionQueue 中。

        直到ViewRootImpl 的 performTraversals 中 调用dispatchAttachedToWindow(mAttachInfo, 0);, 那么才会去处理 RunQueue() 中的Runnable。

        来张图 便于理解这个流程

        我们有时候去子线程操作UI的时候(如:requestLayout),会很经常见到下面的 报错日志:

Only the original thread that created a view hierarchy can touch its views

        为什么会报这个错误呢?

        翻译一下:只有创建视图层次结构的原始线程才能接触到它的视图。

也就是操作UI的线程要和ViewRootImpl创建的线程是同一个线程才行,并不是只有主线程才能更新UI啊。

        ViewRootImpl创建的线程?那么 ViewRootImpl 在哪里被创建的呢?

        从上图可以看到ViewRootImpl创建最开始是从 ActivityThread 的HandleResumeActivity中开始 一直 ViewRootImpl 创建,也就是说ViewRootImpl 对应的UI线程和 ActivityThread 在同一个线程 也就是主线程。

        好了 通过上面的讲解,上面的问题相信你可以自己回答啦~

文章所属分类:探索频道,点击进入>>