知识笔记

Android

在新技术不断涌现的浪潮里,除了学习吸收新的技术,还要学会追根溯源,深入底层,才能更加了解这门技术,而不仅仅站在象牙塔之外,只能看到事物的表象,仅仅皮毛而已。

学无止境!不进则退!

架构图

知识笔记
引用Google官网

Rxjava2

对其馋涎已久,却一直没有机会完全的了解,皆因各种原因中断,着实可惜。近来也是需要多学多看,所以将其重新拾起。

实例演示:

		//test1:支持java1.8的lambdas   
        Disposable hell_tom = Flowable.just("hell Tom").subscribe(System.out::println);
        //or
        Disposable hell_tom1 = Flowable.just("hell Tom").subscribe(new Consumer<String>() {
            @Override
            public void accept(String s) throws Exception {
                System.out.println("or:"+s);
            }
        });

观察者模式

Rxjava是以观察者模式为骨架的。因此有必要熟悉。

观察者模式定义了对象之间的一对多依赖关系,这样一来,当一个对象改变状态时,它的所有依赖者都会收到通知并且自动更新。

在这里,发生改变的对象称之为观察目标,而被通知的对象称之为观察者。一个观察目标可以对应多个观察者,而且这些观察者之间没有相互联系,所以么可以根据需要增加和删除观察者,使得系统更易于扩展。所以观察者提供了一种对象设计,让主题和观察者之间以松耦合的方式结合。

知识笔记

观察者模式包含如下角色:

  • Subject: 目标
  • ConcreteSubject: 具体目标
  • Observer: 观察者
  • ConcreteObserver: 具体观察者

实例:

知识笔记
观察目标:

public abstract class Subject {
    /**
     * 用来保存注册的观察者对象
     */
    private    List<Observer> list = new ArrayList<Observer>();
    /**
     * 注册观察者对象
     * @param observer    观察者对象
     */
    public void attach(Observer observer){
        
        list.add(observer);
        System.out.println("Attached an observer");
    }
    /**
     * 删除观察者对象
     * @param observer    观察者对象
     */
    public void detach(Observer observer){
        
        list.remove(observer);
    }
    /**
     * 通知所有注册的观察者对象
     */
    public void nodifyObservers(String newState){
        
        for(Observer observer : list){
            observer.update(newState);
        }
    }
}

具体观察目标:

public class ConcreteSubject extends Subject{
    
    private String state;
    
    public String getState() {
        return state;
    }

    public void change(String newState){
        state = newState;
        System.out.println("主题状态为:" + state);
        //状态发生改变,通知各个观察者
        this.nodifyObservers(state);
    }
}

观察者:

public interface Observer {
    /**
     * 更新接口
     * @param state    更新的状态
     */
    public void update(String state);
}

具体观察者:

public class ConcreteObserver implements Observer {
    //观察者的状态
    private String observerState;
    
    @Override
    public void update(String state) {
        /**
         * 更新观察者的状态,使其与目标的状态保持一致
         */
        observerState = state;
        System.out.println("状态为:"+observerState);
    }

}

客户端类

public class Client {

    public static void main(String[] args) {
        //创建主题对象
        ConcreteSubject subject = new ConcreteSubject();
        //创建观察者对象
        Observer observer = new ConcreteObserver();
        //将观察者对象登记到主题对象上
        subject.attach(observer);
        //改变主题对象的状态
        subject.change("new state");
    }

}

在运行时,这个客户端首先创建了具体主题类的实例,以及一个观察者对象。然后,它调用主题对象的attach()方法,将这个观察者对象向主题对象登记,也就是将它加入到主题对象的聚集中去。

这时,客户端调用主题的change()方法,改变了主题对象的内部状态。主题对象在状态发生变化时,调用超类的notifyObservers()方法,通知所有登记过的观察者对象。

一个观察目标,对应可以多个的观察者。
1…N的关系。

本段引用

一些术语

  1. Upstream, downstream :上流、下流

  2. Objects in motion :

  3. Backpressure :背压

In RxJava, the dedicated Flowable class is designated to support backpressure and Observable is dedicated for the non-backpressured operations (short sequences, GUI interactions, etc.). The other types, Single, Maybe and Completable don’t support backpressure nor should they; there is always room to store one item temporarily.

在RxJava中,专用的Flowable类被指定为支持反压力,Observable则用于非反压力操作(短序列、GUI交互等)。其他类型,单管、可能和可完井不支持反压力,也不应该支持;总有空间暂时存放一件物品。

  1. Assembly time :装配

  2. Subscription time :订阅

  3. Runtime:运行时

		//术语说明
		//--Assembly time : 装配时
        Flowable<Integer> integerFlowable  = Flowable.range(1,5)
                .map(v -> v * v)
                .filter(v -> v % 3 == 0);
        //--Subscription time :订阅时
        Disposable subscribe = integerFlowable.subscribe(System.out::println);
        
        //--Runtime : 运行时
        Disposable subscribe1 = Observable.create(emitter -> {
            while (!emitter.isDisposed()) {
                long time = System.currentTimeMillis();
                emitter.onNext(time);
                if (time % 2 != 0) {
                    //主动发出异常错误,不影响后面的数据处理
                    emitter.onError(new IllegalStateException("Odd millisecond!"));
                }

            }

        }).subscribe(System.out::println, Throwable::printStackTrace);

Simple background computation:简化的后台操作

RxJava的一个常见用例是在后台线程上运行一些计算、网络请求,并在UI线程上显示结果(或错误)

实例:

		//--Simple background computation:简化的后台操作
        Disposable subscribe2 = Flowable.fromCallable(() -> {
            Thread.sleep(1000); //  imitate expensive computation
            return "done";
        }).subscribeOn(Schedulers.io())
                .observeOn(Schedulers.single())
                .subscribe(System.out::println, Throwable::printStackTrace);
        Thread.sleep(2000); // <--- wait for the flow to finish

        //complex :复杂的
        Flowable<String> source = Flowable.fromCallable(() -> {
            Thread.sleep(1000); //  imitate expensive computation
            return "Done";
        });

        Flowable<String> runBackground = source.subscribeOn(Schedulers.io());

        Flowable<String> showForeground = runBackground.observeOn(Schedulers.single());

        showForeground.subscribe(System.out::println, Throwable::printStackTrace);

        Thread.sleep(2000);

Retrofit2

其实是OkHttp3的一个扩展。

实例代码:

			//test
            Retrofit build = new Retrofit.Builder().baseUrl("https://api.github.com/").build();

            GitHubService gitHubService = build.create(GitHubService.class);
            //很直接就获取了结果。
            //和传统的请求比较:传参和结果的获取更加直观。
            retrofit2.Call<List<Repo>> name = gitHubService.listRepos("name");

源码片段:

import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;

public Retrofit build() {
      if (baseUrl == null) {
        throw new IllegalStateException("Base URL required.");
      }

      okhttp3.Call.Factory callFactory = this.callFactory;
      if (callFactory == null) {
      	//创建okhttp3的实例
        callFactory = new OkHttpClient();
      }
      ....

系统启动流程

(——某应聘时岗位需求)

对于Android系统整个启动过程来说,基本可以划分成三个阶段:Bootloader引导、Linux kernel启动、Android启动。如下的流程图可以看出三个阶段的执行顺序和联系:

知识笔记

Bootloader引导

当电源按下,引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序到RAM,然后执行。
BootLoader是在操作系统内核运行之前运行。可以初始化硬件设备、建立内存空间映射图,从而将系统的软硬件环境带到一个合适状态,以便为最终调用操作系统内核准备好正确的环境。

Linux kernel启动

Android内核启动时,会设置缓存、被保护存储器、计划列表,加载驱动。当内核完成系统设置,它首先在系统文件中寻找init文件,然后启动root进程或者系统的第一个进程。

Init进程的启动

init进程,它是一个由内核启动的用户级进程。内核自行启动(已经被载入内存,开始运行,并已初始化所有的设备驱动程序和数据结构等)之后,就通过启动一个用户级程序init的方式,完成引导进程。
启动过程就是代码init.c中main函数执行过程:system\core\init\init.c

在函数中执行了:文件夹建立,挂载,rc文件解析,属性设置,启动服务,执行动作,socket监听……

在rc文件解析中会启动servicemanager服务和Zygote进程

servicemanager服务

ServiceManager用来管理系统中所有的binder service,不管是本地的c++实现的还是java语言实现的都需要这个进程来统一管理,最主要的管理就是,注册添加服务,获取服务。所有的Service使用前都必须先在servicemanager中进行注册。

Zygote进程的启动

Zygote这个进程起来才会建立起真正的Android运行空间。
不过 Init 进程不会直接启动 Zygote 进程,而是使用 app_process 命令来通过 Android Runtime 来启动,Android Runtime 会启动第一个 Davlik 虚拟机,并调用 Zygote 的 main 方法。
初始化建立的Service都是Navtive service.

在.rc脚本文件中zygote的描述:
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server

Android启动

Zygote启动

Zygote从main(…)@frameworks/base/cmds/app_main.cpp开始

  1. main(…)@frameworks/base/cmds/app_main.cpp
runtime.start("com.android.internal.os.ZygoteInit", startSystemServer); //建立android runtime
  1. @/frameworks/base/core/jni/AndroidRuntime.cpp
 	runtime.start("com.android.internal.os.ZygoteInit",...)//调用java的ZygoteInit
  1. main()@com.android.internal.os.ZygoteInit //真正的Zygote

    在com.android.internal.os.ZygoteInit的main函数中至少可以看到

	registerZygoteSocket(...);	//登记Listen端口
	startSystemServer(...);      //启动SystemServer服务

SystemServer启动

在上一步中com.android.internal.os.ZygoteInit的main函数中调用了startSystemServer, Zygote上fork了一个进程: com.android.server.SystemServer.于是SystemServer@(SystemServer.java)就建立了。

com.android.server.SystemServer的main函数中会启动一些服务:

{ startBootstrapServices(); startCoreServices(); startOtherServices(); }

startBootstrapServices()中看到:

mActivityManagerService = mSystemServiceManager.startService(ActivityManagerService.Lifecycle.class).getService();

ActivityManagerService.LifecycleActivityManagerService的静态内部类, 持有ActivityManagerService实例。所以ActivityManagerService在这里被启动起来.当然还有很多的其他服务:

(eg:PackageManagerService/PowerManagerService/DisplayManagerService/BatteryService等)
都会被启动起来。

在所有的服务启动完成后,会调用Service.systemReady(...)来通知各对应的服务,系统已经就绪。

ActivityManagerService启动

这个类应该属于平台架构中的java api framework层,是java语言写的

ActivityManagerServicesystemReady()方法中:

startHomeActivityLocked(...);
mStackSupervisor.startHomeActivity(...) //启动home app

这样Home程序被启动起来,整个android的启动过程就完成了,进入用户界面。

引导完成

一旦系统服务在内存中跑起来了,Android就完成了引导过程。在这个时候“ACTION_BOOT_COMPLETED”开机启动广播就会发出去。

引用:
github
简书

ActivityManagerService 基本构架详解

(——某应聘时岗位需求,至少懂得一个Service模块)

AmS可以说是Android上层系统最核心的模块之一,其主要完成管理应用进程的生命周期以及进程的Activity,Service,Broadcast和Provider等。

系统运行的角度看,AmS可以分为Client端Service端

  • Client端运行在各个app进程,app进程实现了具体的Activity,Service等,告诉系统我有那些Activity,Service等,并且调用系统接口来完成显示;
  • Service端运行在SystemServer进程,是系统级别的ActivityManagerService的具体实现,其响应Client端的系统调用请求,并且管理Client端各个app进程的生命周期。

知识笔记

如上面AmS基本类图

  1. 在Client端,我们平常接触的Application,Service和Activity都是Context的子类,Context理解为环境,上下文,也就是它能够告诉系统当前我运行的Activity,Service的情况,显示那些View,干那些活等。Context是一个abstract类,定义的方法具体实现在ContextImpl中。

  2. ContextWrapper作为Context的装饰类,里面的成员变量mBase指向ContextImpl
    当然这儿并不是一定要执行ContextImpl,只要是继承了Context,实现了里面定义的接口,都可以实例化mBase对象。

  3. 另外我们注意到我们调用很多Android的接口,必须要传入Context对象,通常我们直接将Activity对象作为参数传入,如果方法内部定义全局变量或者static的变量,导致长期占用mContext引用,这就会导致Activity对象无法被JVM回收,对此这样的引用占用,很可能导致OOM

  4. 我们注意到Application也是继承自Context,所以Application也是Context,我们知道Application在一个应用启动时就创建,所以我们的app一直存在着全局的Context对象——Application对象,那么我们在一些已知的全局占用Context对象引用的方法调用上可以传递Application对象来避免上面所说的OOM

  5. IActivityManager接口定义了app访问AmS的接口,主要是应用请求AmS要完成某些操作,比如启动、finish某个Activity,启动、stop某个Service。

  6. ActivityManagerService实现了IActivityManager中定义的接口,该类可以说是AmS的核心,AmS的所有具体工作基本上都在该类中或者通过该类控制,ActivityManagerService的实例在进程SystemServer刚启动时初始化。

  7. IApplicationThread接口定义了AmS可以访问app的接口,AmS通过这些接口控制app进程以及完成app的响应;

  8. ApplicationThreadIApplicationThread接口的具体实现,ApplicationThread的实例是在app进程启动时创建ActivityThread对象时初始化,ActivityThread的成员变量mAppThread就是ApplicationThread对象。

  9. 另外为了实现跨进程调用,ActivityManagerProxyApplicationThreadProxy分别实现了IActivityManagerIApplicationThread接口,其作为各自的代理供client和server使用。

  10. AmS也是作为一个系统服务,通过ActivityManager定义了一些借口可以供app使用,ActivityManager中访问AmS的接口都是通过ApplicationThreadProxy实现的。

如下图是AmS内部主要数据结构类图,看了code相信大家都会知道这些类都是干什么的,这儿只是总结一下。

知识笔记

本节引用

点击事件的处理

当我们手指按下时,Android是如何处理点击事件的呢?如何确定是让哪一个控件来处理呢?
简单一句话:层层传递-冒泡的方式处理
举个例子:现在公司来了个小项目,老板一看分配给经理做,经理一看分配给小组长,小组长一看好简单,分配给组员。如果在这个传递过程中(也就是还为分配到最底部时),某一层觉得我来负责这个比较好的话就会拦截掉这个消息,然后把它处理了,下面的就收不到有消息的这个通知。如果一直到了底层的话,组员如果能完成,就完成它。如果不能完成,那么就报告给组长,说组长我做不来,边学边做要影响进度。组长一看我也做不来,就给经理,经理一看我也不会,就给老板。这样也就一层层的传递了。
总结一下就是消息从上到下依次传递,如果在传递的过程中被拦截了就停止下传。如果没有被拦截,就一直传递到底部,如果底部不能够消耗该消息,那么就又一层层的返回来,返给上层,直到被消耗或者是到达最顶层。

在Android中,存在三个重要的方法:

dispathTouchEvent(MotionEvent ev)
onInterceptTouchEvent(MotionEvent ev)
onTouchEvent(MotionEvent ev)

第一个方法负责事件的分发,它的返回值就是表示是否消耗当前事件。
第二个方法是用于判断是否拦截该消息,如果当前View拦截了某个时间,那么在同一个事件序列中,此方法不会被再次调用。返回结果表示是否拦截当前事件
第三个方法就是处理事件。返回结果表示是否消耗当前事件,如果不消耗,则在同一时间序列中,当前View无法再次接收到事件。

对于一个根ViewGroup来说,点击事件产生后,首先会传递给它,调用它的dispath方法。如果这个ViewGroup的onIntercept方法返回true就表示它要拦截当前事件,false就表示不拦截,这个时候事件就会继续传递给子元素,接着调用子元素的dispath方法,直到被处理。

冒泡排序

数值大的往下沉,N个数字要排序完成,总共进行N-1趟排序,每i趟的排序次数为(N-i)次,所以可以用双重循环语句,外层控制循环多少趟,内层控制每一趟的循环次数

		for(int i=0;i<arr.length-1;i++){//外层循环控制排序趟数
      for(int j=0;j<arr.length-1-i;j++){//内层循环控制每一趟排序多少次
        if(arr[j]>arr[j+1]){
          int temp=arr[j];
          arr[j]=arr[j+1];
          arr[j+1]=temp;
        }
      }
    } 

滑动冲突

顺带总结一下滑动冲突的解决吧
View的滑动冲突一般可以分为三种:

  1. 外部滑动和内部滑动方向不一致
  2. 外部滑动方向和内部滑动方向一致
  3. 嵌套上面两种情况

比如说一个常见的,外部一个ListView,里面一个ScrollView。这个时候该怎么解决呢?其实这里想到了ViewPager,它里面实际上是解决了滑动冲突的,可以借鉴一下它的。

滑动处理规则

一般来说,我们可以根据用户手指滑动的方向以及角度来判断用户是要朝着哪个方向去滑动。而很多时候还可以根据项目的需求来指定一套合适的滑动方案。

需要重写用到的控件

外部拦截法和内部拦截法

情景:一个ViewPager嵌套了一个Listview,一个是左右滑动,一个上下滑动。这个时候我们可以用外部拦截法,来处理冲突。在父容器ViewPager中,重写onInterceptTouchEvent()方法,判断当左右滑动时就拦截事件,上下滑动就不拦截,将事件交由子元素Listview来处理。首先我们需要重写一个ViewPager,叫MyViewPager,然后重写onInterceptTouchEvent()方法。具体代码如下:

public class MyViewPager extends ViewPager {
    private int startX;
    private int startY;
   public MyViewPager(Context context) {
      super(context);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
   switch (ev.getAction())
   {
       case MotionEvent.ACTION_DOWN:
          startX= (int) ev.getX();
           startY= (int) ev.getY();
           break;
       case MotionEvent.ACTION_MOVE:

           int dX= (int) (ev.getX()-startX);
           int dY= (int) (ev.getY()-startX);
           if(Math.abs(dX)>Math.abs(dY)){//左右滑动
               return true;
           }else {//上下滑动
              return false;
           }
       case MotionEvent.ACTION_UP:
           break;
   }
   return super.onInterceptTouchEvent(ev);
  }
}

内部拦截法

情景:一个ViewPager嵌套了一个ViewPager,两个都是左右滑动。这个时候我们可以用内部拦截法,来处理冲突。即重写子元素的dispatchTouchEvent()方法,并调用getParent().requestDisallowInterceptTouchEvent(true)是父容器不能拦截子元素需要的事件。下面来看具体代码:

public boolean dispatchTouchEvent(MotionEvent event) {
  ...

   switch (action) {
        case MotionEvent.ACTION_MOVE:
                    getParent().requestDisallowInterceptTouchEvent(true);

           break;
       case MotionEvent.ACTION_MOVE:
           if(子元素需要处理此事件)
                      getParent().requestDisallowInterceptTouchEvent(true);

           break;
      case MotionEvent.ACTION_UP: {
           break;
   }
   ...
   return super.dispatchTouchEvent(event);
 ;
}

案例二:

public class HorizontalScrollViewPager extends ViewPager{

public HorizontalScrollViewPager(Context context) {
    super(context);
}

public HorizontalScrollViewPager(Context context, AttributeSet attrs) {
    super(context,attrs);
}

private float startX;
private float startY;
/**
 * 事件分发
 */
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
   // getParent().requestDisallowInterceptTouchEvent(true); //把事件传递给自己
    switch(ev.getAction()) {
        case MotionEvent.ACTION_DOWN:
            //一定要把事件给自己
            getParent().requestDisallowInterceptTouchEvent(true);
            //1.记录起始坐标
            startX = ev.getX();
            startY = ev.getY();
            break;
        case MotionEvent.ACTION_MOVE:
            //1.来到新的坐标
            float endX = ev.getX();
            float endY = ev.getY(); ev.getRawX();
            //2.计算偏移量
            float distanceX = endX - startX;
            float distanceY = endY - startY;
            //3.判断滑动方向
            if(Math.abs(distanceX) > Math.abs(distanceY)) {
                //水平方向滑动
                //1.如果第0个位置,并且滑动方向是从左到右滑动
                //getParent().requestDisallowInterceptTouchEvent(false);
                if(getCurrentItem() == 0 && distanceX >0 ) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
               // 2.如果是页签页面的最后一个位置,并且滑动方向是从右向左滑动
                //getParent().requestDisallowInterceptTouchEvent(false);
                else if (getCurrentItem() == (getAdapter().getCount()-1) && distanceX <0) {
                    getParent().requestDisallowInterceptTouchEvent(false);
                }
               // 3.其他中间部分
               // getParent().requestDisallowInterceptTouchEvent(true);*/
                else {
                    getParent().requestDisallowInterceptTouchEvent(true);
                }
            } else {
                //竖值方向滑动
                getParent().requestDisallowInterceptTouchEvent(false);
            }
            break;
        case MotionEvent.ACTION_UP:
            break;
    }
    return super.dispatchTouchEvent(ev);
  }
}

引用:简书

View

view的工作流程

主要是指measure,layout、draw

开发框架

MVP

知识笔记

模拟登陆:

model:
//必然需要一个bean类
public class User{
	String username;
	String password;
	...set
	...get
}

and

//还需要一个业务类的操作接口
public interface IUserBiz
{
    public void login(String username, String password, OnLoginListener loginListener);
}

and

//实现业务类接口实现
public class UserBiz implements IUserBiz
{

    @Override
    public void login(final String username, final String password, final OnLoginListener loginListener)
    {
        //模拟子线程耗时操作
        new Thread()
        {
            @Override
            public void run()
            {
                try
                {
                    Thread.sleep(2000);
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
                //模拟登录成功
                if ("zhy".equals(username) && "123".equals(password))
                {
                    User user = new User();
                    user.setUsername(username);
                    user.setPassword(password);
                    loginListener.loginSuccess(user);
                } else
                {
                    loginListener.loginFailed();
                }
            }
        }.start();
    }
}

and

//登录结果回调接口
public interface OnLoginListener
{
    void loginSuccess(User user);

    void loginFailed();
}

view

两个按钮,一个是login,一个是clear;

需要考虑视图上交互功能的实现,需要用到什么功能;

	//界面按钮需要的操作功能
    String getUserName();

    String getPassword();
	//login是个耗时操作,我们需要给用户一个友好的提示,一般就是操作ProgressBar,所以再两个
 	void showLoading();

    void hideLoading();
	
	//login当然存在登录成功与失败的处理,我们主要看成功我们是跳转Activity,而失败可能是去给个提醒
	void toMainActivity(User user);

    void showFailedError();
    //还剩个clear
    void clearUserName();

    void clearPassword();


and

//考虑后的接口,当然这个是要用在activity中的
public interface IUserLoginView
{
    String getUserName();

    String getPassword();

    void clearUserName();

    void clearPassword();

    void showLoading();

    void hideLoading();

    void toMainActivity(User user);

    void showFailedError();

}

总结下,对于View的接口,去观察功能上的操作,然后考虑:

  • 该操作需要什么?(getUserName, getPassword)
  • 该操作的结果,对应的反馈?(toMainActivity, showFailedError)
  • 该操作过程中对应的友好的交互?(showLoading, hideLoading)

and

//实现view接口,也就是activity实现该接口
//当然要创建Presenter对象
public class UserLoginActivity extends ActionBarActivity implements IUserLoginView
{


    private EditText mEtUsername, mEtPassword;
    private Button mBtnLogin, mBtnClear;
    private ProgressBar mPbLoading;
	//IUserLoginView接口被实现之后,作为UserLoginPresenter构造参数传参
    private UserLoginPresenter mUserLoginPresenter = new UserLoginPresenter(this);

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_user_login);

        initViews();
    }

    private void initViews()
    {
        mEtUsername = (EditText) findViewById(R.id.id_et_username);
        mEtPassword = (EditText) findViewById(R.id.id_et_password);

        mBtnClear = (Button) findViewById(R.id.id_btn_clear);
        mBtnLogin = (Button) findViewById(R.id.id_btn_login);

        mPbLoading = (ProgressBar) findViewById(R.id.id_pb_loading);

        mBtnLogin.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                mUserLoginPresenter.login();
            }
        });

        mBtnClear.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
                mUserLoginPresenter.clear();
            }
        });
    }


    @Override
    public String getUserName()
    {
        return mEtUsername.getText().toString();
    }

    @Override
    public String getPassword()
    {
        return mEtPassword.getText().toString();
    }

    @Override
    public void clearUserName()
    {
        mEtUsername.setText("");
    }

    @Override
    public void clearPassword()
    {
        mEtPassword.setText("");
    }

    @Override
    public void showLoading()
    {
        mPbLoading.setVisibility(View.VISIBLE);
    }

    @Override
    public void hideLoading()
    {
        mPbLoading.setVisibility(View.GONE);
    }

    @Override
    public void toMainActivity(User user)
    {
        Toast.makeText(this, user.getUsername() +
                " login success , to MainActivity", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showFailedError()
    {
        Toast.makeText(this,
                "login failed", Toast.LENGTH_SHORT).show();
    }
}

Presenter

//控制层
public class UserLoginPresenter
{
    private IUserBiz userBiz;//业务类
    private IUserLoginView userLoginView;
    private Handler mHandler = new Handler();

    public UserLoginPresenter(IUserLoginView userLoginView)
    {
        this.userLoginView = userLoginView;
        this.userBiz = new UserBiz();
    }

    public void login()
    {
        userLoginView.showLoading();
        userBiz.login(userLoginView.getUserName(), userLoginView.getPassword(), new OnLoginListener()
        {
            @Override
            public void loginSuccess(final User user)
            {
                //需要在UI线程执行
                mHandler.post(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        userLoginView.toMainActivity(user);
                        userLoginView.hideLoading();
                    }
                });

            }

            @Override
            public void loginFailed()
            {
                //需要在UI线程执行
                mHandler.post(new Runnable()
                {
                    @Override
                    public void run()
                    {
                        userLoginView.showFailedError();
                        userLoginView.hideLoading();
                    }
                });

            }
        });
    }

    public void clear()
    {
        userLoginView.clearUserName();
        userLoginView.clearPassword();
    }

}


总结一下,数据层、视图层、控制层、+业务层…居然会多出了一个业务层来。

引用鸿洋