精华内容
参与话题
问答
  • Android 系统架构

    千次阅读 2019-04-25 10:56:41
    Android 是一种基于 Linux 的开放源代码软件栈,为广泛的设备和机型而创建。下图所示为 Android 平台的主要组件。 Linux 内核 Android 平台的基础是 Linux 内核。例如,Android Runtime (ART) 依靠 Linux 内核来...

    可以查看 https://developer.android.com/guide/platform

    Android 是一种基于 Linux 的开放源代码软件栈,为广泛的设备和机型而创建。下图所示为 Android 平台的主要组件。

    image.png

    Linux 内核

    Android 平台的基础是 Linux 内核。例如,Android Runtime (ART) 依靠 Linux 内核来执行底层功能,例如线程和低层内存管理。
    使用 Linux 内核可让 Android 利用主要安全功能,并且允许设备制造商为著名的内核开发硬件驱动程序。


    硬件抽象层 (HAL)

    硬件抽象层 (HAL) 提供标准界面,向更高级别的 Java API 框架显示设备硬件功能。HAL 包含多个库模块,其中每个模块都为特定类型的硬件组件实现一个界面,例如相机或蓝牙模块。当框架 API 要求访问设备硬件时,Android 系统将为该硬件组件加载库模块。


    Android Runtime

    对于运行 Android 5.0(API 级别 21)或更高版本的设备,每个应用都在其自己的进程中运行,并且有其自己的 Android Runtime (ART) 实例。ART 编写为通过执行 DEX 文件在低内存设备上运行多个虚拟机,DEX 文件是一种专为 Android 设计的字节码格式,经过优化,使用的内存很少。编译工具链(例如 Jack)将 Java 源代码编译为 DEX 字节码,使其可在 Android 平台上运行。

    ART 的部分主要功能包括:

    预先 (AOT) 和即时 (JIT) 编译
    优化的垃圾回收 (GC)
    更好的调试支持,包括专用采样分析器、详细的诊断异常和崩溃报告,并且能够设置监视点以监控特定字段
    在 Android 版本 5.0(API 级别 21)之前,Dalvik 是 Android Runtime。如果您的应用在 ART 上运行效果很好,那么它应该也可在 Dalvik 上运行,但反过来不一定。

    Android 还包含一套核心运行时库,可提供 Java API 框架使用的 Java 编程语言大部分功能,包括一些 Java 8 语言功能。

    原生 C/C++ 库

    许多核心 Android 系统组件和服务(例如 ART 和 HAL)构建自原生代码,需要以 C 和 C++ 编写的原生库。Android 平台提供 Java 框架 API 以向应用显示其中部分原生库的功能。例如,您可以通过 Android 框架的 Java OpenGL API 访问 OpenGL ES,以支持在应用中绘制和操作 2D 和 3D 图形。

    如果开发的是需要 C 或 C++ 代码的应用,可以使用 Android NDK 直接从原生代码访问某些原生平台库。


    Java API 框架

    您可通过以 Java 语言编写的 API 使用 Android OS 的整个功能集。这些 API 形成创建 Android 应用所需的构建块,它们可简化核心模块化系统组件和服务的重复使用,包括以下组件和服务:

    丰富、可扩展的视图系统,可用以构建应用的 UI,包括列表、网格、文本框、按钮甚至可嵌入的网络浏览器
    资源管理器,用于访问非代码资源,例如本地化的字符串、图形和布局文件
    通知管理器,可让所有应用在状态栏中显示自定义提醒
    Activity 管理器,用于管理应用的生命周期,提供常见的导航返回栈
    内容提供程序,可让应用访问其他应用(例如“联系人”应用)中的数据或者共享其自己的数据
    开发者可以完全访问 Android 系统应用使用的框架 API。

    系统应用

    Android 随附一套用于电子邮件、短信、日历、互联网浏览和联系人等的核心应用。平台随附的应用与用户可以选择安装的应用一样,没有特殊状态。因此第三方应用可成为用户的默认网络浏览器、短信 Messenger 甚至默认键盘(有一些例外,例如系统的“设置”应用)。

    系统应用可用作用户的应用,以及提供开发者可从其自己的应用访问的主要功能。例如,如果您的应用要发短信,您无需自己构建该功能,可以改为调用已安装的短信应用向您指定的接收者发送消息。

    展开全文
  • Android架构组件-App架构指南

    万次阅读 多人点赞 2017-11-08 00:55:21
    与传统的桌面应用程序不同,Android应用程序的结构要复杂得多,在大多数情况下,它们只在桌面快捷启动方式中有一个入口,并且作为单个进程运行。一个典型的Android应用程序是由多个 app组件(Android四大组件) 构成的...

    Android架构组件-App架构指南

    Android架构组件-WorkManager

    Android架构组件- Room数据库的使用

    Android架构组件-Lifecycle

    Android架构组件-Paging库的使用


    版权声明:本文为博主原创文章,欢迎大家转载!

    但是转载请标明出处: http://blog.csdn.net/guiying712/article/details/78474177 ,本文出自:【张华洋的博客】


    注意:本文中的 app组件 指代 Android应用中的四大组件( activities, services, content providers and broadcast receivers

    本指南适用于那些已经拥有开发Android应用基础知识的开发人员,现在想了解能够开发出更加健壮、优质的应用程序架构。

    首先需要说明的是:Android Architecture Components 翻译为 Android架构组件 并不是我自己随意翻译的,而是Google官方博客中明确称其为 Android架构组件,因此我遵循了这种叫法。

    在这里我先贴上Google原文地址,以及Android架构组件官方开源示例代码地址。

    Google原文地址https://developer.android.google.cn/topic/libraries/architecture/guide.html

    Android架构组件官方开源示例代码地址(android-architecture-components)https://github.com/googlesamples/android-architecture-components

    下面这张图是Android架构组件完整的架构图,其中表示了的架构组件的所有模块以及它们之间如何交互:

    这里写图片描述

    APP开发者面临的常见问题


    与传统的桌面应用程序不同,Android应用程序的结构要复杂得多,在大多数情况下,它们只在桌面快捷启动方式中有一个入口,并且作为单个进程运行。一个典型的Android应用程序是由多个 APP组件(Android四大组件) 构成的,包括 activities, fragments, services, content providers and broadcast receivers

    这些 app组件 中的大部分都是在 应用清单(AndroidManifast.xml)中声明的,Android操作系统使用这些组件将应用程序集成到设备的用户界面中。虽然,应用程序通常上是以单个进程运行的,但是一个合理的Android应用需要更加灵活,因为用户可以通过不同的应用程序,在他们的设备上不断切换流程和任务。

    想象下在我们最喜爱的社交网络应用中分享照片时会发生什么情况。首先这个应用程序触发一个Camera(拍照或摄像) Intent,由Android操作系统启动一个Camera应用来处理请求。此时,用户虽然离开了这个社交网络应用,但他们的体验是无缝的。相机应用程序又可能触发其他 Intent,例如启动文件管理器,该文件管理器可以启动另一个应用程序,最终用户回到社交网络应用并分享照片。此外,用户在这个过程的任何时候都可能被电话打断,并在打完电话后回来继续分享照片。

    在Android中,这种应用程序跳转行为是很常见的,所以我们的应用程序必须正确处理这些流程。请记住,移动设备是资源受限的,所以在任何时候,操作系统都可能需要杀死一些应用程序,以腾出空间给新的应用。

    这一切的要点在于,我们的 app组件 可以单独和无序地启动,并且可以在任何时候由用户或系统销毁。由于 app组件 是短暂的,并且它们的生命周期(创建和销毁时)不在我们的控制之下,因此我们不应该在app组件中存储任何 app数据或状态,并且 app组件不应相互依赖。

    通用架构原则


    如果不使用 app组件存储app数据和状态,那该如何构造应用程序呢?

    我们需要关注的最重要的事情是:如何在你的应用中分离关注点。最常见的错误是将所有的代码写入一个 ActivityFragment任何不处理 UI 或 与操作系统交互的代码都不应该出现在这些类中,我们应该尽可能保持 ActivityFragment 精简,这样可以避免许多生命周期相关的问题。请记住,我们不拥有这些类,它们只是建立操作系统和我们的应用程序之间契约的胶水类。Android操作系统可能会随时根据用户交互或其他因素(如低内存)来销毁它们,最好尽可能地减少依赖他们,以提供可靠的用户体验。

    第二个重要原则是: 你应该从一个 Model 驱动你的UI,最好是一个持久化的 Model。之所以说持久化是理想的 Model,原因有两个:如果操作系统销毁你的应用程序以释放资源,那么你的用户就不会丢失数据,即使网络连接不稳定或连接不上,你的应用程序也会继续工作。Model 是负责处理应用程序数据的组件,它们独立于应用程序的 Views 和 app组件,因此 Model 与这些 app组件的生命周期问题是相隔离的。保持简洁的UI代码,以及不受约束的应用程序逻辑,可以使app的管理更加容易,基于具有明确定义的管理数据责任的模型类的应用程序,会更加具有可测试性,并使我们的应用程序状态保持前后一致。

    推荐的App架构


    在本节中,我们将演示如何通过使用用例来构造使用了 架构组件(Architecture Components) 的应用程序。

    注意:不可能有一种编写应用程序的方法对每个场景都是最好的。对于大多数用例来说,推荐的这个架构可能是一个好的起点。如果你已经有了编写Android应用的好方法,那就不要在更改了。

    假如我没正在搭建一个用来显示 用户概况的UI,该用户概况将使用 REST API 从我们自己的服务器端获取。

    搭建用户界面

    这个UI 将由 UserProfileFragment.javaFragment 相应的 user_profile_layout.xml 布局文件组成。

    为了驱动用户界面,我们的数据模型需要保存两个数据元素。

    • 用户ID:用户的标识符。最好使用 fragment 参数(setArguments方法) 将此信息传递到 fragment 中。如果Android系统销毁了你的进程,这些信息将被保留,便于应用在下次重新启动时可用。

    • 用户对象:保存用户数据的 POJO(简单的Java对象)

    我们将创建一个基于ViewModelUserProfileViewModel 类来保存这些信息。

    一个 ViewModel 提供了一个特定 UI 组件中的数据,如一个 fragment 或 activity, 并且负责与数据处理业务的通信,例如调用其他 app组件 来加载数据或转发用户信息的修改。ViewModel不知道View,并且不受配置更改的影响,例如由于屏幕旋转而重新创建 Activity。

    现在我们有3个文件。

    • user_profile.xml:定义屏幕上的 UI。

    • UserProfileViewModel.java:为 UI 准备数据的类。

    • UserProfileFragment.java:显示 ViewModel 中的数据并对用户交互作出响应的 UI 控制器。

    接下来我们将开始实现(为了简单起见,省略了布局文件):

    public class UserProfileViewModel extends ViewModel {
    
        private String userId;
        private User user;
    
        public void init(String userId) {
            this.userId = userId;
        }
    
        public User getUser() {
            return user;
        }
    }
    public class UserProfileFragment extends Fragment {
        private static final String UID_KEY = "uid";
        private UserProfileViewModel viewModel;
    
        @Override
        public void onActivityCreated(@Nullable Bundle savedInstanceState) {
            super.onActivityCreated(savedInstanceState);
            String userId = getArguments().getString(UID_KEY);
            viewModel = ViewModelProviders.of(this).get(UserProfileViewModel.class);
            viewModel.init(userId);
        }
    
        @Override
        public View onCreateView(LayoutInflater inflater,
                    @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
            return inflater.inflate(R.layout.user_profile, container, false);
        }
    }

    现在,我们如何将它们联系在一起呢?当给 ViewModelUser 字段设值后,我们需要一种方法来通知用户界面,这就是 LiveData 类的作用。

    LiveData 是一个可观察的数据持有者。它允许应用程序中的组件观察 LiveData 对象的更改,但不会在它们之间创建明确的和严格的依赖关系路径。 LiveData 还会关联 app组件(activities, fragments, services) 的生命周期状态,并做出合适的事情来防止内存泄漏。


    注意:如果你已经在使用类似 RxJavaAgera 的库 ,则可以继续使用它们而不是LiveData。但是,当你使用它们或其他方式时,请确保正确处理生命周期,以便在相关的LifecycleOwner 停止时暂停数据流,并在销毁 LifecycleOwner 时销毁数据流。你还可以添加 android.arch.lifecycle:reactivestreams 以将 LiveData 与其他的响应流库(例如RxJava2)一起使用。

    现在我们用 LiveData<User> 替换 UserProfileViewModel 中的 User 字段,以便在数据更新时通知 Fragment。最主要的是:LiveData 是生命周期感知的,并且在不在需要时,它将自动清理引用。

    public class UserProfileViewModel extends ViewModel {
        ...
        private User user;
        private LiveData<User> user;
    
        public LiveData<User> getUser() {
            return user;
        }
    }

    现在我们修改 UserProfileFragment 以便观察数据并更新 UI。

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        viewModel.getUser().observe(this, user -> {
          // update UI
        });
    }

    每次更新用户数据时, 都会调用 onChanged 回调,并刷新 UI。

    如果你熟悉其他 可观察回调的库,你可能已经意识到,我们没有重写 fragment 的 onStop() 方法来停止观察数据。这对于 LiveData 来说是没有必要的,因为它是生命周期感知的,这意味着它不会调用回调,除非 Fragment 处于 活动状态(已收到 onStart() 但未收到 onStop())。当 fragment 收到 onDestroy() 时,LiveData也将自动移除观察者 。

    对于配置变化(例如,用户旋转屏幕)我们也没有做任何特殊的处理。当配置改变时,ViewModel 会自动恢复,所以一旦新的 Fragment 生效,它将接收到相同的 ViewModel实例,并且 ViewModel 的回调将立即被当前数据调用,这就是 ViewModels 为什么不应该直接引用 Views 的原因,他们可以比 View的生命周期更持久。想了解更多信息的请查看 The lifecycle of a ViewModel

    获取数据


    现在我们已经将 ViewModel 关联到了 Fragment,但是 ViewModel 如何获取用户数据呢?在这个例子中,我们假设服务器端提供了一个 REST API。我们将使用 Retrofit 库来访问我们的服务器端,虽然你可以自由使用不同的库来达到同样的目的。

    下面是retrofit 的 Webservice ,负责与服务器端进行通信:

    public interface Webservice {
        /**
         * @GET declares an HTTP GET request
         * @Path("user") annotation on the userId parameter marks it as a
         * replacement for the {user} placeholder in the @GET path
         */
        @GET("/users/{user}")
        Call<User> getUser(@Path("user") String userId);
    }

    ViewModel 的一个简单实现是直接调用 Webservice 来获取数据并将其 赋值给 user 对象,虽然这样是可行的,但是我们的应用程序以后将很难维护。它赋予了 ViewModel 类太多的职责,违背了我们前面提到的关注点分离原则。此外,ViewModel 的作用域与一个 Activity 或一个 Fragment 生命周期相关联,当他们的生命周期完成时将丢失所有的数据,这是非常糟糕的用户体验。因此,我们将 ViewModel 的这个工作委托给了一个新的模块 Repository

    Repository 模块负责数据处理操作。他们为应用的其余部分提供了一个干净的API,他们知道从何处获取数据以及在更新数据时调用哪些API。你可以将它们视为不同数据源 (持久化模型, web服务, 缓存, etc.)之间的中介。

    UserRepository 类使用 WebService 来获取用户数据项,如下:

    public class UserRepository {
    
        private Webservice webservice;
        // ...
        public LiveData<User> getUser(int userId) {
            // This is not an optimal implementation, we'll fix it below
            final MutableLiveData<User> data = new MutableLiveData<>();
            webservice.getUser(userId).enqueue(new Callback<User>() {
                @Override
                public void onResponse(Call<User> call, Response<User> response) {
                    // error case is left out for brevity
                    data.setValue(response.body());
                }
            });
            return data;
        }
    }

    虽然 repository 模块看起来没有必要,但是它有一个重要的目的,它从应用程序的其余部分提取数据源。现在我们的 ViewModel 不知道数据是从 Webservice 获取到的,这意味着我们可以根据需要,将它(Webservice)替换为其他的实现。

    注意:为了简单起见,我们忽略了网络错误的情况。对于暴露错误和加载状态的另一个实现,请查看 附录:暴露网络状态

    管理组件之间的依赖关系:

    上面的 UserRepository 类需要一个 Webservice 的实例来工作,UserRepository 可以简单地创建Webservice ,但要做到这一点,它必须需要知道 Webservice 类的依赖关系来构造它,这会使代码显著和成倍的复杂化(例如,每个需要 Webservice实例的类 都需要知道如何用它的依赖来构造它)。另外,UserRepository 可能不是唯一需要 Webservice 的类。如果每个类创建一个新的 WebService,这将是非常沉重的资源。

    现在我们有两种模式可以用来解决这个问题:

    • 依赖注入:依赖注入允许类在不构造它们的情况下定义它们的依赖关系。在运行时,另一个类负责提供这些依赖关系。我们推荐 Google 的 Dagger 2 库,在Android应用中实现依赖注入。Dagger 2 通过遍历依赖关系树来自动构造对象,并为依赖关系提供编译时间保证。
    • 服务定位器:服务定位器提供了一个注册表,这个类可以获得它们的依赖 而不是 构建它们。实现起来比依赖注入(DI)更容易,所以如果你不熟悉DI,可以使用 Service Locator。

    这些模式允许您扩展代码,因为它们提供了用于管理依赖关系的清晰模式,无需复制代码或增加复杂性。这两个模式也允许交换实现测试, 这是使用它们的主要好处之一。

    在这个例子中,我们将使用 依赖注入 来管理依赖关系。

    关联ViewModel和repository


    现在我们修改 UserProfileViewModel 使用的 repository。

    public class UserProfileViewModel extends ViewModel {
    
        private LiveData<User> user;
        private UserRepository userRepo;
    
        @Inject // UserRepository parameter is provided by Dagger 2
        public UserProfileViewModel(UserRepository userRepo) {
            this.userRepo = userRepo;
        }
    
        public void init(String userId) {
            if (this.user != null) {
                // ViewModel is created per Fragment so
                // we know the userId won't change
                return;
            }
            user = userRepo.getUser(userId);
        }
    
        public LiveData<User> getUser() {
            return this.user;
        }
    }

    缓存数据


    上面的 repository 实现 对抽象调用 Web服务是有好处的,但是因为它只依赖于一个数据源,所以它不是很有用。

    UserRepository 实现的问题是,在获取数据之后,它不保存在任何地方。如果用户离开 UserProfileFragment 并返回,应用程序将重新获取数据。这是不好的,原因有两个:浪费宝贵的网络带宽并强制用户等待新的查询完成。为了解决这个问题,我们将添加一个新的数据源到 UserRepository ,这个数据源可以将 User 对象 缓存 到内存中。

    @Singleton  // informs Dagger that this class should be constructed once
    public class UserRepository {
        private Webservice webservice;
        // simple in memory cache, details omitted for brevity
        private UserCache userCache;
        public LiveData<User> getUser(String userId) {
            LiveData<User> cached = userCache.get(userId);
            if (cached != null) {
                return cached;
            }
    
            final MutableLiveData<User> data = new MutableLiveData<>();
            userCache.put(userId, data);
            // this is still suboptimal but better than before.
            // a complete implementation must also handle the error cases.
            webservice.getUser(userId).enqueue(new Callback<User>() {
                @Override
                public void onResponse(Call<User> call, Response<User> response) {
                    data.setValue(response.body());
                }
            });
            return data;
        }
    }

    持久化数据


    在我们当前的实现中,如果用户旋转屏幕或离开并返回到应用,现有UI将立即可见,因为 repository 从内存中检索缓存的数据。但是,如果用户离开应用程序并且数小时后回来,或Android 系统杀死该进程后,会发生什么?

    在目前的实现中,我们将需要从网络上重新获取数据。这不仅是一个糟糕的用户体验,而且会浪费资源,因为它会使用移动数据重新获取相同的数据。你可以简单地通过缓存Web请求来解决这个问题,但是这会产生新的问题。如果相同的用户数据从另一种类型的请求中显示出来(例如,获取朋友列表),会发生什么情况?那么你的应用程序可能会显示不一致的数据,这是一个混乱的用户体验。例如,由于好友列表请求和用户请求可以在不同的时间执行,所以相同用户的数据可能会以不同的方式显示。您的应用需要合并它们以避免显示不一致的数据。

    处理这个问题的正确方法是使用 持久化模型。这就是 Room 持久化库可以拯救的地方。

    Room 是一个对象映射库,使用最小的模板代码来提供本地数据持久化。在编译时,它会根据 Schema 验证每个查询,因此,有问题的SQL查询会导致编译时出错,而不是运行时失败。Room 抽象了处理原始SQL表和查询的一些底层实现细节。它还允许观察对数据库数据(包括集合和 join 查询)的更改,通过 LiveData对象 公开这些更改 。另外,它明确定义了解决常见问题的线程约束,例如在主线程上的访问存储。


    注意:如果你的应用程序已经使用另一个持久化解决方案(如SQLite对象关系映射(ORM)),则不需要使用 Room 替换现有的解决方案。但是,如果你正在编写新的应用程序或重构现有的应用程序,我们建议使用 Room 来保存应用程序的数据。这样,你可以利用库的抽象和查询 验证功能。

    要使用 Room,我们需要定义我们的本地 Schema。首先,使用 @Entity 注解 User 类 以将其标记为数据库中的表。

    @Entity
    class User {
      @PrimaryKey
      private int id;
      private String name;
      private String lastName;
      // getters and setters for fields
    }

    然后,为我们的 app 创建一个数据库类继承于 RoomDatabase

    @Database(entities = {User.class}, version = 1)
    public abstract class MyDatabase extends RoomDatabase {
    }

    注意 MyDatabase 是抽象的。Room 自动提供一个它的实现。有关详细信息,请查看 Android架构组件- Room数据库的使用

    现在我们需要一种将用户数据插入数据库的方法。为此,我们将创建一个数据访问对象(DAO: data access object)

    @Dao
    public interface UserDao {
        @Insert(onConflict = REPLACE)
        void save(User user);
        @Query("SELECT * FROM user WHERE id = :userId")
        LiveData<User> load(String userId);
    }

    然后,从我们的数据库类中引用 DAO (Data Access Object)

    @Database(entities = {User.class}, version = 1)
    public abstract class MyDatabase extends RoomDatabase {
        public abstract UserDao userDao();
    }

    请注意,该 load 方法返回一个 LiveData。Room 知道数据库何时被修改,当数据改变时它会自动通知所有活跃的的察者。因为它使用的是 LiveData,所以这将是有效的,因为只有至少有一个活动的观察者才会更新数据。

    注意:Room 根据 table 的修改来检查失效,这意味着它可能发送误报的通知。

    现在我们可以修改 UserRepository 来包含 Room 数据源。

    @Singleton
    public class UserRepository {
        private final Webservice webservice;
        private final UserDao userDao;
        private final Executor executor;
    
        @Inject
        public UserRepository(Webservice webservice, UserDao userDao, Executor executor) {
            this.webservice = webservice;
            this.userDao = userDao;
            this.executor = executor;
        }
    
        public LiveData<User> getUser(String userId) {
            refreshUser(userId);
            // return a LiveData directly from the database.
            return userDao.load(userId);
        }
    
        private void refreshUser(final String userId) {
            executor.execute(() -> {
                // running in a background thread
                // check if user was fetched recently
                boolean userExists = userDao.hasUser(FRESH_TIMEOUT);
                if (!userExists) {
                    // refresh the data
                    Response response = webservice.getUser(userId).execute();
                    // TODO check for error etc.
                    // Update the database.The LiveData will automatically refresh so
                    // we don't need to do anything else here besides updating the database
                    userDao.save(response.body());
                }
            });
        }
    }

    请注意,尽管我们改变了 来自于 UserRepository 的数据,我们并不需要改变我们 UserProfileViewModelUserProfileFragment。这是抽象提供的灵活性。这对于测试来说有好处的,因为你可以在测试你的UserProfileViewModel 的时候提供一个假的 UserRepository

    现在我们的代码是完整了。如果用户以后回到相同的用户界面,他们会立即看到用户信息,因为我们持久化了。同时,如果数据过期了,我们的仓库将在后台更新数据。当然,根据您的使用情况,如果数据太旧,您可能不希望显示持久化数据。

    在一些使用情况下,如 下拉刷新,UI 显示用户是否正在进行网络操作是非常重要的。将UI 操作与实际数据分开是一个很好的做法,因为它可能因各种原因而导致更新(例如,如果我们获取朋友列表,同一用户可能会再次触发 LiveData<User> 更新)。站在UI 的角度,事实上,当有一个请求执行的时候,另一个数据点,类似于任何其他的数据 (比如 User 对象)。

    这个用例有两种常见的解决方案:

    • 更改 getUser 为返回包含网络操作状态的 LiveData 。附录中提供了一个示例实现:公开网络状态部分。

    • 在 repository 类中提供另一个可以返回用户刷新状态的公共函数。如果只想响应显式的用户操作(如下拉刷新)来显示网络状态,则此选项更好。

    单一的真相来源:

    不同的 REST API 端点通常返回相同的数据。例如,如果我们的服务器端拥有另一个返回 朋友列表的端点,则同一个用户对象可能来自两个不同的API 端点,也许粒度不同。如果 UserRepositoryWebservice请求返回原本的响应,我们的UI可能会显示不一致的数据,因为在这些请求过程中数据可能已经在服务器端发生了改变。这就是为什么在 UserRepository 实现中,Web服务回调只是将数据保存到数据库中。然后,对数据库的更改将触发回调给 活跃状态的 LiveData 对象。

    在这个模型中,数据库充当了 单一的真相来源,应用程序的其他部分通过 Repository 访问它。无论你是否使用磁盘缓存,我们都建议将你的 Repository 指定为应用程序其余部分唯一的真相来源。

    测试


    我们已经提到分离的好处之一就是可测试性,让我们看看如何测试每个代码模块。

    • 用户界面和交互:你唯一需要花费时间的是 Android UI Instrumentation 。测试UI 代码的最好方法是创建一个 Espresso测试。您可以创建 Fragment 并为其提供一个模拟的ViewModel。由于该 Fragment 只与 ViewModel 联系,所以伪造它足以完全测试这个UI。

    • ViewModel:ViewModel 可以使用 JUnit 来测试 。你只需要模拟 UserRepository 来测试它。

    • UserRepository:你同样也可以使用 JUnit 来测试 UserRepository。你需要模拟 Webservice 和 DAO。你可以测试它是否做出了正确的Web服务调用,并将结果保存到数据库中,如果数据已缓存且最新,则不会发出任何不必要的请求。因为 Webservice 和 UserDao 都是接口,你可以模拟它们,或者为更复杂的测试用例创建伪造的实现…

    • UserDao:测试 DAO 类的推荐方法是使用 instrumentation 测试。由于这些 instrumentation 测试不需要任何用户界面,他们将会运行得很快。对于每个测试,您可以创建一个处于内存中的数据库,以确保测试没有任何副作用(如更改磁盘上的数据库文件)。
      Room 也允许指定数据库的实现,所以你可以通过提供 JUnit 来测试 SupportSQLiteOpenHelper 的实现。通常不建议使用这种方法,因为设备上运行的SQLite版本可能与主机上的SQLite版本不同。

    • Webservice:使测试独立于外界是很重要的,所以你的 Webservice 测试也应该避免对后端进行网络调用。有很多库可以帮助你,例如, MockWebServer 是一个强大的库,可以帮助你为测试创建一个伪造的本地服务器。

    • Testing Artifacts 架构组件提供了一个Maven artifact 来控制其后台线程。在android.arch.core:core-testing artifact 内部 ,有2个 JUnit 规则:

      • InstantTaskExecutorRule:此规则可用于强制架构组件立即在调用线程上执行任何后台操作。
      • CountingTaskExecutorRule:此规则可用于检测测试,以等待架构组件的后台操作或将其作为闲置资源连接到 Espresso。

    最终的架构


    下图显示了我们推荐的架构中的所有模块以及它们如何相互交互:

    这里写图片描述

    指导原则


    编程是一个创造性的领域,开发Android应用也不例外。解决问题的方法有很多种,可以在多个Activity 或 Fragment 之间传递数据,检索远程数据并将其保存在本地以进行离线模式,也可以使用其他常见应用程序遇到的情况。

    虽然以下建议不是强制性的,但是根据我们的经验,从长远来看遵循这些建议将使您的代码更加健壮,变得可测试和可维护。

    • 你在 AndroidManifest 中定义的入口点(activities, services, broadcast receivers, 等等)不是数据的来源。相反,他们只应该协调与该入口点相关的数据子集。由于每个 app组件的 存活相当短,这取决于用户与设备的交互以及当前运行时的状况,因此你不希望这些入口点中的任何一个成为数据源。

    • 在应用程序的各个模块之间建立明确的职责界限时要毫不留情。例如,不要将从网络中加载数据的代码分散到多个类或包中。同样,不要把不相关的职责 - 比如数据缓存和数据绑定 - 放到同一个类中。

    • 尽可能少地暴露每个模块。不要试图创建“只有那一个”的快捷方式,从一个模块公开其内部实现细节。你可能会在短期内获得一些时间,但随着代码库的不断发展,你将会花费更多时间付出技术代价。

    • 在定义模块之间的交互时,请考虑如何使每个模块独立地进行测试。例如,如果有一个定义良好的 API 从网络中获取数据,测试将数据保存在本地数据库中的模块会变得更容易。相反,如果将这两个模块的逻辑混合在一起,或者在整个代码库中分散网络请求代码,那么要测试就会更加困难。

    • 你的APP的核心是让它从其他APP中脱颖而出。不要花费时间重新造轮子,或者一次又一次地写出相同的样板代码。相反,将精力集中在可以让你的应用独特的东西上,让Android 架构 和其他推荐的库处理重复的样板代码。

    • 持久化尽可能多的相关和最新的数据,以便当设备处于离线模式时你的APP依然可用。虽然你可能喜欢恒定的高速连接,但你的用户可能并不会。

    • 你的 repository 应该指定一个数据源作为单一的事实来源。无论你的应用程序何时需要访问这些数据,都应始终从单一的事实源头发起。有关更多信息,请查看 单一的真相来源

    附录:暴露网络状态


    在上面推荐的App架构部分,我们故意省略了网络错误和加载状态,以保持示例的简单。在本节中,我们演示了如何使用 Resource 类来暴露网络状态以及封装数据及其状态。

    以下是一个实现的例子:

    //a generic class that describes a data with a status
    public class Resource<T> {
        @NonNull public final Status status;
        @Nullable public final T data;
        @Nullable public final String message;
        private Resource(@NonNull Status status, @Nullable T data, @Nullable String message) {
            this.status = status;
            this.data = data;
            this.message = message;
        }
    
        public static <T> Resource<T> success(@NonNull T data) {
            return new Resource<>(SUCCESS, data, null);
        }
    
        public static <T> Resource<T> error(String msg, @Nullable T data) {
            return new Resource<>(ERROR, data, msg);
        }
    
        public static <T> Resource<T> loading(@Nullable T data) {
            return new Resource<>(LOADING, data, null);
        }
    }

    因为从网络加载数据,而从磁盘显示数据是一个常见的用例,我们将创建一个辅助类 NetworkBoundResource ,它可以在多个地方重复使用。以下是 NetworkBoundResource 的决策树:

    这里写图片描述

    它首先通过对资源的数据库进行观察。当第一次从数据库加载条目时,**NetworkBoundResource**0 会检查结果是否足够好以便被分发,或者它应该从网络中获取。请注意,这两种情况可能同时发生,因为你可能希望在从网络获取数据时显示缓存的数据。

    如果网络调用成功完成,则将响应保存到数据库中并重新初始化流。如果网络请求失败,我们直接发送失败。

    注意:在将新数据保存到磁盘之后,我们会重新初始化数据库中的数据流,但通常我们不需要这样做,因为数据库将会发送更改。另一方面,依靠数据库来发送更改,将产生依赖副作用,因为如果数据没有变化,数据库可以避免发送变化,那么它可能会中断。我们也不希望发送从网络返回的结果,因为这将违背单一的真相来源(也许数据库中会有触发机制可以改变保存的值)。我们也不想在没有新数据的情况下发送 SUCCESS,因为它会向客户发送错误的信息。

    下面是 公开API 是为 NetworkBoundResource 的子类提供的 :

    // ResultType: Type for the Resource data
    // RequestType: Type for the API response
    public abstract class NetworkBoundResource<ResultType, RequestType> {
        // Called to save the result of the API response into the database
        @WorkerThread
        protected abstract void saveCallResult(@NonNull RequestType item);
    
        // Called with the data in the database to decide whether it should be
        // fetched from the network.
        @MainThread
        protected abstract boolean shouldFetch(@Nullable ResultType data);
    
        // Called to get the cached data from the database
        @NonNull @MainThread
        protected abstract LiveData<ResultType> loadFromDb();
    
        // Called to create the API call.
        @NonNull @MainThread
        protected abstract LiveData<ApiResponse<RequestType>> createCall();
    
        // Called when the fetch fails. The child class may want to reset components
        // like rate limiter.
        @MainThread
        protected void onFetchFailed() {
        }
    
        // returns a LiveData that represents the resource, implemented
        // in the base class.
        public final LiveData<Resource<ResultType>> getAsLiveData();
    }

    请注意,上面的类定义了两个类型参数(ResultTypeRequestType),因为从 API 返回的数据类型可能与本地使用的数据类型不匹配。

    另请注意,上面的 ApiResponse 代码用于网络请求。 ApiResponse是一个简单的Retrofit2.Call类包装,将其响应转换为 LiveData

    以下是该 NetworkBoundResource 类的其余部分:

    public abstract class NetworkBoundResource<ResultType, RequestType> {
        private final MediatorLiveData<Resource<ResultType>> result = new MediatorLiveData<>();
    
        @MainThread
        NetworkBoundResource() {
            result.setValue(Resource.loading(null));
            LiveData<ResultType> dbSource = loadFromDb();
            result.addSource(dbSource, data -> {
                result.removeSource(dbSource);
                if (shouldFetch(data)) {
                    fetchFromNetwork(dbSource);
                } else {
                    result.addSource(dbSource,
                            newData -> result.setValue(Resource.success(newData)));
                }
            });
        }
    
        private void fetchFromNetwork(final LiveData<ResultType> dbSource) {
            LiveData<ApiResponse<RequestType>> apiResponse = createCall();
            // we re-attach dbSource as a new source,
            // it will dispatch its latest value quickly
            result.addSource(dbSource,
                    newData -> result.setValue(Resource.loading(newData)));
            result.addSource(apiResponse, response -> {
                result.removeSource(apiResponse);
                result.removeSource(dbSource);
                //noinspection ConstantConditions
                if (response.isSuccessful()) {
                    saveResultAndReInit(response);
                } else {
                    onFetchFailed();
                    result.addSource(dbSource,
                            newData -> result.setValue(
                                    Resource.error(response.errorMessage, newData)));
                }
            });
        }
    
        @MainThread
        private void saveResultAndReInit(ApiResponse<RequestType> response) {
            new AsyncTask<Void, Void, Void>() {
    
                @Override
                protected Void doInBackground(Void... voids) {
                    saveCallResult(response.body);
                    return null;
                }
    
                @Override
                protected void onPostExecute(Void aVoid) {
                    // we specially request a new live data,
                    // otherwise we will get immediately last cached value,
                    // which may not be updated with latest results received from network.
                    result.addSource(loadFromDb(),
                            newData -> result.setValue(Resource.success(newData)));
                }
            }.execute();
        }
    
        public final LiveData<Resource<ResultType>> getAsLiveData() {
            return result;
        }
    }

    现在,我们可以使用 NetworkBoundResource 将在 repository 中绑定 User 实现 写入我们的磁盘和网络。

    class UserRepository {
        Webservice webservice;
        UserDao userDao;
    
        public LiveData<Resource<User>> loadUser(final String userId) {
            return new NetworkBoundResource<User,User>() {
                @Override
                protected void saveCallResult(@NonNull User item) {
                    userDao.insert(item);
                }
    
                @Override
                protected boolean shouldFetch(@Nullable User data) {
                    return rateLimiter.canFetch(userId) && (data == null || !isFresh(data));
                }
    
                @NonNull @Override
                protected LiveData<User> loadFromDb() {
                    return userDao.load(userId);
                }
    
                @NonNull @Override
                protected LiveData<ApiResponse<User>> createCall() {
                    return webservice.getUser(userId);
                }
            }.getAsLiveData();
        }
    }

    Android架构组件-App架构指南

    Android架构组件-WorkManager

    Android架构组件- Room数据库的使用

    Android架构组件-Lifecycle

    Android架构组件-Paging库的使用

    展开全文
  • Android 系统架构详解

    千次阅读 2019-04-12 14:01:25
    本文作为Android系统架构的开篇,起到提纲挈领的作用,从系统整体架构角度概要讲解Android系统的核心技术点,带领大家初探Android系统全貌以及内部运作机制。虽然Android系统非常庞大且错综复杂,需要具备全面的技术...

    引言

     

    本文作为Android系统架构的开篇,起到提纲挈领的作用,从系统整体架构角度概要讲解Android系统的核心技术点,带领大家初探Android系统全貌以及内部运作机制。虽然Android系统非常庞大且错综复杂,需要具备全面的技术栈,但整体架构设计清晰。Android底层内核空间以Linux Kernel作为基石,上层用户空间由Native系统库、虚拟机运行环境、框架层组成,通过系统调用(Syscall)连通系统的内核空间与用户空间。对于用户空间主要采用C++和Java代码编写,通过JNI技术打通用户空间的Java层和Native层(C++/C),从而连通整个系统。

    为了能让大家整体上大致了解Android系统涉及的知识层面,先来看一张Google官方提供的经典分层架构图,从下往上依次分为Linux内核、HAL、系统Native库和Android运行时环境、Java框架层以及应用层这5层架构,其中每一层都包含大量的子模块或子系统。

    上图采用静态分层方式的架构划分,众所周知,程序代码是死的,系统运转是活的,各模块代码运行在不同的进程(线程)中,相互之间进行着各种错终复杂的信息传递与交互流,从这个角度来说此图并没能体现Android整个系统的内部架构、运行机理,以及各个模块之间是如何衔接与配合工作的。为了更深入地掌握Android整个架构思想以及各个模块在Android系统所处的地位与价值,计划以Android系统启动过程为主线,以进程的视角来诠释Android M系统全貌,全方位的深度剖析各个模块功能,争取各个击破。这样才能犹如庖丁解牛,解决、分析问题则能游刃有余。

     

    Android架构

     

    Google提供的5层架构图很经典,但为了更进一步透视Android系统架构,本文更多的是以进程的视角,以分层的架构来诠释Android系统的全貌,阐述Android内部的环环相扣的内在联系。

    系统启动架构图

    图解:Android系统启动过程由上图从下往上的一个过程是由Boot Loader引导开机,然后依次进入 -> Kernel -> Native -> Framework -> App,接来下简要说说每个过程:

    关于Loader层:

    • Boot ROM: 当手机处于关机状态时,长按Power键开机,引导芯片开始从固化在 ROM里的预设代码开始执行,然后加载引导程序到 RAM;

    • Boot Loader:这是启动Android系统之前的引导程序,主要是检查RAM,初始化硬件参数等功能。

    2.1 Linux内核层

    Android平台的基础是Linux内核,比如ART虚拟机最终调用底层Linux内核来执行功能。Linux内核的安全机制为Android提供相应的保障,也允许设备制造商为内核开发硬件驱动程序。

    • 启动Kernel的swapper进程(pid=0):该进程又称为idle进程, 系统初始化过程Kernel由无到有开创的第一个进程, 用于初始化进程管理、内存管理,加载Display,Camera Driver,Binder Driver等相关工作;

    • 启动kthreadd进程(pid=2):是Linux系统的内核进程,会创建内核工作线程kworkder,软中断线程ksoftirqd,thermal等内核守护进程。 kthreadd进程是所有内核进程的鼻祖。

    2.2 硬件抽象层 (HAL)

    硬件抽象层 (HAL) 提供标准接口,HAL包含多个库模块,其中每个模块都为特定类型的硬件组件实现一组接口,比如WIFI/蓝牙模块,当框架API请求访问设备硬件时,Android系统将为该硬件加载相应的库模块。

    2.3 Android Runtime & 系统库

    每个应用都在其自己的进程中运行,都有自己的虚拟机实例。ART通过执行DEX文件可在设备运行多个虚拟机,DEX文件是一种专为Android设计的字节码格式文件,经过优化,使用内存很少。ART主要功能包括:预先(AOT)和即时(JIT)编译,优化的垃圾回收(GC),以及调试相关的支持。

    这里的Native系统库主要包括init孵化来的用户空间的守护进程、HAL层以及开机动画等。启动init进程(pid=1),是Linux系统的用户进程, init进程是所有用户进程的鼻祖。

    • init进程会孵化出ueventd、logd、healthd、installd、adbd、lmkd等用户守护进程;

    • init进程还启动 servicemanager(binder服务管家)、 bootanim(开机动画)等重要服务

    • init进程孵化出Zygote进程,Zygote进程是Android系统的第一个Java进程(即虚拟机进程), Zygote是所有Java进程的父进程,Zygote进程本身是由init进程孵化而来的。

    2.4 Framework层

    • Zygote进程,是由init进程通过解析init.rc文件后fork生成的,Zygote进程主要包含:

    • 加载ZygoteInit类,注册Zygote Socket服务端套接字

    • 加载虚拟机

    • 提前加载类preloadClasses

    • 提前加载资源preloadResouces

    • System Server进程,是由Zygote进程fork而来, SystemServer是Zygote孵化的第一个进程,System Server负责启动和管理整个Java framework,包含ActivityManager,WindowManager,PackageManager,PowerManager等服务。

    • Media Server进程,是由init进程fork而来,负责启动和管理整个C++ framework,包含AudioFlinger,Camera Service等服务。

    2.5 App层

    • Zygote进程孵化出的第一个App进程是Launcher,这是用户看到的桌面App;

    • Zygote进程还会创建Browser,Phone,Email等App进程,每个App至少运行在一个进程上。

    • 所有的App进程都是由Zygote进程fork生成的。

    2.6 Syscall && JNI

    • Native与Kernel之间有一层系统调用(SysCall)层,见Linux系统调用(Syscall)原理;

    • Java层与Native(C/C++)层之间的纽带JNI,见Android JNI原理分析。

     

    通信方式

     

    无论是Android系统,还是各种Linux衍生系统,各个组件、模块往往运行在各种不同的进程和线程内,这里就必然涉及进程/线程之间的通信。对于IPC(Inter-Process Communication, 进程间通信),Linux现有管道、消息队列、共享内存、套接字、信号量、信号这些IPC机制,Android额外还有Binder IPC机制,Android OS中的Zygote进程的IPC采用的是Socket机制,在上层system server、media server以及上层App之间更多的是采用Binder IPC方式来完成跨进程间的通信。对于Android上层架构中,很多时候是在同一个进程的线程之间需要相互通信,例如同一个进程的主线程与工作线程之间的通信,往往采用的Handler消息机制。

    想深入理解Android内核层架构,必须先深入理解Linux现有的IPC机制;对于Android上层架构,则最常用的通信方式是Binder、Socket、Handler,当然也有少量其他的IPC方式,比如杀进程Process.killProcess()采用的是signal方式。下面说说Binder、Socket、Handler:

    3.1 Binder

    Binder作为Android系统提供的一种IPC机制,无论从系统开发还是应用开发,都是Android系统中最重要的组成,也是最难理解的一块知识点,想了解为什么Android要采用Binder作为IPC机制? 可查看我在知乎上的回答(https://www.zhihu.com/question/39440766/answer/89210950)。深入了解Binder机制,最好的方法便是阅读源码,借用Linux鼻祖Linus Torvalds曾说过的一句话:Read The Fucking Source Code。下面简要说说Binder IPC原理。

    Binder IPC原理

    Binder通信采用c/s架构,从组件视角来说,包含Client、Server、ServiceManager以及binder驱动,其中ServiceManager用于管理系统中的各种服务。

    想进一步了解Binder,可查看Binder系列—开篇(http://gityuan.com/2015/10/31/binder-prepare/),Binder系列花费了13篇文章的篇幅,从源码角度出发来讲述Driver、Native、Framework、App四个层面的整个完整流程。根据有些读者反馈这个系列还是不好理解,这个binder涉及的层次跨度比较大,知识量比较广,建议大家先知道binder是用于进程间通信,有个大致概念就可以先去学习系统基本知识,等后面有一定功力再进一步深入研究Binder机制。

    Binder原理篇(具体文章地址可详见:http://gityuan.com/android/)

    序号 文章名 概述
    0 Binder系列—开篇 Binder概述
    1 Binder系列3—启动Service Manager ServiceManager守护进程 注册和查询服务
    2 Binder系列4—获取Service Manager 获取代理对象BpServiceManager
    3 Binder系列5—注册服务(addService) 注册Media服务
    4 Binder系列6—获取服务(getService) 获取Media代理,以及DeathRecipient
    5 Binder系列7—framework层分析 framework层服务注册和查询,Binder注册
    6 理解Binder线程池的管理 Binder的startThreadPool过程
    7 彻底理解Android Binder通信架构 startService为主线
    8 Binder系列10—总结 Binder的简单总结
    9 Binder IPC的权限控制 clearCallingIdentity/restoreCallingIdentity
    10 Binder死亡通知机制之linkToDeath Binder死亡通知机制

    Binder驱动篇:

    1 Binder Driver初探 驱动open/mmap/ioctl,以及binder结构体
    2 Binder Driver再探 Binder通信协议,内存机制

    Binder使用篇:

    1 如何使用Binder Native层、Framwrok层自定义Binder服务
    2 如何使用AIDL App层自定义Binder服务

    3.2 Socket

    Socket通信方式也是C/S架构,比Binder简单很多。在Android系统中采用Socket通信方式的主要有:

    • zygote:用于孵化进程,system_server创建进程是通过socket向zygote进程发起请求;

    • installd:用于安装App的守护进程,上层PackageManagerService很多实现最终都是交给它来完成;

    • lmkd:lowmemorykiller的守护进程,Java层的LowMemoryKiller最终都是由lmkd来完成;

    • adbd:这个也不用说,用于服务adb;

    • logcatd:这个不用说,用于服务logcat;

    • vold:即volume Daemon,是存储类的守护进程,用于负责如USB、Sdcard等存储设备的事件处理。

    等等还有很多,这里不一一列举,Socket方式更多的用于Android framework层与native层之间的通信。Socket通信方式相对于binder比较简单,这里省略。

    3.3 Handler

    Binder/Socket用于进程间通信,而Handler消息机制用于同进程的线程间通信,Handler消息机制是由一组MessageQueue、Message、Looper、Handler共同组成的,为了方便且称之为Handler消息机制。

    有人可能会疑惑,为何Binder/Socket用于进程间通信,能否用于线程间通信呢?答案是肯定,对于两个具有独立地址空间的进程通信都可以,当然也能用于共享内存空间的两个线程间通信,这就好比杀鸡用牛刀。接着可能还有人会疑惑,那handler消息机制能否用于进程间通信?答案是不能,Handler只能用于共享内存地址空间的两个线程间通信,即同进程的两个线程间通信。很多时候,Handler是工作线程向UI主线程发送消息,即App应用中只有主线程能更新UI,其他工作线程往往是完成相应工作后,通过Handler告知主线程需要做出相应地UI更新操作,Handler分发相应的消息给UI主线程去完成,如下图:

    由于工作线程与主线程共享地址空间,即Handler实例对象mHandler位于线程间共享的内存堆上,工作线程与主线程都能直接使用该对象,只需要注意多线程的同步问题。工作线程通过mHandler向其成员变量MessageQueue中添加新Message,主线程一直处于loop()方法内,当收到新的Message时按照一定规则分发给相应的handleMessage()方法来处理。所以说,Handler消息机制用于同进程的线程间通信,其核心是线程间共享内存空间,而不同进程拥有不同的地址空间,也就不能用handler来实现进程间通信。

    上图只是Handler消息机制的一种处理流程,是不是只能工作线程向UI主线程发消息呢,其实不然,可以是UI线程向工作线程发送消息,也可以是多个工作线程之间通过handler发送消息。更多关于Handler消息机制文章:

    • Android消息机制-Handler(framework篇)(http://gityuan.com/2015/12/26/handler-message-framework/)

    • Android消息机制-Handler(native篇)(http://gityuan.com/2015/12/27/handler-message-native/)

    • Android消息机制3-Handler(实战)(http://gityuan.com/2016/01/01/handler-message-usage/)

    要理解framework层源码,掌握这3种基本的进程/线程间通信方式是非常有必要,当然Linux还有不少其他的IPC机制,比如共享内存、信号、信号量,在源码中也有体现,如果想全面彻底地掌握Android系统,还是需要对每一种IPC机制都有所了解。

     

    核心提纲

     

    博主对于Android从系统底层一路到上层都有自己的理解和沉淀,通过前面对系统启动的介绍,相信大家对Android系统有了一个整体观。接下来需抓核心、理思路,争取各个击破。后续将持续更新和完善整个大纲,不限于进程、内存、IO、系统服务架构以及分析实战等文章。

    当然本站有一些文章没来得及进一步加工,有时间根据大家的反馈,不断修正和完善所有文章,争取给文章,再进一步精简非核心代码,增加可视化图表以及文字的结论性分析。基于Android 6.0的源码,专注于分享Android系统原理、架构分析的原创文章。

    建议阅读群体: 适合于正从事或者有兴趣研究Android系统的工程师或者技术爱好者,也适合Android App高级工程师;对于尚未入门或者刚入门的App工程师阅读可能会有点困难,建议先阅读更基础的资料,再来阅读本站博客。

    看到Android整个系统架构是如此庞大的, 该问如何学习Android系统, 以下是我自己的Android的学习和研究论,仅供参考如何自学Android(http://gityuan.com/2016/04/24/how-to-study-android/)。

    从整理上来列举一下Android系统的核心知识点概览:

    4.1 系统启动系列

    Android系统启动-概述:

    链接地址:http://gityuan.com/2016/02/01/android-booting/

    Android系统中极其重要进程:init, zygote, system_server, servicemanager 进程:

    序号 进程启动 概述
    1 init进程 Linux系统中用户空间的第一个进程, Init.main
    2 zygote进程 所有App进程的父进程, ZygoteInit.main
    3 system_server进程(上篇) 系统各大服务的载体, forkSystemServer过程
    4 system_server进程(下篇) 系统各大服务的载体, SystemServer.main
    5 servicemanager进程 binder服务的大管家, 守护进程循环运行在binder_loop
    6 App进程 通过Process.start启动App进程, ActivityThread.main

    再来看看守护进程(也就是进程名一般以d为后缀,比如logd,此处d是指daemon的简称), 下面介绍部分守护进程:

    • debuggerd

    • installd

    • lmkd

    • logd

    4.2 系统稳定性系列

    Android系统稳定性主要是异常崩溃(crash)和执行超时(timeout):

    链接地址:http://gityuan.com/2016/06/19/stability_summary/

    序号 文章名 概述
    1 理解Android ANR的触发原理 触发ANR的场景以及机理
    2 Input系统—ANR原理分析 input触发ANR的原理
    3 理解Android ANR的信息收集过程 AMS.appNotResponding过程分析,收集traces
    4 解读Java进程的Trace文件 kill -3 信息收集过程
    5 Native进程之Trace原理 debuggerd -b 信息收集过程
    6 WatchDog工作原理 WatchDog触发机制
    7 理解Java Crash处理流程 AMS.handleApplicationCrash过程分析
    8 理解Native Crash处理流程 debuggerd守护进程
    9 global reference限制策略 global reference

    4.3 Android进程系列

    进程/线程是操作系统的魂,各种服务、组件、子系统都是依附于具体的进程实体。深入理解进程机制对于掌握Android系统整体架构和运转机制是非常有必要的,是系统工程师的基本功,下面列举进程相关的文章:

    序号 文章名 概述
    1 理解Android进程创建流程 Process.start过程分析
    2 理解杀进程的实现原理 Process.killProcess过程分析
    3 Android四大组件与进程启动的关系 AMS.startProcessLocked过程分析组件与进程
    4 Android进程绝杀技--forceStop force-stop过程分析彻底移除组件与杀进程
    5 理解Android线程创建流程 3种不同线程的创建过程
    6 彻底理解Android Binder通信架构 以start-service为线,阐述进程间通信机理
    7 理解Binder线程池的管理 Zygote fork的进程都默认开启binder线程池
    8 Android进程生命周期与ADJ 进程adj, processState以及lmk
    9 Android LowMemoryKiller原理分析 lmk原理分析
    10 进程优先级 进程nice,thread priority以及scheduler
    11 Android进程调度之adj算法 updateOomAdjLocked过程
    12 Android进程整理 整理系统的所有进程/线程

    4.4 四大组件系列

    对于App来说,Android应用的四大组件Activity,Service,Broadcast Receiver, Content Provider最为核心,接下分别展开介绍:

    序号 文章名 类别
    1 startActivity启动过程分析 Activity
    2 简述Activity生命周期 Activity
    3 startService启动过程分析 Service
    4 bindService启动过程分析 Service
    5 以Binder视角来看Service启动 Service
    6 Android Broadcast广播机制分析 Broadcast
    7 理解ContentProvider原理 ContentProvider
    8 ContentProvider引用计数 ContentProvider
    9 Activity与Service生命周期 Activity&&Service
    10 简述Activity与Window关系 Activity&&Window
    11 四大组件之综述 AMS
    12 四大组件之ServiceRecord Service
    13 四大组件之BroadcastRecord Broadcast
    14 四大组件之ContentProviderRecord ContentProvider
    15 理解Android Context Context
    16 理解Application创建过程 Application
    17 unbindService流程分析 Service
    18 四大组件之ActivityRecord Activity
    19 AMS总结(一) AMS

    4.5 图形系统系列

    图形也是整个系统非常复杂且重要的一个系列,涉及WindowManager,SurfaceFlinger服务。

    序号 文章名 类别
    1 WindowManager启动篇(http://gityuan.com/2017/01/08/windowmanger/) Window
    2 WMS之启动窗口篇(http://gityuan.com/2017/01/15/wms_starting_window/) Window
    3 以Window视角来看startActivity(http://gityuan.com/2017/01/22/start-activity-wms/) Window
    4 Android图形系统概述(http://gityuan.com/2017/02/05/graphic_arch/) SurfaceFlinger
    5 SurfaceFlinger启动篇(http://gityuan.com/2017/02/11/surface_flinger/) SurfaceFlinger
    6 SurfaceFlinger绘图篇(http://gityuan.com/2017/02/18/surface_flinger_2/) SurfaceFlinger
    7 Choreographer原理(http://gityuan.com/2017/02/25/choreographer/) Choreographer

    4.6 系统服务篇

    再则就是在整个架构中有大量的服务,都是基于Binder来交互的,Android系统服务的注册过程也是在此之上的构建的。计划针对部分核心服务来重点分析:

    • AMS服务

      AMS启动过程(一)(http://gityuan.com/2016/02/21/activity-manager-service/)

      更多组件篇[见小节4.3]

    • Input系统

      Input系统—启动篇(http://gityuan.com/2016/12/10/input-manager/)

      Input系统—InputReader线程(http://gityuan.com/2016/12/11/input-reader/)

      Input系统—InputDispatcher线程(http://gityuan.com/2016/12/17/input-dispatcher/)

      Input系统—UI线程(http://gityuan.com/2016/12/24/input-ui/)

      Input系统—进程交互(http://gityuan.com/2016/12/31/input-ipc/)

      Input系统—ANR原理分析(http://gityuan.com/2017/01/01/input-anr/)

    • PKMS服务

      PackageManager启动篇(http://gityuan.com/2016/11/06/packagemanagerservice)

      Installd守护进程(http://gityuan.com/2016/11/13/android-installd)

    • Alarm服务

      理解AlarmManager机制(http://gityuan.com/2017/03/12/alarm_manager_service/)

    • JobScheduler服务

      理解JobScheduler机制(http://gityuan.com/2017/03/10/job_scheduler_service/)

    • BatteryService

      Android耗电统计算法(http://gityuan.com/2016/01/10/power_rank/)

    • PMS服务

    • DropBox服务

      DropBoxManager启动篇(http://gityuan.com/2016/06/12/DropBoxManagerService/)

    • UserManagerService

      多用户管理UserManager(http://gityuan.com/2016/11/20/user_manager/)

    4.7 内存&&存储篇

    • 内存篇

      Android LowMemoryKiller原理分析(http://gityuan.com/2016/09/17/android-lowmemorykiller/)

      Linux内存管理(http://gityuan.com/2015/10/30/kernel-memory/)

      Android内存分析命令(http://gityuan.com/2016/01/02/memory-analysis-command/)

    • 存储篇

      Android存储系统之源码篇:http://gityuan.com/2016/07/17/android-io/

      Android存储系统之架构篇:http://gityuan.com/2016/07/23/android-io-arch

    • Linux驱动篇

      敬请期待

    • dalvik/art

      解读Java进程的Trace文件:http://gityuan.com/2016/11/26/art-trace/

    4.8 工具篇

    再来说说Android相关的一些常用命令和工具以及调试手段。

    序号 文章名 类别
    1 理解Android编译命令(http://gityuan.com/2016/03/19/android-build/) build
    2 理解Android.bp(http://gityuan.com/2018/06/02/android-bp/) build
    2 性能工具Systrace(http://gityuan.com/2016/01/17/systrace/) systrace
    3 Android内存分析命令(http://gityuan.com/2016/01/02/memory-analysis-command/) Memory
    4 ps进程命令(http://gityuan.com/2015/10/11/ps-command/) Process
    5 Am命令用法(http://gityuan.com/2016/02/27/am-command/) Am
    6 Pm命令用法(http://gityuan.com/2016/02/28/pm-command/) Pm
    7 调试系列1:bugreport源码篇(http://gityuan.com/2016/06/10/bugreport/) bugreport
    8 调试系列2:bugreport实战篇(http://gityuan.com/2016/06/11/bugreport-2/) bugreport
    9 dumpsys命令用法(http://gityuan.com/2016/05/14/dumpsys-command/) dumpsys
    10 Android logd日志原理(http://gityuan.com/2018/01/27/android-log/) logd
    11 介绍gdb调试工具(http://gityuan.com/2017/09/09/gdb/) gdb
    12 介绍addr2line调试命令(http://gityuan.com/2017/09/02/addr2line/) addr2line

    4.9 实战篇

    下面列举处理过的部分较为典型的案例,供大家参考:

    序号 文章名 类别
    1 Binder Driver缺陷导致定屏的案例(http://gityuan.com/2018/05/12/binder-driver-bug/) binder
    2 深度解读ArrayMap优势与缺陷(http://gityuan.com/2019/01/13/arraymap/) ArrayMap
    3 数组越界导致系统重启的案例(http://gityuan.com/2018/02/10/reboot-locked-method/) 数组越界
    4 一行Log引发多线程并发问题的案例(http://gityuan.com/2018/02/03/log-fc/) 多线程并发
    5 跑monkey压力测试过程的冻屏案例(http://gityuan.com/2018/02/17/monkey-deadlock/) monkey冻屏
    6 深度剖析APP保活案例(http://gityuan.com/2018/02/24/process-keep-forever/) 保活

     

    结束语

     

    Android系统之博大精深,包括Linux内核、Native、虚拟机、Framework,通过系统调用连通内核与用户空间,通过JNI打通用户空间的Java层和Native层,通过Binder、Socket、Handler等打通跨进程、跨线程的信息交换。只有真正阅读并理解系统核心架构的设计,解决问题和设计方案才能做到心中无剑胜有剑,才能做到知其然知其所以然。当修炼到此,恭喜你对系统有了更高一个层次的理解,正如太极剑法,忘记了所有招式,也就练成了太极剑法。

    再回过头去看看那些API,看到的将不再是一行行代码、一个个接口的调用,而是各种信息的传递与交互工作,而是背后成千上万个小蝌蚪的动态执行流。记得《侠客行》里面的龙木二岛主终其一生也无法参透太玄经,石破天却短短数日练成绝世神功,究其根源是龙木二岛主以静态视角去解读太玄经,而石破天把墙壁的图案想象成无数游动的蝌蚪,最终成就绝世神功。一言以蔽之,程序代码是死的,系统运转是活的,要以动态视角去理解系统架构。

    作者:袁辉辉,安卓知名博主Gityuan,现就职于手机厂商,热衷于研究Android系统架构与内核技术,对Android系统框架有着深刻理解与丰富的实战经验。

    转载地址:https://mp.weixin.qq.com/s?__biz=MjM5MjAwODM4MA==&mid=2650717962&idx=4&sn=d2d68b759ba53c392b5e56acb171e6ba&chksm=bea6ced989d147cfaa6ca7544209fc520f7de260a82bf8ba418a88ec2f46a91afb03e16343e9&mpshare=1&scene=23&srcid=0412B6lHrCvWrHkLTCfgtMBv#rd

    展开全文
  • Android架构设计模式总结(MVX)

    千次阅读 2017-02-20 11:42:48
    Android架构设计模式总结(MVX)一、安卓软件架构设计的目的通过设计使程序模块化,做到模块内部的高聚合和模块之间的低耦合。这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点,提高程序开发的效率...

    Android架构设计模式总结(MVX)


    hongyang关于MVP
    郭霖MVP

    一、安卓软件架构设计的目的

    通过设计使程序模块化,做到模块内部的高聚合模块之间的低耦合。这样做的好处是使得程序在开发的过程中,开发人员只需要专注于一点,提高程序开发的效率,并且更容易进行后续的测试以及定位问题。但设计不能违背目的,对于不同量级的工程,具体架构的实现方式必然是不同的,切忌犯为了设计而设计,为了架构而架构的毛病。

    举个简单的例子:

    一个Android App如果只有3个Java文件,那只需要做点模块和层次的划分就可以,引入框架或者架构反而提高了工作量,降低了生产力;
    但如果当前开发的App最终代码量在10W行以上,本地需要进行复杂操作,同时也需要考虑到与其余的Android开发者以及后台开发人员之间的同步配合,那就需要在架构上进行一些思考!

    二、MVC、MVP、MVVM架构设计模式的联系与区别

    1、MVC(单向循环,每一层都做两件事,先接受命令再通知)

    什么是MVC?

    MVC:Model View Controller,是软件架构中最常见的一种框架

    View:对应于布局文件,一般采用XML文件进行界面的描述
    
    Model:对应于安卓中的database、实体类等,负责业务逻辑的处理和实体模型;
    
    Controllor:对应于Activity与Fragment,处理数据、业务和UI;
    

    特点:

    View可以与Model直接交互。 
    
    Controller是基于行为的,并且可以被多个View共享。 
    

    原理:

    当用户触发事件的时候,view层会发送指令到controller层,接着controller去通知model层更新数据,model层更新完数据以后直接显示在view层上。
    

    2、MVP

    什么是MVP?

    在实际运用中人们发现View和Model之间的依赖还是太强,希望他们可以绝对独立的存在,慢慢的就演化出了MVP,Presenter 替换掉了Controller,不仅仅处理逻辑部分,而且还控制着View的刷新,监听Model层的数据变化。这样隔离掉View和Model的关系后使得View层变的非常的薄,没有任何的逻辑部分又不用主动监听数据,被称之为“被动视图”。

    View: 对应于Activity和XML文件,负责View的绘制以及与用户交互 
    
    Model: 依然是业务逻辑和实体模型 
    
    Presenter: 负责完成View于Model间的交互和业务逻辑,不仅负责业务处理还控制着view的刷新
    

    特点:

    View不直接与Model交互,而是通过与Presenter交互来与Model间接交互。
    
    Presenter与View的交互是通过接口来进行的。
    
    通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑。
    

    原理:

    举个简单的例子,UI层通知逻辑层(Presenter)用户点击了一个Button,逻辑层(Presenter)自己决定应该用什么行为进行响应,该找哪
    
    个模型(Model)去做这件事,最后逻辑层(Presenter)将完成的结果更新到UI层。
    

    3、MVVM

    说到Android MVVM,相信大家都会想到Google 2015年推出的DataBinding框架。然而两者的概念是不一样的,不能混为一谈。MVVM是一种架构模式,而DataBinding是一个实现数据和UI绑定的框架,是构建MVVM模式的一个工具。

    Model:代表你的基本业务逻辑和实体模型,也就是数据源
    
    View:对应于Activity和XML,负责View的绘制以及与用户交互,显示内容
    
    ViewModel:ViewModel是Model和View的中介,负责处理视图逻辑。通常,ViewModel通过调用Model中的方法与Model交互。然后,ViewModel以一种View很容易地使用的形式提供来自Model的数据。ViewModel从模型中检索数据并将数据提供给视图,并可能重新格式化数据以某种方式使它的视图来处理更简单。视图模型还提供了应用程序的用户在视图中启动的命令的实现
    

    特点:

    低耦合 。 视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
    
    可重用性 。 你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
    
    独立开发 。 开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
    
    可测试 。 界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。
    

    原理

    Android中MVVM的实现要借助Google提出的DataBinding库实现。首先viewmodel与view层通过DataBinding双向绑定,一个更新另一个也跟着更新。model提供数据,viewmodel通过model提供的接口方法进行数据操作,并在view层显示出来。
    

    具体实现

    1、添加DataBinding的依赖–在app级的module中添加

    //绑定DataBinding库
    dataBinding {
    enabled true
    }
    

    2、布局实现上的区别–根标签是layout,在中添加变量

    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android">
    <data>
        <variable
            name="user2"
            type="com.metashipanwf.mvvmtest.model.User2" />
    </data>
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal"
        android:padding="10dp">
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.firstName}" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{user.lastName}" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text='@{user.age+""}' />
    </LinearLayout>
    </layout>
    

    3、添加如上布局之后自动生成binding类,在布局对应的activity中绑定数据

    public class MainActivity extends AppCompatActivity {
      private ActivityMainBinding binding;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
      	super.onCreate(savedInstanceState);
      	binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
      	User user = new User("Micheal", "Jack");
      	binding.setUser(user);
      }
    }
    

    三、参考网址

    http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece763105392230e54f7636d918d027fa3c215cc7902155a66e1b823201619d4c77d610aad5e5c9daa702d691765eadb9e875dadb985582f9f5131676cf25613a346eb8f1932c050872a98b86f96ad803984ded9c4de2444bc57120c82e78a2a1765cb78806026e2d68e3e154860cafa3115e829773ee82257b737ee9037797686e1dc2c5bb259c7111180de34a74e14&p=ce759a45d5c100fc57efc8265048bb&newp=c662c31286cc41ac5ab2d62d0214cd231610db2151d4d201298ffe0cc4241a1a1a3aecbf26281100d3c47b6202a8495ae8f03273330834f1f689df08d2ecce7e70ca&user=baidu&fm=sc&query=%B0%B2%D7%BFmvvm%BC%DC%B9%B9%D6%D0%C3%BF%D2%BB%B2%E3%B7%C5%CA%B2%C3%B4%A1%A2&qid=d9f8ec8900001a0d&p1=4
    
    http://blog.csdn.net/zh394759565/article/details/51613831
    
    http://mp.weixin.qq.com/s?__biz=MjM5NDkxMTgyNw==&mid=405212572&idx=1&sn=e2a84186dccc7661e0644feb38d74f8c&scene=21#wechat_redirect
    
    http://tech.meituan.com/android_mvvm.html	
    

    微软解释
    DataBinding
    郭霖关于mvvm的解读

    四、其他网站

    1、用 Retrofit 2 简化 HTTP 请求

    2、RxJava详解

    3、完全掌握Android Data Binding

    4、mvvm

    5、Android APK反编译就这么简单 详解(附图)

    展开全文
  • Android体系架构

    万次阅读 2018-08-29 08:52:54
    Android 是一种基于 ...与其他操作系统一样,Android也采用分层的架构设计,从高到低分别是系统应用层(System Apps),Java API 框架层(Java API Framework),Android系统运行层(包括Android Runtime和原生态...
  • Android架构

    千次阅读 2010-09-25 14:56:00
    1、架构图直观 2、架构详解 2.1、Linux Kernel 2.1、Android Runtime 2.3、...Android系统架构(来源于:android sdk)可以很明显看出,Android系统架构由5部 分组成,分别是:Linux Kernel、Android Runtime、Libra
  • android系统架构

    2018-08-15 14:49:52
    android 的系统架构图,设计工作者可查阅————————————————————————————
  • 一种更清晰的Android架构

    千次阅读 多人点赞 2015-03-23 10:14:37
    过去几个月以来,通过在Tuenti网站上与@pedro_g_s和@flipper83(安卓开发两位大牛)进行友好讨论之后,我决定写这篇关于架构Android应用的文章。 我写这篇文章的目的是想把我在过去几个月体悟到的小方法以及在调查...
  • Android技术架构演进与未来

    千次阅读 2019-03-22 11:27:43
    经授权转载自公众号Android达摩院(id:AndroidAcademy) 引言 众所周知,Android是谷歌开发的一款基于Linux的开源操作系统,每年迭代一次大版本升级。 小米、华为、OPPO、VIVO、三星等各大厂商对Android原生系统...
  • Android系统框架总结(好文)

    万次阅读 2018-08-15 15:47:06
    Android框架图 Android应用程序框架 UI基本开发 Fragment 安全策略 在Android中,安全涵盖了应用程序的部署和执行.对于部署来 说,Android应用程序必须被赋予一个数字证书才允许被安装到某个设备中.对 于运行来...
  • 基于Android 8.0 的 Telephony 解析,对Android 通话 进行详细分析 Dialer Telecom TeleService RIL 之间的通信.
  • Android 架构师成长之路

    千次阅读 多人点赞 2017-09-11 14:56:53
    前言 ...其中,稳坐榜首的仍然是Java编程语言,...从普遍的企业的角度来看,存在的问题是:后台被认为是技术核心,客户端却被认为技术含量不高,甚至小企业会让后台人员顺便开发简单的客户端,或者让后台的架构师管理...
  • 前言 近几年,Android 开发的套路日趋成熟,越来越多的 Android 工程师获得了「高级」的称号,也有不少人在参与公司的 ...市场真正需要的所谓「高级架构师」到底需要具备什么条件?在此和大家探讨一下 Android 工程...
  • Android架构组件

    2018-01-26 16:57:44
    Android架构组件 App开发者面临的常见问题 与传统的桌面应用程序不同,android应用程序的结构要复杂得多,在大多数情况下,它们只在桌面快捷启动方式中有一个入口,并且作为单个进程运行。一个典型的android...
  • 很多朋友都或多或少经历过很多面试,而有的人拿到了自己心仪的offer,有的人却被拒之门外。那么今天小编整理了一份面试经常会问到的题目,希望可以帮助到更多的人。 本文讲解: 我们为什么要选择离职 ...
  • Android 架构组件(一)——Lifecycle

    万次阅读 多人点赞 2017-12-22 11:25:38
    Android 架构组件(一)——Lifecycle-Aware Components 有一天“谷歌开发者”官微推送了《正式发布 Android 架构组件 1.0 稳定版 | 附带中文介绍视频》,发现这种架构足够秒杀MVP、MVVM,虽然之前的Google I/...
  • Android架构设计模式(三)

    千次阅读 2018-04-16 20:10:04
    前言 在Android日常开发中或者在面试过程中总会涉及到“设计模式”这个词。...此设计模式系列仅含有应用架构设计模式,这里我就MVC,MVP,MVVM这3个最常见的架构设计模式来总结。 MVVM设计模式 ...
  • 很早就有人总结了Android架构师技能图谱,小伙伴们可以参考一下自己学习到不到位。看到这浩瀚的知识海洋,很多小伙伴们怕是要晕了。别急,今天给大家沿着Android的技能知识图谱来总结一下视频学习教程。感兴趣的小...
  • Android架构组件-WorkManager

    万次阅读 2018-05-21 20:41:52
    使用WorkManager安排任务 WorkManager API可以轻松指定可延迟的异步任务以及何时运行,这些API可让您创建任务并将其交给WorkManager,以便立即或在适当的时间运行。例如,应用程序可能需要不时从网络下载新资源。...
  • 很多Android的小伙伴在做了多年的开发之后,始终搞不清楚达到Android架构师需要何种技能,我们对比着Android高级工程师来说明。 我们先来看一下Android高级工程师的招聘要求 职位描述: Responsibility Android...
  • 当程序员容易,当一个优秀的程序员需要不断学习,从初级程序员到高级程序员,从初级架构师到资深架构师,或者走向管理,从技术经理到技术总监,每个阶段都需要掌握不同的能力。早早确定自己的职业方向,才能在工作和...
  • Android系统的架构可以分为六个部分。笔者根据自己的体会为大家推荐每个部分对应的精品书籍,不喜勿喷。  1、Android Application  《Android Developer Document》、《Pro Android》、《Professional Android ...
  • Android架构实战(一)—— 核心思想

    千次阅读 2015-07-19 22:33:53
    经过对一种更清晰的Android架构的学习,并进行了一定的实战摸索后,也算是对这种Android架构有了一定的了解。由于该架构作者的DEMO比较简单,在实际运用的时候也遇到了许多坑,在自己摸索之后,基本构建出了一套比较...
  • Android架构文章集合

    2016-12-18 15:38:33
    安卓架构文章合集(a collection of android Architecture) fork from :https://github.com/Juude/Awesome-Android-Architecture!!! 博客原地址: 简书博客 github地址: ...
  • 几十亿的设备都在用 Android 系统,从高端手机到飞机上的影音娱乐系统,应有尽有,不一而足。...为了简化开发流程,我们在 Google I/O 大会上发布了架构组件(Architecture Components)的预览版,为 App 开发构架提
  • Android 架构师需要掌握的技能架构与设计设计模式重构技术选型特性可用性性能包大小方法数文档技术支持UI架构模式MVCMVPMVVM研发工具集成开发环境Android StudioSublime Text版本控制系统...
  • 本文通过实验而不是源码 Android 系统层层拆解 令读者深刻透彻地掌握 Android 系统的内部技术;以init进程为切入点详细阐述了 Android 的启动过程和关键服务;从 Android 作为资源协调者和服务提供者的角度,重点...
  • 2017 Google IO 大会 不仅确立了 Kotlin 为安卓开发的官方语言,还有一个细节,发布了谷歌官方 Android 应用架构库。这新的架构库旨在帮助开发者设计健壮、可测试的和可维护的应用程序。在Mvp之后,Google又增加Mvp+...
  • 今天,就为大家整理一份目前互联网公司最主流的Android开发架构技术。按照这个框架去完善和深入自己的技术,年薪40w又有何难呢? 1.Java语言进阶与Android相关技术 Android应用是由Java语言进行开发的,S...
  • Android 架构篇----------Framework初识

    千次阅读 2018-09-06 12:55:20
    来谈Android Framework时我们先来看一张Android架构图,   Android的架构大概有四层,(1)Application (2) Application FrameWork (3) Library Android Runtime (4)Linux Kernel 今天主要聊聊 ...

空空如也

1 2 3 4 5 ... 20
收藏数 170,934
精华内容 68,373
关键字:

android架构