Android Jetpack 应用指南(四) - Lifecycle

简介:

Lifecycle诞生的背景

在 Android 应用程序的开发过程,我们会经常遇到这样一个需求,一个第三方或系统组件需要在Activity/Fragment的onCreate()方法中初始化,在onPause()方法中停止组件,在onDestroy()中对组件进行资源回收。这样的工作非常繁琐,会让组件与页面的耦合度增高,但是这些繁琐的工作却又不得不做,否则可能会造成内存泄露。
我们希望我们对自定义的组件的管理,不依赖与页面的生命周期的回调方法,同时,在页面的生命周期发生变化时,也能及时收到通知。为此Google提供了Lifecycle作为解决方案。

什么是Lifecycle

Lifecycle 可以用于构建生命周期感知型组件,这些组件可以根据 Activity 或 Fragment 的当前生命周期状态调整行为。

Lifecycle的作用与意义

Lifecycle可以帮助开发者创建可感知生命周期的组件。这样,组件就可以在其内部管理自己的生命周期,从而降低模块间的耦合度,并降低内存泄露的发生
需要注意的是,LifeCycle不仅只对Activity/Fragment有用,在Service和Application中同样可以使用。

Lifecycle使用示例

使用Lifecycle监听Activity/Fragment的生命周期

接下来我们以一个案例来演示Lifecycle是如何使用的。
假设有这样一个常见的需求:在用户打开某个页面时,获取用户当前的地理位置。用户离开该页面后,则停止获取用户地理位置。面对这样的需求,我们通常会这么写

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 初始化任务管理器
        initLocationManager();
    }

    @Override
    protected void onStart() {
        super.onStart();
        // 获取用户位置
        getUserLocation();
    }

    @Override
    protected void onPause() {
        super.onPause();
        // 停止获取用户位置
        stopGetLocation();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 释放任务管理器
        releaseLocationManager();
    }
}

从上述代码可以看出,获取地理位置这个需求的实现,和页面的生命周期息息相关。如果我们希望将获得地理位置这一功能独立成一个组件,那么我们不得不在页面生命周期各个回调方法中,对组件进行通知,只有这样组件才能感知到页面的生命周期变化(有经验的开发一定会注意到,这就是早期MVP架构中Presenter感知页面生命周期的做法)。
面对这个问题,接下来我们就来看看,LifeCycle是如何处理的。
在使用 Lifecycle 之前我们需要在应用或模块的build.gradle中添加lifecycle的依赖项

lifecycle-extensions 目前已经弃用。Google为我们提供了,可供自由选择的lifecycle组件
```Groovy
dependencies {
def lifecycle_version = "2.3.1"

// Lifecycles only (without ViewModel or LiveData)
implementation "androidx.lifecycle:lifecycle-runtime:$lifecycle_version"

}

  我们为位置管理器定义一个类LocationObserver的类,LocationObserver需要实现LifecycleObserver接口。如果我们需要得知页面的生命周期,只需要定义一个@OnLifecycleEvent(Lifecycle.Event.ON_XXX)标签即可,当页面的生命周期发生变化时,这些被标识的方法会自定被调用。如下所示:

```Java
//1. 自定义的LifecycleObserver观察者,用注解声明每个方法观察的宿主的状态
class LocationObserver extends LifecycleObserver{
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    void onStart(@NotNull LifecycleOwner owner){
      //开启定位
    }

    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    void onStop(@NotNull LifecycleOwner owner){
       //停止定位
    }
}

在Activity/Fragment中引用LocationObserver,并同过getLifecycle().addObserver(observer)方法将观察者与被观察者绑定起来,这样LocationObserver就能感知到Activity/Fragment的生命周期变化。如下所示:

//2. 注册观察者,观察宿主生命周期状态变化
class MyActivity extends AppCompactActivity{
  public void onCreate(Bundle bundle){

    MyLifecycleObserver observer =new MyLifecycleObserver();
    getLifecycle().addObserver(observer);

  }
}

//2. 注册观察者,观察宿主生命周期状态变化
class MyFragment extends Fragment{
  public void onCreate(Bundle bundle){

    MyLifecycleObserver observer =new MyLifecycleObserver();
    getLifecycle().addObserver(observer);

  }
}

除了继承LifecycleObserver运用注解的形式来实现宿主生命周期的监听,Lifecycle组件中还提供了LifecycleEventObserver来监听宿主的生命周期。

  public class LocationObserver implements LifecycleEventObserver {

    @Override
    public void onStateChanged(@NonNull LifecycleOwner source,
                               @NonNull Lifecycle.Event event) {
        //需要自行判断event是onstart, 还是onstop
    }
}

如果你的项目使用了Java8,那么你还可以使用DefaultLifecycleObserver来监听宿主的生命周期。
使用DefaultLifecycleObserver之前,你还需要在build.gradle中额外引入以下依赖

dependencies {
    def lifecycle_version = "2.3.1"
  ...//省略其它依赖
  implementation "androidx.lifecycle:lifecycle-common-java8:$lifecycle_version"
}

然后实现DefaultLifecycleObserver接口,根据需要实现里面的方法即可。

public class LocationObserver implements DefaultLifecycleObserver {

    @Override
    public void onCreate(@NonNull LifecycleOwner owner) {

    }

    @Override
    public void onStart(@NonNull LifecycleOwner owner) {

    }

    @Override
    public void onResume(@NonNull LifecycleOwner owner) {

    }

    @Override
    public void onPause(@NonNull LifecycleOwner owner) {

    }

    @Override
    public void onStop(@NonNull LifecycleOwner owner) {

    }

    @Override
    public void onDestroy(@NonNull LifecycleOwner owner) {

    }
}

需要额外注意的是,如果你正在使用Java8,那么首先应该考虑的选择是使用DefaultLifecycleObserver而不是LifecycleObserver

  • 如果业务类同时实现DefaultLifecycleObserverLifecycleEventoServer,则将首先调用DefaultLifecycleObserver的方法,然后再调用LifecycleEventoServer.onStateChanged(LifecycleOwner,Lifecycle.Event)
  • 如果一个类实现了DefaultLifecycleObserver,又同时使用了OnLifecycleEvent注解,那么注解方法将被忽略。

使用Lifecycle监听Service的生命周期

在上面我们介绍了如何在组件中监听Activity/Fragment的生命周期,在Android中Service同样是一个拥有生命周期的重要组件。

为了便于对Service生命周期的监听,达到解耦Service与组件的目的,Google提供了一个LifecycleService的类。该类继承自Service,并实现了LifeCycleOwner接口,与Activity/Fragment类似,它也提供了一个名为getLifeCycle()的方法供我们使用,除此以外,它与普通的Service使用起来并无差别。LifecycleService的源码如下:

public class LifecycleService extends Service implements LifecycleOwner {

    private final ServiceLifecycleDispatcher mDispatcher = new ServiceLifecycleDispatcher(this);

    @CallSuper
    @Override
    public void onCreate() {
        mDispatcher.onServicePreSuperOnCreate();
        super.onCreate();
    }

    @CallSuper
    @Nullable
    @Override
    public IBinder onBind(@NonNull Intent intent) {
        mDispatcher.onServicePreSuperOnBind();
        return null;
    }

    @SuppressWarnings("deprecation")
    @CallSuper
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        mDispatcher.onServicePreSuperOnStart();
        super.onStart(intent, startId);
    }

    // this method is added only to annotate it with @CallSuper.
    // In usual service super.onStartCommand is no-op, but in LifecycleService
    // it results in mDispatcher.onServicePreSuperOnStart() call, because
    // super.onStartCommand calls onStart().
    @CallSuper
    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }

    @CallSuper
    @Override
    public void onDestroy() {
        mDispatcher.onServicePreSuperOnDestroy();
        super.onDestroy();
    }

    @Override
    @NonNull
    public Lifecycle getLifecycle() {
        return mDispatcher.getLifecycle();
    }
}

Android原生的Service因为并未实现LifecycleOwner接口,所以LiveData等需要感知生命周期组件,就无法在Service中使用。为了解决这个问题,Android中提供了一个支持Lifecycle功能的LifecycleService
1.在 Service 中使用 Lifecycle 首先需要在build.gradle中添加相关依赖

dependencies {
    def lifecycle_version = "2.3.1"

    // optional - helpers for implementing LifecycleOwner in a Service
    implementation "androidx.lifecycle:lifecycle-service:$lifecycle_version"

}

2.添加相应的依赖之后,我们在创建Service时需要让它继承LifecycleService,LifecycleService是Service的子类,它在Service的基础上实现了LifecyclerOwner接口,并提供getLifecycle()方法以便我们使用。除此以外,它与普通的Service使用起来并无差别。

public class MainService extends LifecycleService {

    public MyLifecycleObserver observer;

    public MainService() {
        observer = new MyLifecycleObserver();
        getLifecycle().addObserver(observer);
    }
}

使用Lifecycle监听Application的生命周期

具有生命周期的系统组件,除了Activity、Fragment和Service外,还有Application。很多时候,我们会遇到这样的需求:我们想知道应用程序当前是前台还是后台,或者当应用程序从后台回到前台时,我们能够得到通知。以前也有不少方案能实现,但是大多比较繁琐,现在利用Lifecycle组件中的ProcessLifecycleOwner,可以非常轻松的监控整个应用程序的生命周期。
1.监听 Application 的生命周期,首先需要在build.gradle中添加相关依赖

dependencies {
    def lifecycle_version = "2.3.1"

    // optional - ProcessLifecycleOwner provides a lifecycle for the whole application process
    implementation "androidx.lifecycle:lifecycle-process:$lifecycle_version"

}

2.然后,定义一个AppLifecycle的类,该类实现LifecycleObserver接口,负责监控整个应用程序的生命周期。

public class AppLifecycle implements LifecycleObserver {

        @OnLifecycleEvent(Lifecycle.Event.ON_CREATE)
        public void onAppCreate() {
            // 在应用的生命周期内,只会被回调一次
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_START)
        public void onAppStart() {
            // 当应用在前台出现时被回调
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
        public void onAppResume() {
           // 当应用在前台出现时被回调
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
        public void onAppPause() {
           // 当应用退出到后台时被回调
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
        public void onAppStop() {
           // 当应用退出到后台时被回调
        }

        @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
        public void onAppDestroy() {
           // 永远不会被调用,系统不会分发ON_DESTROY事件
        }
    }

需要注意的是,监控应用程序生命周期的时候,Lifecycle.Event.ON_CREATE只会被回调一次,Lifecycle.Event.ON_DESTROY则永远不会被回调。
3.接下来在Application中使用ProcessLifecycleOwner.get().getLifecycle().addObserver()方法添加观察者,示例如下:

public class AbsApplication extends Application {

    @Override
    public void onCreate() {
        ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppLifecycle());
        super.onCreate();
    }
}

实际上监听整个应用生命周期并不需要在Application中添加观察者,我们可以在任意合适的地方调用 ProcessLifecycleOwner.get().getLifecycle().addObserver(new AppLifecycle()),但是,如果你需要监听Lifecycle.Event.ON_CREATE事件,就必须在Application生命周期进入OnCreate()之前完成观察者的添加。

Lifecycle原理解析

Fragment是如何实现Lifecycle的

Fragemnt中关于Lifecycle的核心代码如下

public class Fragment implements LifecycleOwner {
LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
  @Override
  public Lifecycle getLifecycle() {  
      return mLifecycleRegistry;
  }

 void performCreate(){
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE);
  }

 void performStart(){
     mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_START);
  }
  .....
 void performResume(){
    mLifecycleRegistry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME);
  }  
}

Fragment中主要就是实现LifecycleOwner接口,然后通过LifecycleRegistry来分发各个生命周期的事件。LifecycleRegistry是整个Lifecycle组件最核心的功能。LifecycleRegistry的工作原理非常复杂,我们在之后的章节中再做介绍。

Activity是如何实现Lifecycle的

Activity在实现上并不类似Fragment,而是额外在Activity中加入了一个ReportFragment来实现对生命周期的分发,主要目的是为了兼容一些不继承自AppComponentActivity的Activity也可以使用Lifecycle的特性。核心源码如下:

public class ComponentActivity extends Activity implements LifecycleOwner{
  private LifecycleRegistry mLifecycleRegistry = new LifecycleRegistry(this);
   @NonNull
   @Override
   public Lifecycle getLifecycle() {
      return mLifecycleRegistry;
   }

  protected void onCreate(Bundle bundle) {
      super.onCreate(savedInstanceState);
      ReportFragment.injectIfNeededIn(this); 
}

接下来我们继续分析ReportFragmentReportFragment中没有加载布局,所以他在Activity中实际是是一个不可见Fragment。它的源码也很简单,本质上就是通过FragmentManager将Fragment加载到Activity中,然后Fragment的生命周期发生变化时,获取Activity中的LifecycleRegistry对象,将事件分发出去。

public class ReportFragment extends Fragment{
  public static void injectIfNeededIn(Activity activity) {
        android.app.FragmentManager manager = activity.getFragmentManager();
        if (manager.findFragmentByTag(REPORT_FRAGMENT_TAG) == null) {
            manager.beginTransaction().add(new ReportFragment(), REPORT_FRAGMENT_TAG).commit();
            manager.executePendingTransactions();
        }
  }

    @Override
    public void onStart() {
        super.onStart();
        dispatch(Lifecycle.Event.ON_START);
    }

    @Override
    public void onResume() {
        super.onResume();
        dispatch(Lifecycle.Event.ON_RESUME);
    }

    @Override
    public void onPause() {
        super.onPause();
        dispatch(Lifecycle.Event.ON_PAUSE);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        dispatch(Lifecycle.Event.ON_DESTROY);
    }

    private void dispatch(Lifecycle.Event event) {
         Lifecycle lifecycle = activity.getLifecycle();
         if (lifecycle instanceof LifecycleRegistry) {
             ((LifecycleRegistry) lifecycle).handleLifecycleEvent(event);
         }
}

Lifecycle是如何分发宿主状态的

观察者与宿主生命周期的对齐

通过查看Fragment/Activity的源代码,我们发现LifecycleRegistry才是负责分发生命周期事件的核心,在分析LifecycleRegistry是如何进行事件分发之前,我们先来看看我们在Activity/Fragmnent中调用的addObserver()方法干了些什么。

 @Override
    public void addObserver(@NonNull LifecycleObserver observer) {
        ...
        // 初始化一个State状态值
        State initialState = mState == DESTROYED ? DESTROYED : INITIALIZED;
        // 将宿主状态的初始值,和传递进来的`LifecycleObserver`封装成一个`ObserverWithState`对象
        ObserverWithState statefulObserver = new ObserverWithState(observer, initialState);
        ObserverWithState previous = mObserverMap.putIfAbsent(observer, statefulObserver);
                ...
                // `calculateTargetState(observer)`计算得出当前宿主的状态
        State targetState = calculateTargetState(observer);
        mAddingObserverCounter++;
        // 循环比对宿主状态与观察者的状态,如果不同步,将`ObserverWithState`的状态前进到和宿主相同的状态
        while ((statefulObserver.mState.compareTo(targetState) < 0
                && mObserverMap.contains(observer))) {
            pushParentState(statefulObserver.mState);
            // 根据观察者当前状态,得到它的下一步状态。需要注意,状态的前进是步进的,它并不会跳过事件,而是依次前进,多次分发。
            final Event event = Event.upFrom(statefulObserver.mState);
            ...
            // 分发状态
            statefulObserver.dispatchEvent(lifecycleOwner, event);
            popParentState();
            // mState / subling may have been changed recalculate
            targetState = calculateTargetState(observer);
        }
      ...
    }

addObserver()的逻辑大致可以分为以下流程

  • 1.初始化一个State状态值
  • 2.将宿主状态的初始值,和传递进来的LifecycleObserver封装成一个ObserverWithState对象
  • 3.将ObserverWithState对象存入一个Map中
  • 4.calculateTargetState(observer)计算得出当前宿主的状态
  • 5.将ObserverWithState的状态前进到和宿主相同的状态
    • 5.1 分发状态变化,内部会调用onStateChanged()方法
            void dispatchEvent(LifecycleOwner owner, Event event) {
            State newState = event.getTargetState();
            mState = min(mState, newState);
            // 分发状态
            mLifecycleObserver.onStateChanged(owner, event);
            // 观察者,内部状态更新
            mState = newState;
        }
      
      通过上述分析,可以看出,addObserver方法主要就完成两个任务,1.将宿主和观察者关联起来;2.将观察者的生命周期与宿主对齐。
      但是我们有两点需要注意:
  • 第一:宿主的状态并不等同于宿主的生命周期,他们的关系如下图。
    宿主状态与生命周期.jpg
  • 第二:观察者在同步宿主状态时,是依次步进的。即使宿主状态与观察者的状态存在多个间距,也不会从INITIALIZED直接跳到STARTED
    反应在开发中,我们就会观察到,如果在Activity的OnStop中注册了一个观察者,那么注册完毕后,该观察者实际上会立即收到OnCreateOnStartOnResumeOnPause的回调,并最终收到OnStop的回调。

宿主分发生命周期状态

handleLifecycleEvent()是宿主在生命周期发生变化时,主动调用的方法,由他负责完成观察者的状态变化和事件分发,接下来我们就来看一下它的实现逻辑:

    public void handleLifecycleEvent(@NonNull Lifecycle.Event event) {
        ...
        moveToState(event.getTargetState());
    }
        ...Lifecycle
        // 根据Event推导出下一步的状态
        public State getTargetState() {
            switch (this) {
                case ON_CREATE:
                case ON_STOP:
                    return State.CREATED;
                case ON_START:
                case ON_PAUSE:
                    return State.STARTED;
                case ON_RESUME:
                    return State.RESUMED;
                case ON_DESTROY:
                    return State.DESTROYED;
                case ON_ANY:
                    break;
            }
            throw new IllegalArgumentException(this + " has no target state");
        }
    ...    
    private void moveToState(State next) {
        ...
        // 更新宿主的状态
        mState = next;
        // 同步观察者的状态
        sync();
        ...
    }

    private void sync() {
        LifecycleOwner lifecycleOwner = mLifecycleOwner.get();
        ...
       // isSynced():判断宿主的状态与观察者状态是否已经同步
        while (!isSynced()) {
            mNewEventOccurred = false;
            // 当宿主状态发生倒退时,同步观察者的状态,并分发相应的事件
            if (mState.compareTo(mObserverMap.eldest().getValue().mState) < 0) {
                backwardPass(lifecycleOwner);
            }
            Map.Entry<LifecycleObserver, ObserverWithState> newest = mObserverMap.newest();
            //当宿主状态发生前进时,同步观察者的状态,并分发相应的事件
            if (!mNewEventOccurred && newest != null
                    && mState.compareTo(newest.getValue().mState) > 0) {
                forwardPass(lifecycleOwner);
            }
        }
        mNewEventOccurred = false;
    }

相关逻辑在代码里已经有相应的注释,这里就不再赘述。到此为止我们就分析完了LifecycleRegistry是如何分发宿主生命周期事件了。

目录
相关文章
|
5天前
|
JSON Java Android开发
探索安卓开发之旅:打造你的第一个天气应用
【10月更文挑战第30天】在这个数字时代,掌握移动应用开发技能无疑是进入IT行业的敲门砖。本文将引导你开启安卓开发的奇妙之旅,通过构建一个简易的天气应用来实践你的编程技能。无论你是初学者还是有一定经验的开发者,这篇文章都将成为你宝贵的学习资源。我们将一步步地深入到安卓开发的世界中,从搭建开发环境到实现核心功能,每个环节都充满了发现和创造的乐趣。让我们开始吧,一起在代码的海洋中航行!
|
5天前
|
存储 搜索推荐 Java
打造个性化安卓应用:从设计到实现
【10月更文挑战第30天】在数字化时代,拥有一个个性化的安卓应用不仅能够提升用户体验,还能加强品牌识别度。本文将引导您了解如何从零开始设计和实现一个安卓应用,涵盖用户界面设计、功能开发和性能优化等关键环节。我们将以一个简单的记事本应用为例,展示如何通过Android Studio工具和Java语言实现基本功能,同时确保应用流畅运行。无论您是初学者还是希望提升现有技能的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧。
|
9天前
|
搜索推荐 开发工具 Android开发
打造个性化Android应用:从设计到实现的旅程
【10月更文挑战第26天】在这个数字时代,拥有一个能够脱颖而出的移动应用是成功的关键。本文将引导您了解如何从概念化阶段出发,通过设计、开发直至发布,一步步构建一个既美观又实用的Android应用。我们将探讨用户体验(UX)设计的重要性,介绍Android开发的核心组件,并通过实际案例展示如何克服开发中的挑战。无论您是初学者还是有经验的开发者,这篇文章都将为您提供宝贵的见解和实用的技巧,帮助您在竞争激烈的应用市场中脱颖而出。
|
11天前
|
算法 Java 数据库
Android 应用的主线程在什么情况下会被阻塞?
【10月更文挑战第20天】为了避免主线程阻塞,我们需要合理地设计和优化应用的代码。将耗时操作移到后台线程执行,使用异步任务、线程池等技术来提高应用的并发处理能力。同时,要注意避免出现死循环、不合理的锁使用等问题。通过这些措施,可以确保主线程能够高效地运行,提供流畅的用户体验。
25 2
|
15天前
|
Java API Android开发
安卓应用程序开发的新手指南:从零开始构建你的第一个应用
【10月更文挑战第20天】在这个数字技术不断进步的时代,掌握移动应用开发技能无疑打开了一扇通往创新世界的大门。对于初学者来说,了解并学习如何从无到有构建一个安卓应用是至关重要的第一步。本文将为你提供一份详尽的入门指南,帮助你理解安卓开发的基础知识,并通过实际示例引导你完成第一个简单的应用项目。无论你是编程新手还是希望扩展你的技能集,这份指南都将是你宝贵的资源。
43 5
|
15天前
|
移动开发 Dart 搜索推荐
打造个性化安卓应用:从零开始的Flutter之旅
【10月更文挑战第20天】本文将引导你开启Flutter开发之旅,通过简单易懂的语言和步骤,让你了解如何从零开始构建一个安卓应用。我们将一起探索Flutter的魅力,实现快速开发,并见证代码示例如何生动地转化为用户界面。无论你是编程新手还是希望扩展技能的开发者,这篇文章都将为你提供价值。
|
24天前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
40 4
|
24天前
|
编解码 Android开发 UED
构建高效Android应用:从内存优化到用户体验
【10月更文挑战第11天】本文探讨了如何通过内存优化和用户体验改进来构建高效的Android应用。介绍了使用弱引用来减少内存占用、懒加载资源以降低启动时内存消耗、利用Kotlin协程进行异步处理以保持UI流畅,以及采用响应式设计适配不同屏幕尺寸等具体技术手段。
47 2
|
1月前
|
XML 数据可视化 Android开发
Android应用界面
Android应用界面中的布局和控件使用,包括相对布局、线性布局、表格布局、帧布局、扁平化布局等,以及AdapterView及其子类如ListView的使用方法和Adapter接口的应用。
20 0
Android应用界面
|
28天前
|
Android开发
Android开发显示头部Bar的需求解决方案--Android应用实战
Android开发显示头部Bar的需求解决方案--Android应用实战
19 0