欧酷网

您的位置:主页>移动开发>

Android进阶

Activity生命周期

典型情况下的生命周期

对于一个特定Activity,第一次启动,回调:onCreate->onStart->onResume

打开新的Activity或切换到桌面:onPause->onStop

  • 新打开Activity时,原Activity的onPause先执行,新的Activity的onResume再执行

回到原来Activity:onRestart->onStart->onResume

按back键回退:onPause->onStop->onDestroy

异常情况下的生命周期

异常情况如:

资源相关的系统配置发生改变导致Activity被杀死并重新创建

重建回调:
Activity->onSaveInstanceState(保存数据)->onCreate->onRestoreInstanceState(恢复数据)

在Activity被意外终止时,系统工作流程(保存数据):
onSaveInstanceState->Window->ViewGroup

注意:系统只有在Activity即将被销毁并且有机会重新显示时才会调用onSaveInstanceState。如正常被销毁则不会调用。

资源内存不足导致低优先级的Activity被杀死

数据存储和恢复同以上情况。

Activity按照优先级分为以下三种:

  • 前台Activity,正在与用户交互,优先级最高
  • 可见但非前台Activity,如在Activity中弹出对话框,此时可见但位于后台无法与用户交互
  • 后台Activity,已经暂停Activity,优先级最低

后台工作若脱离四大组件独自运行在后台,则进程容易被杀死,所以将后台工作放在Service中保有一定优先级则不会轻易被系统杀死。

如果系统配置发生改变,不想Activity被重新创建

在AndroidManifest.xml中Activity的声明,加入以下代码

android:configChanges = "orientation"

代码示例:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c2TEydKy-1581561867014)(…/…/pic/image-20191113093539026.png)]

configChanges项目以及含义:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CcmOeue6-1581561867018)(…/…/pic/image-20191113092944737.png)]

Activity的启动模式

四种启动模式:standard,singleTop,singleTask,singleInstance

Activity的LaunchMode

standard:标准模式
系统默认的模式,每次启动Activity都会重新创建一个新的实例,onCreate,onStart,onResmue都被调用。Activity A被Activity C启动时,A会被加入到C的任务栈中。

singleTop:栈顶复用模式
新的Activity已经位于任务栈的栈顶,则不会被重新创建,且它的onNewIntent方法会被回调,通过此方法取出当前请求信息。onCreate,onStart不会被系统调用。

singleTask:栈内复用模式
单例模式,只要Activity在一个栈中存在,多次启动就不会被重新创建实例,系统也会回调onNewIntent

singleInstance:单例模式
加强的singleTask模式,Activity只能单独位于一个任务栈中。比如A启动后,系统为其重建一个新的任务栈,由于栈内复用性特征,后续请求均不会创建新的A,除非这个独特的任务栈被系统销毁。

任务栈
TaskAffinity任务相关性,这个参数标识Activity所需要的任务栈的名字,默认情况下所有Activity所需任务栈的名字就是应用的包名。

可以单独指定TaskAffinity的属性,TaskAffinity属性主要和singleTask启动模式和allowTaskReparenting属性配对使用。

  • TaskAffinity和singleTask配对使用时,具有该模式的Activity的目前任务栈的名字
  • TaskAffinity和allowTaskReparenting配对使用,产生比较特殊效果。
    • 如当应用A启动应用B的某个Activity,如果此时allowTaskReparenting = true,则应用B被启动后,此Activity直接从应用A的任务栈转移到应用B的任务栈。
    • 因为Activity原本属于B,它的TaskAffinity值不能和A的任务栈相同,所以B启动后,B会创建自己的任务栈,此时Activity原本想要的任务栈已经创建则将Activity从A的任务栈转移到B的任务栈。

任务栈分为前台任务栈和后台任务栈,后台任务栈的Activity处于暂停状态。

给Activity指定启动模式
通过AndroidManifest.xml指定Activity启动模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3vncgTBo-1581561867019)(…/…/pic/image-20191113100922110.png)]

在Intent中设置标志位来为Activity指定启动模式

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DwLYvqpY-1581561867020)(…/…/pic/image-20191113101032370.png)]

第二种方式优先级高于第一种,所以两者同时存在,以第二种为准。第一种方式无法直接为Activity设定FLAG_ACDTIVITY_CLEAR_TOP标识,第二种方式无法为Activity指定singleInstance模式

Activity的Flags

FLAG_ACTIVITY_NEW_TASK
为Activity指定singleTask启动模式

FLAG_ACTIVITY_SINGLE_TOP
为Activity指定singleTop模式

FLAG_ACTIVITY_CLEAR_TOP
具有此标记的Activity启动时,在同一个任务栈中所有位于它上面的Activity都要出栈。

此模式一般和FLAG_ACTIVITY_NEW_TASK配合使用:此时被启动的Activity的实例如果已经存在,则系统会调用onNewIntent。

如果被启动的Activity采用standard模式启动,则它连同它之上的Activity都要出栈,系统会创建新的Activity实例并放入栈中。

FLAG_ACTIVITY_EXCLUDE_FROM_RECENT
具有此标记的Activity不会出现在历史Activity列表中。

IntentFilter的匹配原则

隐式调用需要Intent匹配目标组件的任何一组的IntentFilter就可以启动目标Aactivity。

IntentFilter中设置的过滤信息,如:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-z4wG9luj-1581561867021)(…/…/pic/image-20191113111253017.png)]

action的匹配规则
action的匹配要求Intent中的action存在且必须和过滤规则中的其中一个action相同,action区分大小写,大小写不同的字符串相同的action会匹配失败。

action在Intent中匹配代码如下:

Intent intent = new Intent("com.example.activitytest.ACTION_START");

category的匹配规则
category要求Intent可以没有category,如果存在category,则每一个category都要和过滤规则中任意一个category相同。

注意:不设置category也可匹配的原因是系统在调用startActivity或startAactivityForResult时默认为Intent加上“android.intent.category.DEFAULT”这个category。

category在Intent匹配代码如下:

Intent.addCategory("com.ryg.category.d");

data的匹配规则
data的规则和action类似,要求Intent中必须含有data数据,且data数据能够完全匹配过滤规则中某一个data。

如下过滤规则:

仅含媒体类型的data

<intent-filter>
	<data android:mimeType="image/*"/>
	...</intent-filter>
intent.setDataAndType(Uri.parse("file://abc"),"image/png");

含有媒体类型和其他参数,如URI模式scheme的data

<intent-filter>
	<data android:mimeType="video/mpeg" android:scheme="http" ... />
	<data android:mimeType="audio/mpeg" android:scheme="http" ... />
	...</intent-filter>
intent.setDataAndType(Uri.parse("http://abc"),"video/mpeg");// 或者intent.setDataAndType(Uri.parse("http://abc"),"audio/mpeg");

判断是否有Activity匹配隐式Intent
PackageManager或Intent的resolveActivity返回最佳匹配的Activity信息;queryIntentActivities返回所有成功匹配Activity信息。

public abstract List<ResolveInfo> queryIntentActivities(Intent inetent,int flags);public abstract ResolveInfo resolveActivity(Intent intent,int flags);

只要上述这两个方法不返回null则startActivity可以运行成功。

不含DEFAULT这个category的Activity无法接收隐式Intent。

IPC机制

开启多进程模式

在AndroidManifest中指定android:process属性,以下代码示例在Android中创建多线程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k14LwrjH-1581561867023)(…/…/pic/image-20191113151723964.png)]

进程名的命名
“:”的含义是指要在当前进程名面前附加上当前的包名,则对于SecondActivity,完整的进程名为com.ryg.chapter_2:remote。

  • 进程名以“:”开头的进程属于当前应用的私有进程,其他组件不可以和它跑在同一个进程中。
  • 进程名不以“:”开头的进程是全局进程,其他应用可以通过ShareUID方式和它跑在同一个进程。
    • 系统为每个应用分配唯一一个UID,具有相同UID的应用才能共享数据。两个应用通过相同的ShareUID跑在同一个进程前提是两个应用的签名相同。在这种情况下,它们可以相互访问私有数据、组件信息、内存数据。

ThirdActivity中则是一种完整的命名方式。

IPC基础概念

Serializable接口

Serializable将对象持久化到存储设备上或通过网络传输给其他客户端。

Serializable是java提供的序列化接口,是一个空接口,为对象提供标准的序列化和反序列化操作。

只需在类实现Serializable接口,serialVersionUID声明非必需,即可实现默认的序列化过程。代码示例如下:

public class User implements Serializable{
    
    private static final long serviceVersionUID = 8711368828010083044L;
    
    private int userId;
    private String userName;
    private boolean isMale;
    ...}

ObjectOutputStream和ObjectInputStream实现对象的序列化、反序列化,示例代码如下:

用transient关键字标记的成员变量不参与序列化过程。

// 序列化User user = new User(0,"jake",true);ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("cache.txt"));out.writeObject(user);out.close();// 反序列化ObjectInputStream in = new ObjectInputStream(new FileInputStream("cache.txt"));User newUser = (User)in.readObject();in.close();

Parcelable接口

实现Parcelable接口,一个类的对象就可以实现序列化并可以通过Intent和Binder传递。

序列化的过程需要实现的功能有序列化、反序列化和内容描述:

  • 序列化的功能由writeToParcel方法完成,通过Parcel中write方法实现
  • 反序列化由CREATOR实现,内部标明如何创建序列化对象和数组,通过Parcel的read方法实现
  • 内容描述由describeContents方法实现,仅当当前对象中存在文件描述符时,此方法返回1,其余情况返回0.

以下是示例代码:

public class User implements Parcelable{
    
    public int userId;
    public String userName;
    public boolean isMale;
    
    public Book book;
    
    public User(int userId, String userName, boolean isMale){
        this.userId = userId;
        this.userName = userName;
        this.isMale = isMale;
    }
    
    public int describeContents(){
        return 0;
    }
    
    public void writeToParcel(Parcel out, int flags){
        out.writeInt(userId);
        outwirteString(userName);
        out.writeInt(isMale ? 1: 0);
        out.writeParcelable(book, 0);
    }
    
    public static final Parcelable.Creator<User> CREATOR = 
        new Parcelable.Creator<User>(Parcel in){
        return new User(int);
    }
    
    public User[] newArray(int size){
        return new User[size];
    }
    
    private User(Parcel in){
        userId = in.readInt();
        userName = in.readString();
        isMale = in.readInt() == 1;
        
        /*由于book是另一个可序列化对象,则反序列化时需要传递当前线程上下文类加载器,否则会报无法找到类的错误*/
        book = in.readParcelable(
            Thread.currentThread().
            getContextClassLoader());
    }
        }

系统Parcelable接口

系统提供实现Parcelable接口的类,可以直接序列化,如Intent,Bundle、Bitmap等,同时List、Map也可序列化前提是它们里面的每个元素是可序列化的。

Parcelable vs Serializable

Serializable是java实现的接口,开销很大,序列化和反序列化的过程都需要大量的I/O操作。

Parcelable是Android提供序列化方式,使用较为麻烦但效率较高。

在Android平台上Parcelable更适用;需要将对象序列化到存储设备中或将对象序列化后通过网络传输给客户端则Serializable更适用

Binder

Binder是Android一个类,实现IBinder接口,是一种跨进程通信方式,在bindService时服务端返回一个包含服务端业务调用的BInder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务或者数据。

Binder工作机制
以下是SDK自动生成AIDL文件对应Binder类中的各个方法说明。AIDL文件就是为了方便系统生成代码。

DESCRIPTOR
Binder的唯一标识,一般用当前Binder的类名表示

asInterface(android.os.IBinder obj)
用于将服务端的Binder对象转为客户端所需AIDL接口类型对象。

  • 如果客户端服务端在同一进程,则此方法返回的是服务端的Stub对象本身。

  • 反之则回返系统封装后的Stub.proxy对象。

asBinder
返回当前Binder对象。

onTransact
此方法运行在服务端Binder线程池中,当客户端发起跨进程请求,远程请求通过系统底层封装后由此方法处理。以下是该方法的原型:

public Boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply ,int flags);

输入参数:

  • code:客户端请求目标方法
  • data:目标方法参数
  • reply:目标方法的返回值

Binder的工作机制如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FZme1Q6p-1581561867029)(…/…/pic/image-20191113181802231.png)]

linkToDeath unlinkToDeath
这两个方法使用场景:如果服务端进程由于某种原因异常终止,服务端的Binder连接断裂,从而导致远程调用失败。且我们不知道Binder连接已经断裂,则客户端功能会受影响。

解决思路:通过linkToDeath给Binder设置一个死亡代理,当Binder死亡时得到通知,从而重新发起连接请求恢复连接。

如何设置Binder死亡代理?
声明一个DeathRecipient对象,DeathRecipient是一个接口,内部只有binderDeath()方法,实现这个方法,当Binder死亡时,系统就会回调binderDied方法,就可以移出之前绑定的binder代理并重新绑定远程服务。示例代码如下:

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){
	@override
    public void binderDied(){
        if(mBookManager == null){
            return;
        }
        // 重新绑定远程Service
        mBookManager.asBinder().
            unlinkToDeath(mDeathRecipient, 0);
        mBookManager = null;
    }}

在客户端绑定远程服务成功,给binder设置死亡代理,代码如下:

mService = IMessageBoxManager.Stub.asInterface(binder);binder.linkToDeath(mDeathRecipient, 0);

Android中的IPC方式

Bundle

Activity,Service,Receiver都是支持在Intent中传递Bundle数据的,由于Bundle实现Parcelable接口。传输的数据必须能够被序列化,Bundle不支持的类型无法通过它在进程间传递数据。

文件共享

文件共享的方式适合在对数据同步要求不高的进程间进行通信,并且妥善处理并发读/写的问题。

SharedPreferences是Android提供轻量级存储方案,通过键值对方式存储数据,底层实现上采用xml文件存储键值对。但在多进程模式下,系统对它的读写就会变得不可靠,当面对高并发的读/写访问,SharedPreferences有很大几率丢失数据。

两个进程通过读/写同一个文件来交换数据,示例代码如下:

// 在MainActivity中修改private void persistToFile(){
	new Thread(new Runnable(){
	
		@Override
		public void run(){
				User user = new User(1, "hello world", false);
				File dir = new File(MyConstants.CHAPTER_2_PATH);
				if(!dir.exists()){
					dir.mkdirs();
				}
				File cachedFile = new File(MyConstants.CACHE_FILE_PATH);
				ObjectOutputStream objectOutputStream = null;
				try{
					objectOutputStream = new ObjectOutputStream(
					new FileOutputStream(cachedFile));
					objectOutputStream.writeObject(user);
				}
				catch(IOException e){
					e.printStackTrace();
				}
				finally{
					MyUtils.close(objectOutputStream);
				}
		}
	}).start();}// SecondActivity中的修改private void recoverFromFile(){
    new Thread(new Runnable(){
        
        @Override
        public void run(){
            User user = null;
            File cachedFile = new File(MyConstants.CACH_FILE_PATH);
            if(cachedFile.exists()){
                objectInputStream in = null;
                try{
                    in = new ObjectInputStream(
                        new FileInputStream(cachedFile));
                    user = (User)in.readObject();
                }
                catch(IOException | ClassNotFoundException e){
                    e.printStackTrace();
                }
                finally{
                    MyUtils.close(in);
                }
            }
        }
    }).start();}

Messenger

Messenger是以串行的方式处理客户端请求,如果大量请求同时发送到服务端,Messenger就不合适。

传输字段

Messenger和Message都实现Parcelable接口,因此可以跨进程传输。通过Messenger传输Message,Message中使用的载体只有what,arg1,arg2,Bundle以及replyTo。另一个object字段不支持跨进程传输。

实现Messenger的步骤:

服务端进程
在服务端创建一个Srevice处理客户端连接请求,同时创建一个Handler并通过它创建Messenger对象,在Service的OnBinder返回这个Messenger对象的Binder。

客户端进程
绑定服务端Service,绑定成功后用服务端返回IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息。

如果需要服务端回应客户端,创建一个Handler并创建一个新的Messenger,把这个Messenger对象通过Message的replyTo参数传递给服务端。

如何在服务端接收客户端中发送的消息
服务端代码示例如下:

public class MessengerService extends Service{
    
    private static final String TAG = "MessengerService";
    
    // Service接收处理来自客户端的消息
    private static class MessengerHandler extends Handler{
        
        @Override
        public void handleMessage(Message msg){
            switch(msg.what){
                case MyConstants.MSG_FROM_CLIENT:
                    Log.i(TAG, "receive msg from Clinet:" + msg.getData());
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }
    
    private final Messenger mMessenger = new Messenger(new MessengerHandler());
    
    @Override
    public IBinder onBinder(Intent intent){
        return mMessenger.getBinder();    }}

客户端实现的代码如下:

public class MessengerActivity extends Activity{
    
    private static final String TAG = "MessengerActivity";
    
    private Messenger mService;
    
    private ServiceConnection mConn = new ServiceConnection(){
        
        public void onServiceConnected(ComponentName className, IBinder service){
            mService = new Messenger(service);
            Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle;
            data.putString("msg", "hello ,this is client");
            msg.setData(data);
            try{
                mService.send(msg);
            }
            catch (RemoteException e){
                e.printStackTrace();
            }
        }
        
        public void onServiceDisconnected(ComponentName className){}
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        
    }
    
    @Override
    protected void onDestroy(){
        unbinderService(mConnection);
        super.onDestroy();
    }}

如何在客户端接收服务端发送的消息
服务端只需修改MessengerHandler,当收到消息后立即回复客户端,以下是示例代码:

服务端的代码如下:

private static class MessengerHandler extends Handler{
    @Override
    public void handleMessage(Message msg){
        switch(msg.what){
            case MyConstants.MSG_FROM_CLINET:
                Log.i(TAG, "receive msg from client:" + msg.getData().getString("msg"));
                Messenger client = msg.replyTo;
                Message replyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
                Bundle bundle = new Bundle();
                bundle.putString("reply", "your messge has been received,I will reply you later");
                replyMessage.setData(bundle);
                try{
                    client.send(relpyMessage);
                }catch (RemoteException e){
                    e.printStackTrace();
                }
                break;
            default:
                super.handleMessage(msg);
        }
    }}

客户端代码的修改,客户端需要准备一个接收消息的Messenger和Handler,如下:

private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());private static class MessengerHandler extends Handler{
    @Override
    public void handleMessage(Message msg){
        switch(msg.what){
            case MyConstants.MSG_FROM_SERVICE:
                Log.i(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
                break;
            default:
                super.handleMessage(msg);
        }
    }}

Messenger的工作原理[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SbrW10i3-1581561867031)(…/…/pic/image-20191114114908398.png)]

AIDL

一个AIDL文件被aidl工具解析之后会有三个产物:

  • IMyServer接口,仅仅用来在java中声明IMyServer.aidl中声明的接口。
  • IMyServer.Stub类继承自Binder类的抽象类实现服务端与Binder通信相关代码。
  • IMyServer.Stub.Proxy类,这个类实现客户端与Binder通信的相关代码。

服务端
创建一个Service监听客户端的连续请求,创建AIDL文件,将暴露给客户端的接口在AIDL文件中声明,最后在Service中实现这个AIDL文件。

客户端
绑定服务端Service,将服务端返回的Binder对象转成AIDL接口所属类型,接着就可以调用AIDL中的方法。

AIDL接口的创建
AIDL接口创建的示例代码如下:

package com.ryg.chapter_2.aidl;import com.ryg.chapter_2.aidl.Book;interface IBookManager{
	List<Book>getBookList;
	void addBook(in Book book);}

AIDL支持的数据类型:

  • 基本数据类型
  • String,CharSequence
  • List:只支持ArrayList
  • Map:只支持HashMap
  • Parcelable:所有实现Parcelable接口的对象
  • AIDL:所有的AIDL接口本身

注意:AIDL除了基本类型,其他类型的参数必须标上方向:in,out,inout。in表示输入参数;out表示输出参数;inout表示输入输出型参数。

因为在IBookManager.aidl用到Book这个类,则需创建Book.aidl,代码如下:

pakcage com.ryg.chapter_2.aidl;parcelable Book;

AIDL的包结构:服务端和客户端必须保持一致,否则会运行出错,因为客户端需要反序列化服务端中和AIDL接口相关的所有类,如果类的完整路径不一样则无法反序列化成功。

远程服务端Service的实现
创建Service,示例代码如下

public class BookManagerServicee extends Service{
    
    private static final String TAG = "BMS";
    
    // CopyOnWriteArrayList支持并发读写
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
    
    private Binder mBinder = new IBinderManager.Stub(){
        
        @Override
        public List<Book> getBookList() throws RemoteException{
            return mBookList;
        }
        
        @Override
        public void addBook(Book book)throws RemoteException{
            mBookList.add(book);
        }
    };
    
    @Override
    public void onCreate(){
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
    }
    
    @Override
    public IBinder onBind(Intent intent){
        return mBinder;
    }}

客户端的实现
绑定远程服务,绑定成功之后将服务端返回的Binder对转换成AIDL接口,即可通过这个接口去调用服务端的远程方法。示例代码如下:

public class BookManagerActivity extends Activity{
    
    private static final String TAG = "BookManagerActivity";
    
    private ServiceConnection mconn = new ServiceConnection(){
        
        public void onServiceConnected(ComponentName className, IBinder service){
            try{
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "query book list, list type:" + list.getClass().getCanonicalName());
                Log.i(TAG, "query book list:" + list.toString());
            }catch(RemoteException e){
                e.printStackTrace();
            }
        }
        
        public void onServiceDisconnected(ComponentName className){}
    };
    
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this,BookManagerService.class);
        bindService(intent,mConn,Context.BIND_AUTO_CREATE);
    }
    
    @Override
    protected void onDestroy(){
        unbindService(mConn);
        super.onDestroy();
    }}

观察者模式
比如每个感兴趣的用户都在观察新书,当新书到的时候,图书馆就通知每一个对这本书感兴趣的用户。

创建一个iOnNewBookArrivedListener.aidl文件,onNewBookArrived()方法把新书的对象通过参数传递给客户端。示例代码如下:

package com.ryg.chapter_2.aidl;import com.ryg.chapter_2.aidl.Book;interface IOnNewBookArrivedListener{
    void onNewBookArrived(in Book newBook);}

在原接口中新增两个方法:

package com.ryg.chapter_2.aidl;import com.ryg.chapter_2.aidl.Book;import com.ryg.chpter_2.aidl.IOnNewBookArrivedListener;interface IBookManager{
	List<Book>getBookList;
	void addBook(in Book book);
    void registerListener(IOnNewBookArrivedListener listener);
    void unregisterListener(IOnNewBookArrivedListener listener);}

在BookManagerService中开启一个线程,每隔5s就向书库增加一本新书并通知所有感兴趣的用户。服务端的代码示例如下:

public class BookManagerServicee extends Service{
    
    private static final String TAG = "BMS";
    
    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
    
    // CopyOnWriteArrayList支持并发读写
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
    
    private CopyOnWriteArrayList<IOnNewBookArrivedListener> mListenerList = new CopyOnWriteArrayList<IOnNewBookArrivedListener>();
    
    private Binder mBinder = new IBinderManager.Stub(){
        
        @Override
        public List<Book> getBookList() throws RemoteException{
            return mBookList;
        }
        
        @Override
        public void addBook(Book book)throws RemoteException{
            mBookList.add(book);
        }
        
        @Override
        public void registerListener(IOnNewBookArrivedListener listener)throws RemoteException{
            if(!mListenerList.contains(listener)){
                mListenerList.add(listener);
            }
            else{
                Log.d(TAG, "already exists");
            }
            Log.d(TAG, "registerListener, size:" +mListenerList.size());
        }
        
        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener)throws RemoteExceotion{
            if(mListenerList.contains(listener)){
                mListenerList.remove(listener);
                Log.d(TAG, "unregister listener succeed");
            }
            else{
                Log.d(TAG, "not found, can not unregister.");
            }
            Log.d(TAG, "unregisterListener, current size:" + mListenerList.size());
        }
    };
    
    @Override
    public void onCreate(){
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
        new Thread(new ServiceWorker()).start();
    }
    
    @Override
    public IBinder onBind(Intent intent){
        return mBinder;
    }
    
    @Override
    public void onDestroy(){
        mIsServiceDestroyed.set(true);
        super.onDestroy();
    }
    
    private void onNewBookArrived(Book book)throws RemoteException{
        mBookList.add(book);
        Log.d(TAG, "onNewBookArrived, notify listeners:" + mListenerList.size());
        for(int i = 0; i < mListenerList.size(); i++){
            IOnNewBookArrivedListener listener = mListenerList.get(i);
            Log.d(TAG, "onNewBookArrived, notify listener:" + listener);
            listener.onNewBookArrived(book);
        }
    }
    
    private class ServiceWorker implements Runnable{
        @Override
        public void run(){
            while(!mIsServiceDestroyed.get()){
                try{
                    Thread.sleep(5000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try{
                    onNewBookArrived(newBook);
                }catch(RemoteException e){
                    e.printStackTrace();
                }
            }
        }
    }}

客户端先要注册IOnNewBookArrivedListener到远程服务端,在Activity退出时解除这个注册。当有新书时服务端回调客户端IOnNewBookArrivedListener对象中的onNewBookArrived方法,此方法在Binder线程池中执行,为了方便UI操作,需要一个Handler将其切换到客户端的主线程中执行。客户端的代码如下:

public class BookManagerActivity extends Activity{
    
    private static final String TAG = "BookManagerActivity";
    
    private static final int MESSAGE_NEW_BOOK_ARRIVED = 1;
    
    private IBookManager mRemoteBookManager;
    
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg){
            switch(msg.what){
                case MESSAGE_NEW_BOOK_ARRIVED:
                    Log.d(TAG, "receive new book :" + msg.obj);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    };
    
    private ServiceConnection mconn = new ServiceConnection(){
        
        public void onServiceConnected(ComponentName className, IBinder service){
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try{
                mRemoteBookManager = bookManager;
                List<Book> list = bookManager.getBookList();
                Log.i(TAG, "query book list, list type:" + list.getClass().getCanonicalName());
                Log.i(TAG, "query book list:" + list.toString());
                Book newBook = new Book(3, "Android Advanced");
                bookManager.addBook(newBook);
                Log.i(TAG, "add book:" + newBook);
                List<Book>newList = bookManager.getBookList();
                Log.i(TAG, "query book list:" + newList.toString());
                bookManager.registerListener(mOnNewBookArrivedListener);  
            }catch(RemoteException e){
                e.printStackTrace();
            }
        }
        
        public void onServiceDisconnected(ComponentName className){
            mRemoteBookManager = null;
            Log.e(TAG, "binder died");
        }
    };
    
    private IOnNewBookArrivedListener mOnNewBookArrivedListener = new IOnNewBookArrivedListener.Stub(){
        @Override
        public void onNewBookArrived(Book newBook)throws RemoteException{
            mHandler.obtainMessage(MESSAGE_NEW_BOOK_ARRIVED, newBook).sendToTarget();
        }
    }
    
    @Override
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this,BookManagerService.class);
        bindService(intent,mConn,Context.BIND_AUTO_CREATE);
    }
    
    @Override
    protected void onDestroy(){
        if(mRemoteBookManager != null &&
          mRemoteBookManager.asBinder().isBinderAlive()){
            try{
                Log.i(TAG, "unregister listener:" + mOnNewBookArrivedListener);
                mRemoteBookManager.unregisterListener(mOnNewBookArrivedListener);
            }catch(RemoteException e){
                e.printStackTrace();
            }
        }
        unbindService(mConn);
        super.onDestroy();
    }}

按以上代码的逻辑,监听器在解注册时服务器无法找到之前注册的监听器。因为通过Binder传递到服务端后产生两个全新的对象,而对象是不能跨进程传输的。可以使用RemoteCallbackList解决这个问题。

以下是示例代码,修改BookManagerService

public class BookManagerServicee extends Service{
    
    private static final String TAG = "BMS";
    
    private AtomicBoolean mIsServiceDestroyed = new AtomicBoolean(false);
    
    // CopyOnWriteArrayList支持并发读写
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
    
    // 创建RemoteCallbackList代替CopyOnWriteArrayList
    private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList = new RemoteCallbackList<IOnNewBookArrivedListener>();
    
    private Binder mBinder = new IBinderManager.Stub(){
        
        @Override
        public List<Book> getBookList() throws RemoteException{
            return mBookList;
        }
        
        @Override
        public void addBook(Book book)throws RemoteException{
            mBookList.add(book);
        }
        
        /*
        修改register和unregister这两个接口的实现*/
        @Override
        public void registerListener(IOnNewBookArrivedListener listener)throws RemoteException{
            mListenerList.register(listener);
       
        }
        
        @Override
        public void unregisterListener(IOnNewBookArrivedListener listener)throws RemoteExceotion{
            mListenerList.unregister(listener);
        }
    };
    
    @Override
    public void onCreate(){
        super.onCreate();
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "Ios"));
        new Thread(new ServiceWorker()).start();
    }
    
    @Override
    public IBinder onBind(Intent intent){
        return mBinder;
    }
    
    @Override
    public void onDestroy(){
        mIsServiceDestroyed.set(true);
        super.onDestroy();
    }
    
    // 修改OnNewBookArrived
    private void onNewBookArrived(Book book)throws RemoteException{
        mBookList.add(book);
        final int N = mListenerList.beginBroadcast();
        for(int i = 0; i < N; i++){
            IOnNewBookArrivedListener listener = mListenerList.getBroadcastItem(i);
            if(listener != null){
                try{
                    listener.onNewBookArrived(book);
                }catch(RemoteException e){
                    e.printStackTrace();
                }
            }
        }
        mListenerList.finishBroadcast();
    }
    
    private class ServiceWorker implements Runnable{
        @Override
        public void run(){
            while(!mIsServiceDestroyed.get()){
                try{
                    Thread.sleep(5000);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
                int bookId = mBookList.size() + 1;
                Book newBook = new Book(bookId, "new book#" + bookId);
                try{
                    onNewBookArrived(newBook);
                }catch(RemoteException e){
                    e.printStackTrace();
                }
            }
        }
    }}

Binder意外死亡
可以给Binder设置DeathRecipient监听,当Binder死亡时,收到binderDied方法回调,在binderDied中重连远程服务。binderDied在客户端Binder线程池被回调,所以在binderDied中不能访问UI

在onServiceDisconnected中重连远程服务,onServiceDisconnected在客户端UI线程被回调。

AIDL使用权限验证

第一种方式:在onBind中验证

给服务加入权限验证功能,权限验证失败则无法调用服务中的方法。

在AndroidManifest中声明所需权限

<use-permission
	android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE"
	android:protectionLevel="normal" />

在BookManagerService的onBind方法中做权限验证

public IBinder onBind(Intent intent){
    int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
    if(check == PackageManager.PERMISSION_DENIED){
        return null;
    }
    return mBinder;}

第二种方式在服务端onTransact方法进行权限验证,示例代码如下:

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)throws RemoteException{
    int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
    if(check == PackageManager.PERMISSION_DENIED){
        return false;
    }
    
    String packageName = null;
    String[] packages = getPackageManager().getPackagesForUid(getCallingUid());
    if(packages != null){
        packageName = packages[0];
    }
    if(!packageName.startsWith("com.ryg")){
        return false;
    }
    return super.onTranscat(code, data, reply, flags);}

ContentProvider

ContentProvider支持文件数据,比如图片,视频等。处理这类数据时可以在ContentProvider中返回文件句柄给外界从而让文件来访问ContentProvider中的文件信息。Android提供MediaStore功能就是文件类型的ContentProvider

Socket

使用Socket通信需要在AndroidManifest中声明以下权限:

<uses-permission android:name="android.permission.INTERNET" /><uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

其次是不能在主线程中访问网络,因为网络操作很可能是耗时的,从而抛出异常(android.os.NetworkOnMainThreadException)。

聊天室实现
在远程Service建立一个TCP服务,在主界面中连接TCP服务,连接上之后服务端与多个客户端响应。

服务端的代码实现:
通过判断服务端输入流的返回值,当客户端断开连接后,服务端的输入流会返回null,这时就知道客户端已经退出。

public class TCPServerService extends Service{
    private boolean mIsServiceDestroyed = false;
    private String[] mDefinedMessages = new String[]{
        "你好啊,哈哈哈",
    	"请问你叫什么名字?",
    	"今天深圳的天气不错",
    	"你知道吗?我可是可以和多个人同时聊天的哦",
    	"给你讲个笑话:据说爱笑的人运气不会太差,不知真假"};
    
    @Override
    public void onCreate(){
        new Thread(new TcpServer()).start();
        super.onCreate();
    }
    
    @Override
    public IBinder onBind(Intent intent){
        return null;
    }
    
    @Override
    public void onDestroy(){
        mIsServiceDestroyed = true;
        super.Destroy();
    }
    
    private class TcpServer implements Runnable{
        @SuppressWarnings("resource")
        @Override
        public void run(){
            ServerSocket serverSocket = null;
            try{
                serverSocket = new ServerSocket(8688);
            }catch(IOException e){
                e.printStackTrace();
                return;
            }
            
            while(!mIsServiceDestroyed){
                try{
                    final Socket client = mClientSocket.accept();
                    System.out,println("accept");
                    new Thread(){
                        @Override
                        public void run(){
                            try{
                                responseClient(client);
                            }catch(IOException e){
                                e.printStackTrace();
                            }
                        };
                    }.start();
                }catch(IOException e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    private void responseClient(Socket client)throws IOException{
        BufferedReader in = new BufferedReader(new InputStreamReader(
            client.getInputStream()));
        PrintWriter out = new PrintWriter(new BufferedWriter(
            new OutputStreamWriter(client.getOutputStream())),true);
        out.println("欢迎来到聊天室");
        while(!mIsServiceDestroyed){
            String str = in.readLine();
            System.out.println("msg from client:" + str);
            if(str == null){
                break;
            }
            int i = new Random().nextInt(mDefinedMessage.length);
            String msg = mDefinedMessage[i];
            out.println("send : " + msg);
        }
        System.out.println("client quit.");
        out.close();
        in.close();
        client.close();
    }}

客户端示例代码如下:

public class TCPClientActivity extends Activity implements onClickListener{
    
    private static final int MESSAGE_RECEIVE_NEW_MSG = 1;
    private static final int MESSAGE_SOCKET_CONNECTED = 2;
    
    private Button mSendButton;
    private TextView mMessageTextView;
    private EditText mMessageEditText;
    
    private PrintWriter mPrintWriter;
    private Socket mClientSocket;
    
    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg){
            switch(msg.what){
                case MEESAGE_RECEIVE_NEW_MSG:
                    mMessageTextView.setText(
                        mMessageTextView.getText() + (String) msg.obj);
                    break;
                case MESSAGE_SOCKET_CONNECTED:
                    mSendButton.setEnable(true);
                    break;
                default:
                    break;
            }
        }
    };
    
    @Override 
    protected void onCreate(Bundle savedInstanceState){
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tcpclient);
        mMessageTextView = (TextView)findViewById(R.id.msg_container);
        mSendButton = (Button)findViewById(R.id.send);
        mMessageEditText = (EditText)findViewById(R.id.msg);
        Intent service = new Intent(this, TCPServerService.class);
        startService(service);
        new Thread(){
            @Override
            public void run(){
                connectTCPServer();
            }
        }.start();
    }
    
    @SuppressLint("SimpleDataFormat")
    private String formatDataTime(long time){
        return new SimpleDataFormat("(HH:mm:ss)").
            format(new Data(time));
    }
    
    @Override
    public void onClick(View v){
        if(v == mSendButton){
            final String msg = mMessageEditText.getText().toString();
            if(!TextUtils.isEmpty(msg) && mPrintWriter != null){
                mPrintWriter.println(msg);
                mMessageEditText.setText("");
                String time = formatDataTime(System.currentTimeMillis());
                final String showedMsg = "self" + time + ":" + msg + "\n";
                mMessageTextView.setText(mMessageTextView.getText() + showedMsg);
            }
        }
    }
    
    private void connectTCPServer(){
        /**
        采用超时重连的策略,每次连接失败后都会重新建立连接,为降低重试机制的开销,加入休眠机制,每次重试的时间间隔为1000ms
        */
        Socket socket = null;
            while(socket == null){
                try{
                    socket = new Socket("localhost",8688);
                    mClientSocket = socket;
                    mPrintWriter = new PrintWriter(new BufferedWriter(
                    new OutputStreamWriter(socket.getOutputStream())),true);
                    mHandler.sendEmptyMessage(MEESGAE_SOCKET_CONNECTED);
                    System.out.println("connect server success");
                }catch(IOException e){
                    SystemClock.sleep(1000);
                    System.out.println("connect tcp server failed,retry....");
                }
            }
        try{
            // 接收服务端消息
			BufferedReader read = new BufferedReader(new InputStreamReader(
						socket.getInputStream()));
			while(!TCPClientActivity.this.isFinishing()){
    			String msg = read.readLine();
    			System.out.println("receive : " + msg);
    			if(msg != null){
        			String time = formatDataTime(System.currentTimeMillis());
        			final String showedMsg = "server " + time + ":" + msg + "\n";
                    mHandler.obtainMessage(MESSAGE_RECEIVE_NEW_MSG,showedMsg).
                        sendToTarget();
    			}
			}
            System.out,println("quit...");
            mPrintWriter.close;
            read.close();
            socket.close();
        }catch(IOException e){
            e.printStackTrace();
        }
    }

	// 当Activity退出时,关闭当前的Socket
	@Override
	protected void onDestroy(){
    	if(mClientSocket != null){
        	try{
            	mClientSocket.shutdownInput();
            	mClientSocket.close();
        	}catch(IOException e){
            	e.printStackTrace();
        	}
    	}
    	super.onDestroy();
	}}

Binder连接池

Binder连接池的工作原理
每个业务模块创建自己的AIDL接口并实现此接口,向服务端提供自己唯一标识和其对应的Binder对象;对服务端来说,只需要建一个Service,提供一个queryBinder接口,这个接口能够根据业务模块的特征返回相应的Binder对象给他们,不同业务模块拿到所需的Binder对象之后就可以进行远程方法调用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JyCKBRxF-1581561867032)(…/…/pic/image-20191115120658369.png)]

比如有以下两个AIDL接口的实现:

// SecurityCenterImplpublic class SecurityCenterImpl extends ISecurityCenter.Stub{
    
    private static final char SECRET_CODE = '^';
    
    @Override
    public String encrypt(String content)throws RemoteException{
        char[] chars = content.toCharArray();
        for(int i = 0;i < chars.length; i++){
            chars[i] ^= SECRET_CODE;
        }
        return new String(chars);
    }
    
    @Override
    public String decrypt(String password)throws RemoteException{
        return encrypt(password);
    }}// ComputeImplpublic class ComputeImpl extends ICompute.Stub{
    @Override
    public int add(int a, int b)throws RemoteException{
        return a + b;
    }}

为Binder连接池创建AIDL接口IBinderPool.aidl

interface IBinderPool{
    IBinder queryBinder(int binderCode);}

远程Service的实现

public class BinderPoolService extends Service{
    
    private static final String TAG = "BinderPoolService";
    
    private Binder mBinderPool = new BinderPool.BinderPoolImpl();
    
    @Override
    public void onCreate(){
        super.onCreate();
    }
    
    @Override
    public IBinder onBinder(Intent intent){
        Log.d(TAG,"onBind");
        return mBinderPool;
    }
    
    @Override
    public void onDestroy(){
        super.onDestroy();
    }}

Binder连接池的具体实现示例代码如下:

public class BinderPool{
    private static final String TAG = "BinderPool";
    public static final int BINDER_NONE = -1;
    public static final int BINDER_COMPUTE = 0;
    public static final int BINDER_SECURITY_CENTER = 1;
    
    private Context mContext;
    private IBinderPool mBinderPool;
    private static volatile BinderPool sInstance;
    private CountDownLatch mConnectBinderPoolCountDownLatch;
    
    private BinderPool(Context context){
        mContext = context.getApplicationContext();
        connectBinderPoolService();
    }
    
    public static BinderPool getInstance(Context context){
        if(sInstance == null){
            synchronized(BinderPool.class){
                if(sInstance == null){
                    sInstance = new BinderPool(context);
                }
            }
        }
        return sInstance;
    }
    
    private synchronized void connectBinderPoolService(){
        mConnectBinderPoolCountDownLatch = new CountDownLatch(1);
        Intent service = new Intent(mConext, BinderPoolService.class);
        mContext.bindService(service, mBinderPoolConnection, Context.BIND_AUTO_CREATE);
        try{
            mConnectBinderPoolCountDownLatch.await();
        }catch(InterruptedException e){
            e.printStackTrace();
        }
    }
    
    public IBinder queryBinder(int binderCode){
        IBinder binder = null;
        try{
            if(mBinderPool != null){
                binder = mBinderPool.queryBinder(binderCode);
            }
        }catch*(RemoteException e){
            e.printStackTrace();
        }
        return binder;
    }
    
    private ServiceConnection mBinderPoolConnection = new ServiceConnection(){
        @Override
        public void onServiceDisconnected(ComponentName name){
            
        }
        
        @Override
        public void onServiceConnected(ComponentName name,IBinder service){
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try{
                mBinderPool.asBinder().
                    linkToDeath(mBinderPoolDeathRecipient, 0);
            }catch(RemoteException e){
                e.printStackTrace();
            }
            mConnectBinderPoolCountDownLatch.countDown();
        }
    };
    
    private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient(){
        @Override
        public void binderDied(){
            Log.w(TAG,"binder died");
            mBinderPool.asBinder().unlinkToDeath(mBinderPoolDeathRecipient,0);
            mBinderPool = null;
            connectBinderPoolService();
        }
    };
   
    public static class BinderPoolImpl extends IBinderPool.Stub{
        
        public BinderPoolImpl(){
            super();
        }
        
        @Override
        public IBinder queryBinder(int binderCode)throws RemoteException{
            IBinder binder = null;
            switch(binderCode){
                case BINDER_SECURITY_CENTER:
                    binder = new SecurityCenterImpl();
                    break;
                case BINDER_COMPUTE:
                    binder = new ComputeImpl();
                    break;
                default:
                    break;
                    return binder;
            }
        }
    }}

在Activity中的线程中完成多个AIDL接口的工作。示例代码如下:

private void doWork(){
    BinderPool binderPool = BinderPool.getInstance(BinderPoolActivity.this);
    IBinder securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER);
    mSecurityCenter = (ISecurityCenter)SecurityCenterImpl.asInterface(securityBinder);
    Log.d(TAG,"visit ISecurityCenter");
    String msg = "hello world - android";
    System.out.println("content:" + msg);
    try{
        String password = mSecurityCenter.encrypt(msg);
        System.out.println("encrypt:"+password);
        System.out.println("decrypt:"+mSecurityCenter.decrypt(password));
    }catch(RemoteException e){
        e.printStackTrace();
    }
    
    Log.d(TAG,"visit ICompute");
    IBinder computeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE);
    mCompute = ComputeImpl.asInterface(computeBinder);
    try{
        System.out.println("3+5="+mCompute.add(3,5));
    }catch(RemoteException e){
        e.printStackTrace();
    }
       }

注意:在线程中执行的原因是CountDownLatch将bindService异步操作转为同步操作,这就意味着这可能是耗时的,则不建议放在主线程执行。

各个IPC的比较

名称优点缺点适用场景
Bundle简单易用只能传输Bundle支持的数据类型四大组件间进程通信
文件共享简单易用不适合高并发场景,无法做到进程间的即时通信无并发访问情形,交换简单数据实时性不高
AIDL功能强大,支持一对多并发通信,支持实时通信使用复杂,需要处理好线程同步一对多通信且有RPC需求
Messenger功能一般,支持一对多串行通信,支持实时通信不能很好处理高并发的情形,不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的数据类型低并发的一对多即时通信,无RPC需求
ContentProvider在数据访问的方面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作受约束的AIDL,主要提供数据源的CRUD操作一对多的进程间数据共享
Socket功能强大,可以通过网络传输字节流,支持一对多并发实时通信实现细节繁琐,不支持直接的RPC网络数据交换

多线程编程

一个Android应用在创建时会开启一个线程,即是主线程或UI线程。如果要访问数据库或者网络等耗时的操作都会开启子线程处理。

线程基础

进程与线程

什么是进程
进程是操作系统结构的基础,是程序在一个数据集合上运行的过程,是系统进行资源分配和调度的基本单位。进程可以被看作程序的实体,同样,它也是线程的容器。

什么是线程
如QQ浏览器的进程,它里面运行了很多子任务,这些 子任务有的加载网页,有的处理缓存,有的进行下载,这些子任务就是线程。

线程是操作系统调度的最小单元,也叫作轻量级进程。在一个进程中可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。

为什么使用多线程?

  • 使用多线程可以减少程序的响应时间。如果某个操作很耗时,或者陷入长时间的等待,使用多线程后可以把这个耗时的线程分配到一个单独的线程中去执行。
  • 与进程相比,线程创建和切换开销更小,同时多线程在数据共享方面效率非常高。
  • 如果使用单个进程,将无法重复利用计算机资源,这会造成资源的巨大浪费。
  • 使用多线程能简化程序的结构,使程序便于理解和维护。

线程状态

New:新创建状态。线程被创建,还没有调用 start 方法,在线程运行之前还有一些基础工作要做。

Runnable:可运行状态。一旦调用start方法,线程就处于Runnable状态。一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间。

Blocked:阻塞状态。表示线程被锁阻塞,它暂时不活动。

Waiting:等待状态。线程暂时不活动,并且不运行任何代码,这消耗最少的资源,直到线程调度器重新激活它。

Timed waiting:超时等待状态。和等待状态不同的是,它是可以在指定的时间自行返回的。

Terminated:终止状态。表示当前线程已经执行完毕。导致线程终止有两种情况:第一种就是run方法执行完毕正常退出;第二种就是因为一个没有捕获的异常而终止了run方法,导致线程进入终止状态。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-100OT154-1581561867034)(…/…/pic/image-20191115181503943.png)]

创建线程

一个类应该在加强或者修改的时候才会被继承,因此如果没有必要写Thread类的其他方法,在这种情况下最好用实现Runnable接口的方式。

继承Thread类,重写run()方法
Thread本质上实现Runnable接口,调用start()并不是立即执行多线程代码而是使该线程变为可运行态,多线程代码的运行由操作系统决定。以下是创建的主要步骤:

  • 定义Thread类的子类,重写该类的run()方法,该方法体代表线程要完成的任务
  • 创建Thread子类的实例
  • 调用线程对象的start()方法来启动该线程
public class TestThread extends Thread{
    
    public void run(){
        System.out.println("Hello World");
    }
    
    public static void main(String[] args){
        Thread mThread = new TestThread();
        mThread.start();
    }}

实现Runnable接口,并实现接口的run()
以下是主要步骤:

  • 自定义并实现Runnable接口,实现run()
  • 创建Thread子类的实例,实现Runnable接口的对象作为参数实例化Thread对象
  • 调用Thread的start()方法启动该线程
public class TestRunnable implements Runnable{
    
    public void run(){
        System.out.println("Hello World");
    }
    
    public class TestRunnable{
        public static void main(String[] args){
            TestRunnable mTestRunnable = new TestRunnable();
            Tread mTread = new Thread(mTestRunnable);
            mThread.start();
        }
    }}

实现Callable接口,重写call()
Callable接口属于Executor框架中的功能类,Callable接口提供以下功能:

  • Callable可以在任务接受后提供一个返回值;
  • Callable中的call()可以抛出异常;
  • 运行Callable可以拿到一个Future对象,表示异步计算结果,提供检查计算是否完成的方法。调用Future的get()获取结果时,当前线程会阻塞直到call()返回结果。
public class TestCallable{
    
    public static class MyTestCallable implements Callable{
        
        public String call() throws Exception{
            return "Hello World";
        }
    }
    
    public static void main(String[] args){
        MyTestCallable mMyTestCallable = new MyTestCallable();
        ExecutorService mExecutorService = Executors.newSingleThreadPool();
        Future mfuture = mExecutorService.submit(mMyTestCallable);
        try{
            System.out.println(mfuture.get());
        }catch(Exception e){
            e.printStackTrace();
        }
    }}

中断

如何中断线程
一个线程调用interrupt方法,线程的中断标识位被置为true,线程会不时检测这个中断标识位以判断线程是否应该被中断。可以调用Thread.currentTread().isInterrupted()来判断线程是否中断。

线程阻塞时中断
一个线程备阻塞则无法检测中断,此时检查中断标识位为true,则在阻塞方法中抛出InterruptedException异常,将中断标识位复位。

中断线程的含义
被中断的线程不一定被终止,中断线程只是为了引起线程的注意,线程决定如何响应中断。大部分情况线程将中断作为一个终止的请求。

抛出InterruptedException异常后的处理
(1) 在catch中调用Thread.currentThread.interrupt()设置中断状态,让外界通过判断Thread.currentThread().isInterrupted()来决定是否终止线程继续

void myTask(){
    ...
        try{
            sleep(50)
        }catch(InterruptedException e){
            Thread.currentThread().interrupted();
        }
    ...}

(2) 不使用try来捕获这样的异常,让方法直接抛出,调用者来捕获此异常

void myTask()throws InterruptedException{
    sleep(50)}

安全终止线程

中断终止线程

public class StopThread{
    
    public static void main(String[] args) throws InterruptedException{
        MoonRunner runnable = new MoonRunner();
        Thread thread = new Thread(runnable,"MoonThread");
        thread.start();
        TimeUnit.MILLISECONDS.sleep(10); // 使main线程睡眠10ms,留给MoonThread线程时间感知中断从而结束。
        thread.interrupt();
    }
    
    public static class MoonRunner implements Runnable{
        
        private long i;
        
        @Override
        public void run(){
            while(!Thread.currentThread().isInterrupted()){
                i++;
                System.out.println("i = " + i);
            }
            System.out.println("stop");
        }
    }}

采用boolean变量控制是否需要停止线程

public class StopThread{
    
    public static void main(String[] args) throws InterruptedException{
        MoonRunner runnable = new MonnRunner();
        Thread thread = new Thread(runnable, "MoonThread");
        thread.start();
        TimeUnit.MILISECONDS.sleep(10);
        runnabel.cancel();
    }
    
    public static class MonnRunner implements Runnable{
        private long i;
        private volatile boolean on = true;
        
        @Override
        public void run(){
            while(on){
                i++;
                System.out.println(" i = "+i);
            }
            System.out.println("stop");
        }
        
        public void cancel(){
            on = false;
        }
    }}

注意:使用volatile标注的变量,当有其他线程改变此变量的值时所有线程都会感知到它的变化。

同步

同步应用场景
在多线程应用中,两个或者多个线程需要共享对同一个数据的存取时,当一个线程需要使用这个数据就给它一把锁,等它把事情做完后再把锁给另一个要用此数据的线程。

重入锁与条件对象


synchronized关键字自动提供锁以及相关条件。
ReetrantLock支持重入锁,表示该锁能够支持一个线程对资源的重复加锁。确保任何时刻只有一个线程进入临界区。

条件对象
进入临界区后,发现某一条件满足之后线程才能执行。这时可以用一个条件对象来管理已经获得一个锁但是不能做有用工作的线程。

为何需要条件对象?
假设一个场景需要用支付宝转账,构造法的参数是支付宝账户数量和每个账户的账户金额。转账的方法,from是转帐方,to是接收方,amount是转账金额。
发现转帐方的余额不足,如果有其他线程给转帐方再转足够的钱就可转账成功。但这个线程获取锁具有排它性,别的线程无法获取锁进行存款操作,从而引入条件对象。

条件对象的应用
一个锁对象可以拥有多个条件对象,用newCondition获取一个条件对象,得到条件对象之后调用await方法,当前线程就被阻塞并放弃锁。
一旦一个线程调用await,进入该条件的等待集并处于阻塞状态,直到另一个线程调用同一个条件的sigalAll()为止。
调用signalAll方法时并不是立即激活一个等待线程,仅仅解除等待线程的阻塞,以便线程能够在当前线程退出同步方法后,通过竞争实现对对象的访问。
signal方法随机解除某个线程的阻塞,如果该线程任不能运行则再次被阻塞,如果没有其他线程再次调用signal,则系统死锁。

public class Alipay{
    
    private Lock alipaylock;
    
    private Condition condition;
    
    public Alipay(int n, double money){
        accounts = new double[n];
        alipaylock = new ReetrantLock();
        condition = alipaylock.newCondition(); // 得到条件对象
        for (int i = 0; i<accounts.length; i++){
            accounts[i] = money;
        }
    }
    
    public void transfer(int from, int to, int amount)throws InterruptedException{
        alipaylock.lock();
        try{
            while(accounts[from] < amount){
                condition.await(); // 阻塞当前线程,并放弃锁
            }
            /*转账操作*/
            accounts[from] = accounts[from]-amount;
            accounts[to] = accounts[to] + amount;
            condition.signalAll(); // 重新激活因为这一条件而等待的所有线程
        }finally{
            alipaylock.unlock();
        }
    }}

同步方法

如果一个方法用synchronized关键字声明,则对象锁将保护整个方法。此时要调用该方法,线程就必须获得内部的对象锁。内部对象锁只有一个相关条件。

public synchronized void method(){
	...}

上面的例子中,可以将Alipay类的transfer方法声明为synchronized,wait方法将一个线程添加到等待集中,notifyAll或notify方法解除等待线程的阻塞状态。wait相当于condition.await(),notifyAll等价于condition.signalAll()。
 以上的transfer方法也可以这样写:

public synchronized void transfer(int from, int to, int amount)throws InterruptedException{
    while(accounts[from] < amount){
        wait();
    }
    accounts[from] = accounts[from] - amount;
    accounts[to] = accounts[to] + amount;
    notifyAll();}

同步代码块

同步代码块是非常脆弱的,通常不推荐使用,一般实现同步最好用java.util.concurrent包下提供的类,比如阻塞队列。尽量使用同步方法。

public class Alipay{
    
    private double[] accounts;
    
    private Object lock = new Object();
    
    public Alipay(int n, double money){
        accounts = new double[n];
        for(int i = 0; i < accounts.length; i++){
            accounts[i] = money;
        }
    }
    
    public void transfer(int from,int to,int amount){
        synchronized(lock){
            accounts[from] = accounts[from] - amount;
            accounts[to] = accounts[to] + amount;
        }
    }}

volatile

volatile关键字为实例域的同步访问提供免锁机制。当一个共享变量被volatile修饰,有两个含义:对其他线程立刻可见;禁止使用指令排序。

volatile无法保证原子性,保证有序性。

java内存模型
java的内存模型控制线程间的通信,决定一个线程对主存共享变量的写入何时对另一个线程可见。

内存可见性问题:java中的堆内存用来存储对象实例,堆内存是被所有线程共享的运行时内存区域,则存在内存可见性问题。

本地内存:本地内存是一个抽象概念,涵盖缓存、写缓冲区、寄存器等区域。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U29rhBPG-1581561867036)(…/…/pic/image-20191118135116294.png)]

原子性
对基本数据类型变量的读取和赋值操作是原子性操作,这些操作是不可被中断的,即要么执行完毕要么就不执行。

可见性
一个线程修改的结果会被立刻更新到主存,则对于其他线程是可见的,可以从主存中读取该结果。

有序性
java内存模型允许编译器和处理器对指令进行重排序,重排序不会影响到单线程执行的正确性,但会影响到多线程并发执行的正确性。除了volatile,还有synchronized和Lock可以保证每个时刻只有一个线程执行同步代码,从而保证有序性。
重排序通常是编译器或者运行时环境为了优化程序性能而采取对指令重新排序的手段。重排序可以分为:编译期重排序和运行期重排序,分别对应编译时环境和运行时环境。

volatile的应用具备条件

  • 对变量的写操作不会依赖当前值
    • 即是不能自增、自减
  • 该变量没有包含在具有其他变量的不变式中
    • 不变式如:下界总是小于等于上界。
      以下的方式将lower和upper字段定义为volatile类型不能够充分实现类的线程安全。如果当两个线程在同一时间使用不一致的值执行setLower和setUpper的话,则会使范围处于不一致的状态。
      例如,如果初始状态是(0,5),在同一时间内,线程A调用setLower(4)并且线程B调用setUpper(3),虽然这两个操作交叉存入的值是不符合条件的,但是这两个线程都会通过用于保护不变式的检查,使得最后的范围值是(4,3)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6U34G3bA-1581561867037)(…/…/pic/image-20191118143634672.png)]

volatile应用场景
(1)标志位状态

volatile boolean shutdownRequested;...public void shutdown(){
    shutdownRequested = true;}public void doWork(){
    while(!shutdownRequested){
        ...
    }}

(2)双重检查模式DCL
DCL优点是资源利用率高,第一次执行getInstance方法时单例对象才被实例化,效率高。缺点是第一次加载反应较慢,在高并发环境下有一定缺陷。

public class Singleton{
    
    private volatile static Singleton instance = null;
    public static Singleton getInstance(){
        if(instance == null){
            synchronized(this){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }}

阻塞队列

阻塞队列基础

阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。

**常见的阻塞场景: **
(1)当队列中没有数据的情况下,消费者端的所有线程都会被自动阻塞(挂起),直到有数据放入队列。
(2)当队列中填满数据的情况下,生产者端的所有线程都会被自动阻塞(挂起),直到队列中有空的位置,线程被自动唤醒。

BlockingQueue的核心方法

(1)放入数据:

  • offer(an Object):表示如果可能的话,将an Object加到BlockingQueue里。即如果BlockingQueue可以容纳,则返回true,否则返回false。本方法不阻塞当前执行方法的线程。

  • offer(E o,long timeout,TimeUnit unit):可以设定等待的时间。如果在指定的时间内还不能往队列中加BlockingQueue,则返回失败。

  • put(an Object):将an Object加到BlockingQueue里。如果BlockQueue没有空间,则调用此方法的线程被阻断,直到BlockingQueue里面有空间再继续。

(2)获取数据:

  • poll(time):取走 BlockingQueue 里排在首位的对象。若不能立即取出,则可以等 time参数规定的时间。取不到时返回null。

  • poll(long timeout,TimeUnit unit):从BlockingQueue中取出一个队首的对象。如果在指定时间内,队列一旦有数据可取,则立即返回队列中的数据;否则直到时间超时还没有数据可取,返回失败。

  • take():取走BlockingQueue里排在首位的对象。若BlockingQueue为空,则阻断进入等待状态,直到 BlockingQueue有新的数据被加入。

  • drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数)。通过该方法,可以提升获取数据的效率;无须多次分批加锁或释放锁。

Java中的阻塞队列

ArrayBlockingQueue

由数组结构组成的有界阻塞队列,并按照先进先出(FIFO)的原则对元素进行排序。默认情况下不保证线程公平地访问队列。

公平访问队列就是指阻塞的所有生产者线程或消费者线程,当队列可用时,可以按照阻塞的先后顺序访问队列。通常情况下为了保证公平性会降低吞吐量。

  • 即先阻塞的生产者线程,可以先往队列里插入元素;
  • 先阻塞的消费者线程,可以先从队列里获取元素。
// 创建公平的阻塞队列ArrayBlockingQueue fairQueue=new ArrayBlockingQueue(2000,true);

LinkedBlockingQueue

由链表结构组成的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序,其内部也维持着一个数据缓冲队列(该队列由一个链表构成)。

数据缓冲
当生产者往队列中放入一个数据时,队列会从生产者手中获取数据,并缓存在队列内部,而生产者立即返回;
只有当队列缓冲区达到缓存容量的最大值时,可以通过构造方法指定该值,才会阻塞生产者队列,直到消费者从队列中消费掉一份数据,生产者线程会被唤醒。反之,对于消费者这端的处理也基于同样的原理。

数据同步
LinkedBlockingQueue之所以能够高效地处理并发数据,还因为其对于生产者端和消费者端分别采用了独立的锁来控制数据同步。这也意味着在高并发的情况下生产者和消费者可以并行地操作队列中的数据

LinkedBlockingQueue的容量指定
如果构造一个LinkedBlockingQueue对象,而没有指定其容量大小,LinkedBlockingQueue会默认一个类似无限大小的容量(Integer.MAX_VALUE)。这样一来,如果生产者的速度一旦大于消费者的速度,也许还没有等到队列满阻塞产生,系统内存就有可能已被消耗殆尽了。

PriorityBlockingQueue

它是一个支持优先级的无界队列。默认情况下元素采取自然顺序升序排列。

这里可以自定义实compareTo()方法来指定元素进行排序规则;或者初始化PriorityBlockingQueue时,指定构造参数 Comparator来对元素进行排序。但其不能保证同优先级元素的顺序。

DelayQueue

它是一个支持延时获取元素的无界阻塞队列。队列使用PriorityQueue来实现。

队列中的元素必须实现Delayed 接口。创建元素时,可以指定元素到期的时间,只有在元素到期时才能从队列中取走。

SynchronousQueue

它是一个不存储元素的阻塞队列。

  • 每个插入操作必须等待另一个线程的移除操作;
  • 同样任何一个移除操作都等待另一个线程的插入操作。

因此此队列内部其实没有任何一个元素,或者说容量是0,严格来说它并不是一种容器。由于队列没有容量,因此不能调用peek操作。

LinkedTransferQueue

它是一个由链表结构组成的无界阻塞TransferQueue队列。LinkedTransferQueue实现了一个重要的接口TransferQueue。

该接口含有3个重要的方法:

(1)transfer(E e):

  • 若当前存在一个正在等待获取的消费者线程,则立刻将元素传递给消费者;
  • 如果没有消费者在等待接收数据,就会将元素插入到队列尾部,并且等待进入阻塞状态,直到有消费者线程取走该元素。

(2)tryTransfer(E e):

  • 若当前存在一个正在等待获取的消费者线程,则立刻将元素传递给消费者;

  • 若不存在,则返回 false,并且不进入队列,这是一个不阻塞的操作。

    与 transfer 方法不同的是,tryTransfer方法无论消费者是否接收,其都会立即返回;而transfer方法则是消费者接收了才返回。

(3)tryTransfer(E e,long timeout,TimeUnit unit):

  • 若当前存在一个正在等待获取的消费者线程,则立刻将元素传递给消费者;
  • 若不存在则将元素插入到队列尾部,并且等待消费者线程取走该元素。
    • 若在指定的超时时间内元素未被消费者线程获取,则返回false;
    • 若在指定的超时时间内其被消费者线程获取,则返回true。

LinkedBlockingDeque

它是一个由链表结构组成的双向阻塞队列。双向队列可以从队列的两端插入和移出元素。

生产者-消费者模式

非阻塞队列实现生产者-消费者模式

首先使用Object.wait()、Object.notify()和非阻塞队列实现生产者-消费者模式。

public class Test{
    
    private int queueSize = 10;
    
    private PriorityQueue<Integer> queue = new PriorityQueue<Integer>(queueSize);
    
    public static void main(String[] args){
        Test test = new Test();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();
        producer.start();
        consumer.start();
    }
    
    class Consumer extends Thread{
        
        @Override
        public void run(){
            while(true){
                synchronized(queue){
                    while(queue.size() == 0){
                        try{
                            System.out.println("队列空,等待数据");
                            queue.wait();
                        }catch(InterruptedException e){
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.poll();
                    queue.notify();
                }
            }
        }
    }
    
    class Producer extends Thread{
        @Override
        public void run(){
            while(true){
                synchronized(queue){
                    while(queue.size == queueSize){
                        try{
                            System.out.println("队列满,等待有余空间");
                            queue.wait();
                        }catch(InterruptedException e){
                            e.printStackTrace();
                            queue.notify();
                        }
                    }
                    queue.offer(1);
                    queue.notify();
                }
            }
        }
    }}

阻塞队列实现生产者-消费者模式

public class Test{
    
    private int queueSize = 10;
    
    private ArrayBlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(queueSize);
    
    public static void main(String[] args){
        Test test = new Test();
        Producer producer = test.new Producer();
        Consumer consumer = test.new Consumer();
        producer.start();
        consumer.start();
    }
    
    class Consumer extends Thread{
        @Override
        public void run(){
            while(true){
                try{
                    queue.take();
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
    
    class Producer extends Thread{
        @Override
        public void run(){
            while(true){
                try{
                    queue.put(1);
                }catch(InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }}

线程池

线程池应用场景
在编程中经常会使用线程来异步处理任务,但是每个线程的创建和销毁都需要一定的开销。这时就需要线程池来对线程进行管理。
在Java 1.5中提供了Executor框架用于把任务的提交和执行解耦,任务的提交交给Runnable或者Callable,而Executor框架用来处理任务。Executor框架中最核心的成员就是ThreadPoolExecutor。

ThreadPoolExecutor

以下是ThreadPoolExecutor的构造法以及各个参数含义:

public ThreadPoolExecutor(int corePoolSize,
                         int maxmumPoolSize,
                         long keepAliveTime,
                         TimeUnit unit,
                         BlockingQueue<Runnable> workQueue,
                         ThreadFactory threadFactory,
                         RejectedExecutionHandler handler){
    ....}

corePoolSize:核心线程数。默认情况下线程池是空的,只有任务提交时才会创建线程。

  • 如果当前运行的线程数少于corePoolSize,则创建新线程来处理任务;
  • 如果等于或者多于corePoolSize,则不再创建。
  • 如果调用线程池的prestartAllcoreThread方法,线程池会提前创建并启动所有的核心线程来等待任务。

maximumPoolSize:线程池允许创建的最大线程数。如果任务队列满了并且线程数小于maximumPoolSize时,则线程池仍旧会创建新的线程来处理任务。

keepAliveTime:非核心线程闲置的超时时间。

  • 超过这个时间则回收。如果任务很多,并且每个任务的执行事件很短,则可以调大keepAliveTime来提高线程的利用率。
  • 另外,如果设置allowCoreThreadTimeOut属性为true时,keepAliveTime也会应用到核心线程上。

TimeUnit:keepAliveTime参数的时间单位。可选的单位有天(DAYS)、小时(HOURS)、分钟(MINUTES)、秒(SECONDS)、毫秒(MILLISECONDS)等。

workQueue:任务队列。如果当前线程数大corePoolSize,则将任务添加到此任务队列中。该任务队列是BlockingQueue类型。

ThreadFactory:线程工厂。可以用线程工厂给每个创建出来的线程设置名字。一般情况下无须设置该参数。

RejectedExecutionHandler:饱和策略。这是当任务队列和线程池都满了时所采取的应对策略,默认是AbordPolicy,表示无法处理新任务,并抛出RejectedExecutionException异常。
此外还有3种策略,它们分别如下:

(1)CallerRunsPolicy:用调用者所在的线程来处理任务。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

(2)DiscardPolicy:不能执行的任务,并将该任务删除。

(3)DiscardOldestPolicy:丢弃队列最近的任务,并执行当前的任务。

线程池的处理流程和原理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DKIHqRjQ-1581561867039)(…/…/pic/image-20191119091302474.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ipH3TMtj-1581561867040)(…/…/pic/image-20191119091656208.png)]

(1)如果线程池中的线程数未达到核心线程数,则创建核心线程处理任务。
(2)如果线程数大于或者等于核心线程数,则将任务加入任务队列,线程池中的空闲线程会不断地从任务队列中取出任务进行处理。
(3)如果任务队列满了,并且线程数没有达到最大线程数,则创建非核心线程去处理任务。
(4)如果线程数超过了最大线程数,则执行饱和策略。

线程种类

FixedThreadPool

FixedThreadPool创建

public static ExecutorService newFixedThreadPool(int nThreads){
    return new ThreadPoolExecutor(nThread, nThread,
                                 0L, TimeUnit.MILISECONDS,
                                 new LinkedBlockingQueue<Runnable>());
    }

输入参数分析:

  • corePoolSize = nThreads
    maximumPoolSize = nThreads
    意味着FixedThreadPool只有核心线程,并且数量是固定的,没有非核心线程。
  • keepAliveTime = 0L
    意味着多余的线程会被立即终止。因为不会产生多余的线程,所以keepAliveTime是无效的参数。
  • 任务队列采用了无界的阻塞队列LinkedBlockingQueue。

FixedThreadPool的execute方法的执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3uBLAzXG-1581561867042)(…/…/pic/image-20191119092942174.png)]

当执行execute方法时,如果当前运行的线程未达到corePoolSize(核心线程数)时就创建核心线程来处理任务;如果达到了核心线程数则将任务添加到LinkedBlockingQueue中。
 FixedThreadPool就是一个有固定数量核心线程的线程池,并且这些核心线程不会被回收。
 当线程数超过corePoolSize 时,就将任务存储在任务队列中;当线程池有空闲线程时,则从任务队列中去取任务执行。

CachedThreadPool

CachedThreadPool 比较适于大量的需要立即处理并且耗时较少的任务。

CachedThreadPool创建

public static ExecutorService newCachedThreadPool(){
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                 60L, TimeUnit.SECONDS,
                                 new SynchronousQueue<Runnable>());
    }

输入参数分析:

  • corePoolSize = 0
    maximumPoolSize = Integer.MAX_VALUE
    这意味着CachedThreadPool没有核心线程,非核心线程是无界的。
  • keepAliveTime = 60L
    空闲线程等待新任务的最长时间为 60s。
  • 在此用了阻塞队列 SynchronousQueue,它是一个不存储元素的阻塞队列,每个插入操作必须等待另一个线程的移除操作,同样任何一个移除操作都等待另一个线程的插入操作。

CachedThreadPool的execute方法的执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-StkvQyXn-1581561867043)(…/…/pic/image-20191119094128577.png)]

当执行execute方法时,首先会执行SynchronousQueue的offer方法来提交任务,并且查询线程池中是否有空闲的线程执行SynchronousQueue的poll方法来移除任务。

  • 如果有则配对成功,将任务交给这个空闲的线程处理;
  • 如果没有则配对失败,创建新的线程去处理任务。

当线程池中的线程空闲时,它会执行SynchronousQueue的poll方法,等待SynchronousQueue中新提交的任务。

  • 如果超过 60s 没有新任务提交到SynchronousQueue,则这个空闲线程将终止。

因为maximumPoolSize 是无界的,所以如果提交的任务大于线程池中线程处理任务的速度就会不断地创建新线程。另外,每次提交任务都会立即有线程去处理。

ScheduledThreadPool

能实现定时和周期性任务的线程池

ScheduledThreadPool创建

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize){
    return new ScheduledThreadPoolExecutor(corePoolSize);}// ScheduledThreadPool的构造法public ScheduledThreadPoolExecutor(int corePoolSize){
    super(corePoolSize, Integer.MAX_VALUE,
         DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
         new DelayedWorkQueue());}

ScheduledThreadPoolExecutor的execute方法的执行

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-C5MlBEqB-1581561867047)(…/…/pic/image-20191119095126302.png)]

当执行 ScheduledThreadPoolExecutor 的 scheduleAtFixedRate 或者 scheduleWithFixedDelay方法时,会向DelayedWorkQueue 添加一个 实现 RunnableScheduledFuture 接口的ScheduledFutureTask(任务的包装类),并会检查运行的线程是否达到 corePoolSize。

  • 如果没有则新建线程并启动它,但并不是立即去执行任务,而 是去 DelayedWorkQueue 中取ScheduledFutureTask,然后去执行任务。
  • 如果运行的线程达到了corePoolSize 时,则将任务添加到DelayedWorkQueue中。

DelayedWorkQueue会将任务进行排序,先要执行的任务放在队列的前面。当执行完任务后,会将ScheduledFutureTask中的time变量改为下次要执行的时间并放回到DelayedWorkQueue中。

AsyncTask原理

AsyncTask封装Thread和Handler,可执行后台任务以及在主线程中访问UI,不适合进行特别耗时的任务。

AsyncTask构造法

WorkerRunnable<Params,Result>

  • call()->doBackground(),postResult()
  • WorkerRunnable实现了Callable接口,并实现了它的call方法,在call方法中调用了doInBackground(mParams)来处理任务并得到结果,并最终调用postResult将结果投递出去。

FutureTask<Result>(WorkerRunnable)

  • 的FutureTask是一个可管理的异步任务,它实现了Runnable和Futrue这两个接口。因此,它可以包装Runnable和Callable,并提供给Executor执行。
public AsyncTask(){
    
    mWorker = new WorkerRunnable<Params, Result>(){
        public Result call() throws Exception{
            mTaskInvoked.set(true); // mTaskInvocked设为true表示当前任务已经被调用过。
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            Result result = doInBackground(mParams);
            Binder.flushPendingCommands();
            return postResult(result);
        }
    };
    
    mFuture = new FutureTask<Result>(mWorker){
        @Override
        protected void done(){
            try{
                postResultIfNotInvocked(get());
            }catch(InterruptedException e){
                android.util.Log.w(LOG_TAG, e);
            }catch(ExecutionException e){
                throw new RuntimeException("An error occurred while exeuting doInBackground()", e.getCause());
            }catch(CancellationException e){
                postResultIfNotInvocked(null);
            }
        }
    };}

执行AsyncTask

执行调用流程
execute() -> executorOnExecutor() ->SerialExecutor -> FutureTask.run() -> WorkerRunnable.call() -> postResult()

AsyncTask任务取消
如果AsyncTask任务被取消了,则执行onCancelled方法,否则就调用onPostExecute方法。正是通过onPostExecute 方法,我们才能够得到异步任务执行后的结果。

线程串并处理
Android 3.0及以上版本用SerialExecutor作为默认的线程,它将任务串行地处理,保证一个时间段只有一个任务执行;而Android 3.0之前的版本是并行处理的。
如果想要在Android 3.0及以上版本使用并行的线程处理,可以使用如下的代码:
asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,"");

自定义AsyncTask
当然也可以传入4种线程池,比如传入 CachedThreadPool。

asyncTask.executeOnExecutor(Executors.newCachedThreadPool()," "); 

还可传入自定义线程池,实例代码如下:

Executor exec = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnablel>());asyncTask.executeOnExecutor(exec,"");

execute()
execute方法返回executeOnExecutor,executeOnExecutor中会调用exec的execute方法,并将mFuture也就是FutureTask传进去。exec是传进来的参数sDefaultExecutor,它是一个串行的线程池 SerialExecutor。

public final AsyncTask<Params, Progress, Result> execute(Params... params){
    return executeOnExecutor(sDefaultExecutor, params);}
public final AsyncTask<Params, Progress, Result>executeOnExecutor(Executor exec, Params.. params){
    if(mStatus != Status.PENDING){
        switch(mStatus){
            case RUNNING:
                throw new IllegalStateException("Cannot execute task:"
                                                + " the task is already running.");
                break;
            case FINISHED:
                throw new IllegalStateException("Cannot execute task:"
                                               + "the task has already been executed"
                                               + "a task can be executed only once");
                break;
        }
    }
    mStatus = State.RUNNING;
    onPreExecute();
    mWorker.mParams = params; // 处将AsyncTask 的参数传给WorkerRunnable
    exec.execute(mFuture);
    return this;}

SerialExecutor

  • execute():当调用SerialExecutor 的execute方法时,会将FutureTask加入到mTasks中。
  • scheduleNext():当任务执行完或者当前没有活动的任务时都会执行scheduleNext方法,它会从 mTasks 取出 FutureTask任务并交由 THREAD_POOL_EXECUTOR 处理。
  • run():从这里可以看出SerialExecutor是串行执行的。到执行了FutureTask的run方法,它最终会调用WorkerRunnable的call方法。call方法最终会调用AsycTask中的postResult方法将结果投递出去。
private static class SerialExecutor implements Executor{
    
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    
    Runnable mActive;
    
    public synchronized void execute(final Runnable r){
        mTask.offer(new Runnable(){
            public void run(){
                try{
                    r.run();
                }finally{
                    scheduleNext();
                }
            }
        });
        if(mActive == null){
            scheduleNext();
        }
    }
    protected synchronized void scheduleNext(){
        if((mActive = mTasks.poll()) != null){
            THREAD_POOL_EXECUTOR.execute(mActive);
        }
    }}
private Result postResult(Result result){
    @SuppressWarnings("unchecked")
    Message message = getHandler.obtainMessage(MESSAGE_POST_RESULT,
                                              new AsyncTaskResult<Result>(this,result));
    message.sendToTarget();
    return result;}

 // 通过getHandler方法得到Handler,并通过这个Handler发送消息private static Handler getHandler(){
    synchronized(AsyncTask.class){
        if(sHandler == null){
            sHandler = new InternalHandler();
        }
        return sHandler;
    }}// 在getHandler方法中创建了InternalHandlerprivate static class InternalHandler extends Handler{
    public InternalHandler(){
        super(Looper.getMainLooper());
    }
    
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg){
        AsyncTaskResult<?> result = (AsyncTaskResult<?>)msg.obj;
        switch(msg.what){
            case MESSAGE_POST_RESULT:
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }}// 在接收到MESSAGE_POST_RESULT消息后会调用AsyncTask的finish方法private void finish(Result result){
    if(isCancelled()){
        onCancelled(result);
    }else{
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;}

在SerialExecutor中调用 scheduleNext 方法时,将任务交给 THREAD_POOL_EXECUTOR。THREAD_POOL_EXECUTOR同样是一个线程池,用来处理任务。代码如下

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;private static final int KEEP_ALIVE_SECONDS = 30;private static final BlockingQueue<Runnable> sPoolWorkerQueue = new LinkedBlockingQueue<Runnable>(128);public static final Executor THREAD_POOL_EXECUTOR;static{
    ThreadPoolExecutor threadPoolExecutor = new threadPoolExecutor(
    CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS,
    TimeUnit.SECONDS, sPoolWorkerQueue, sThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;}

网络编程和网络框架

HttpClient

Android SDK中包含了HttpClient。Android 6.0版本直接删除了HttpClient类库。如果仍想使用它,解决方法就是在相应module下的build.gradle中加入如下代码:

android{
    userLibrary 'org.apache.http.legacy'
}

HttpClient的GET请求

使用DefaultHttpClient类实例化一个HttpClient

private HttpClient createHttpClient(){
    
    HttpParams mDefaultHttpParams = new BasicHttpParams();
    
    // 设置连接超时
    HttpConnectionParams.setConnectionTimeout(mDefaultHttpParams, 15000);
  
    // 设置请求超时
    HttpConnectionParams.setSoTimeout(mDefaultHttpParams, 15000);
    HttpConnectionParams.setTcpNoDelay(mDelayHttpParams, true);
    HttpProtocolParams.setVersion(mDefaultHttpParams, HttpVersion.HTTP_1_1);
    HttpProtocolParams.setContentCharset(mDefaultHttpParams, HTTP.UTF_8);
    
    // 持续握手
    HttpProtocolParams.setUseExceptContinue(mDefaultHttpParams, true);
    HttpClient mHttpClient = new DefaultHttpClient(mDefaultHttpParams);
    return mHttpClient;
    }

创建HttpGet和HttpClient,请求网络获取HttpResponse,并对HttpResponse处理。

private void useHttpClientGet(String url){
    HttpGet mHttpGet = new HttpGet(url);
    mHttpGet.addHeader("Connection", "Keep-Alive");
    try{
        HttpClient mHttpClient = createHttpClient();
        HttpResponse mHttpResponse = mHttpClient.execute(mHttpGet);
        HttpEntity mHttpEntity = mHttpResponse.getEntity();
        int code = mHttpResponse.getStatusLine().getStatusCode();
        if(null != mHttpEntity){
            InputStream mInputStream = mHttpEntity.getContent();
            String respose = convertStreamToString(mInputStream);
            Log.d(TAG, "请求状态码:" + code + "\n请求结果:\n" + repose);
            mInputStream.close();
               
        }
    }catch(IOException e){
        e.printStakcTrace();    }}private String convertStreamToString(InputStream is) throws IOException{
    BufferedReader reader = new BufferedReader(new InputStreamReader(is));
    StringBuffer sb = new StringBuffer();
    String line = null;
    while((line = reader.readLine()) != null){
        sb.append(line + "\n");
        
    }
    String repose = sb.toString();
    return repose;}

开启线程访问百度

new Thread(new Runnable(){
    @Override
    public void run(){
        useHttpClientGet("http://www.baidu.com");
    }}).start();

HttpClient的Post请求

private void useHttpClientPost(String url){
    HttpPost mHttpPost = new HttpPost(url);
    mHttpPost.addHeader("Connection", "Keep-Alive");
    try{
        HttpClient mHttpClient = createHttpClient();
        List<NameValuePair> postParams = new ArrayList<>();
        // 要传递的参数
        postParams.add(new BasicNameValuePair("ip", "59.108.54.37"));
        HttpResponse mHttpResponse = mHttpClient.execute(mHttpPost);
        HttpEntity mHttpEntity = mHttpResponse.getEntity();
        int code = mHttpResponse.getStatusLine().getStatusCode();
        if(null != mHttpEntity){
            InputStream mInputStream = mHttpEntity.getContent();
            String respose = convertStreamToString(mInputStream);
            Log.d(TAG, "请求状态码:" + code + "\n请求结果:\n" + respose);
            mInputStream.close();
        }
    }catch(IOException e){
        e.printStackTrace();
    }}

开启线程访问淘宝IP库

new Thread(new Runnable(){
    @Override
    public void run(){
        useHttpClientPost("http://ip.taobao.com/service/getIppInfo.php");
    }}).start();

HttpURLConnection

HttpURLConnection的优点

所以在Android 2.2版本及其之前的版本使用HttpClient是较好的选择,因为在Android 2.2版本及其之前,HttpURLConnection一直存在着一些令人厌烦的bug。比如说对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效。

而在Android 2.3版本及其之后,HttpURLConnection则是最佳的选择,它的 API 简单,体积较小,HttpURLConnection的压缩和缓存机制可以有效地减少网络访问的流量,在提升速度和省电方面也起到了较大的作用。

HttpURLConnection的Post请求

创建HttpURLConnection

public static HttpURLConnection getHttpURLConnection(String url){
    try{
        URL mUrl = new URL(url);
        mHttpURLConnection = (HttpURLConnection)mUrl.openConnection();
        // 设置连接超时时间
        mHttpURLConnection.setConnectTimeout(15000);
        // 设置读取超时时间
        mHttpURLConnection.setReadTimeout(15000);
        // 设置请求参数
        mHttpURLConnection.setRequestMethod("POST");
        // 添加Header
        mHttpURLConnection.setRequestProperty("Connection", "keep-Alive");
        // 接收输入流
        mHttpURLConnection.setDoInput(true);
        // 传递参数时需要开启
        mHttpURLConnection.setDoOutput(true);
    }catch(IOException e){
        e.printStackTrace();
    }
    return mHttpURLConnection;}

添加请求参数,传递给HttpURLConnection的输出流,请求连接并处理返回结果

public static void postParams(OutputStream output, List<NameValuePair> paramList)throws IOException{
    StringBuilder mStringBuilder = new StringBuilder();
    for(NameValuePair pair : paramList){
        if(!TextUtils.isEmpty(mStringBuilder)){
            mStringBuilder.append("&");
        }
        mStringBuidler.append(URLEncoder.encode(pair.getName(), "UTF-8"));
    	mStringBuilder.append("=");
    	mStringBuidler.append(URLEncoder.encode(pair.getValue, "UTF-8"));
    }
   BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, "UTF-8"));
    writer.write(mStringBuidler.toString());
    writer.flush();
    writer.close();}private void useHttpUrlConnectionPost(String url){
    InputStream mInputStream = null;
    HttpURLConnection mHttpURLConnection = UrlConnManager.getHttpURLConnection(url);
    try{
        List<NameValuePair> postParams = new ArrayList<>();
        postParams.add(new BasicNameValuePair("ip", "59.108.54.37"));
        UrlConnManager.postParams(mHttpURLConnection.getOutputStream(), postParams);
        mHttpURLConnection.connect();
        mInputStream = mHttpURLConnection.getInputStream();
        int code = mHttpURLConnection.getResponseCode();
        String respose = convertStreamToString(mInputStream);
        Log.d(TAG, "请求状态码:" + code + "\n请求结果:\n" + respose);
        mInputStream.close();
    }catch(IOException e){
        e.printStackTrace();
    }}

开启线程请求淘宝IP库

new Thread(new Runnable(){
    @Override
    public void run(){
        useHttpUrlConnectionPost("http://ip.taobao.com/service/getIpInfo.php");
    }}).start();

Volley

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eyEtf80g-1581561867048)(…/…/pic/image-20191122090855625.png)]

Volley既可以访问网络取得数据,也可以加载图片,并且在性能方面进行了大幅度的调整。

它的设计目标就是适合进行数据量不大但通信频繁的网络操作。不适用于大数据量的网络操作,比如说下载文件等。

Volley的基本用法

在使用 Volley 前请下载 Volley 库且放在 libs 目录下并 add 到工程中。其下载地址为http://central.maven.org/maven2/com/mcxiaoke/volley/library/

Volley 请求网络都是基于请求队列的,开发者只要把请求放在请求队列中就可以了,请求队列会依次进行请求,创建队列的代码如下:

RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());

StringRequest的用法

以下是StringRequest的源码:

public class StringRequest extends Request<String>{
    private final Listener<String> mListener;
    public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener){
        super(method, url, errorListener);
        this.mListener = listener;
    }
    
    // 默认是GET请求
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener){
        this(0, url, listener, errorListener);
    }}

使用第二个方法来请求百度

RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());Listener<String> listener = new Response.Listener<String>(){                                          @Override
                 public void onResponse(String response){
                 	Log.d(TAG, response);
                 };ErrorListener errorListener = new Response.ErrorListener(){
    @Override
    public void onErrorResponse(VolleyError error){
        Log.e(TAG, error.getMessage(), error);
    }};StringRequest mStringRequest = new StringRequest(Request.Method.GET,                      "http://baidu.com",Listener,errorListener);mQueue.add(mStringRequest);

JsonRequest

针对这个接口返回的 JSON 数据格式,我们可以采用http://www.bejson.com/json2javapojo/这个网站来将JSON字符串转换成Java实体类。

JsonRequest的实例代码如下:

JsonObjectRequest mJsonObjectRequest = new JsonObjectRequest(Request.Method.POST,"http://ip.taobao.com/service/getIpInfo.php?ip=59.108.54.37",new Response.Listener<JSONObject>(){
    @Override
    public void onResponse(JSONObject response){
        IpModel ipModel = new Gson().fromJson(response.toString(), IpModel.class);
        if(null != ipModel && null != ipModel.getData()){
            String city = ipMolde.getData().getCity();
            Log.d(TAG, city);
        }
    }},new Response.ErrorListener(){
    @Override
    public void onErrorResponse(VolleyError error){
        Log.d(TAG, error.getMessage(), error);
    }});mQueue.add(mJsonObjectRequest);

ImageRequest加载图片

ImageRequest可以设置图片的最大宽度和高度,在加载图片时,如果图片超过期望的最大宽度和高度,则会进行压缩。

ImageRequest imageRequest = new ImageRequest("https://img-my.csdn.net/uploads/201603/26/1458988468_ 5804.jpg",new Response.Listener<Bitmap>(){
    @Override
    public void onResponse(Bitmap response){
        iv_image.setImageBitmap(response);
    }}, 0, 0, Bitmap.Config.RGB_565, new Response.ErrorListener(){
    @Override
    public void onErrorResponse(VolleyError error){
        iv_image.setImageResource(R.drawable.ico_default);
    }});mQueue.add(imageRequest);

ImageLoader

ImageLoader 的内部使用 ImageRequest 来实现,它的构造方法可以传入一个 ImageCache缓存形参,实现图片缓存的功能;同时还可以过滤重复连接,避免重复发送请求。

与ImageRequest实现效果不同的是,ImageLoader加载图片会先显示默认的图片,等待图片加载完成才会显示在ImageView上。

ImageLoader的使用示例代码如下:

RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());ImageLoader imageLoader = new ImageLoader(mQueue, new BitmapCache());ImageLoader.ImageListener listener = ImageLoader.getImageListener;imageLoader.get("https://img-my.csdn.net/uploads/201603/26/1458988468_ 5804.jpg", listener);

ImageLoader也提供设置最大宽度和高度的方法:

public ImageLoader.ImageContainer get(String requestUrl, ImageLoader.ImageListener imageListener, int maxWidth, int maxHeight){
    return this.get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);}

使用NetworkImageView加载图片

其并没有提供设置最大宽度和高度的方法,而是根据我们设置控件的宽和高,结合网络图片的宽和高内部会自动实现压缩。如果我们不想要压缩,也可以设置NetworkImageView控件的宽和高都为wrap_content。

NetworkImageView是一个自定义控件,继承自ImageView,其封装了请求网络加载图片的功能。先在布局中引用:

<com.android.volley.toolbox.NetworkImageView
                                             android:id="@+id/nv_image"
                                             android:layout_width="200dp"
                                             android:layout_height="200dp"
                                             android:layout_centerHorizontal="true"
                                             android:layout_below="@id/iv_image"
                                             android:layout_marginTop="20dp"></com.android.volley.toolbox.NetworkImageView>

在代码中的调用

iv_image = (ImageView) this.findViewById(R.id.iv_image);RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());ImageLoader imageLoader = new ImageLoader(mQueue, new BitmapCache());nv_image.setDefaultImageResId(R.drawable.ico_default);nv_image.setErrorImageResId(R.drawable.ico_default);nv_image.setImageUrl("https://img-my.csdn.net/uploads/201603/26/1458988468_ 5804.jpg",imageLoader);

OkHttp

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aEF2yM5M-1581561867051)(…/…/pic/image-20191122090720274.png)]

使用OkHttp之前需要先配置gradle,如下:

implementation "com.squareup.okhttp3:okhttp:3.2.0"
implementation 'com.squareup:okio:okio:1.7.0'

注意在AndroidManifest中添加网络权限

异步GET请求

创建OkHttpClient、Request和Call,再调用Call的enqueue()方法,onResponse的回调不在UI线程。如果要同步调用GET请求可以调用Call的execute方法。

Request.Builder requestBuilder = new Request.Builder().url("http://blog.csdn.net/itachi85");requestBuilder.method("GET", null);Request request = requestBuilder.build();OkHttpClient mClient = new OkHttpClient();Call mCall = mClient.newCall(request);mCall.enqueue(new Callback(){
    @Override
    public void onFailure(Call call, IOException e){
        
    }
    
    @Override
    public void onResponse(Call call, Response response)throws IOException{
        String str = response.body().toString();
        Log.d(TAG, str);
    }})

异步POST请求

访问淘宝IP库

与GET请求类似,只是多了用FromBody来封装请求参数,并传递给Request

RequestBody fromBody = new FromBody.Builder()
    .add("ip", "59.108.54.37")
    .build();Request request = new Request.Builder()
    .url("http://ip.taobao.com/service/getIpInfo.php")
    .post(fromBody)
    .build();okHttpClient mclient = new okHttpClient();Call call = mclient.newCall(request);call.enqueue(new Callback(){
    
    @Override
    public void onFailure(Call call, IOException e){
        
    }
    
    @Override
    public void onResponse(Call call, Response response)throws IOException{
        String str = response.body().toString();
        Log.d(TAG, str);
    }})

异步上传文件

// 定义上传文件类型public static final MediaType MEDIA_TYPE_MARKDOWN = MediaType.parse("text/x-markdown; charset=utf-8");// 在SD卡根目录创建文件String filePath = "";if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
    filePath = Enviroment.getExternalStorageDirectory().getAbsolutePath();}else{
    return;}File file = new File(filePath, "wangshu.txt");Request request = new Request.Builder()
    .url("http://api.github.com/markdown/raw")
    .post(RequestBody.create(MEDIA_TYPE_MARKDOWN, file))
    .build();mOkHttpClient.newCall(request).enqueue(new Callbakc(){
    @Override
    public void onFailure(Call call, IOException e){}
    
    @Override
    public void onResponse(Call call, Response response)throws IOException{
        Log.d(TAG, response.body().toString());
    }})

异步下载文件

下载一张图,得到Response后将流写进我们指定的图片文件中。

Request request = new Request.Builder().url(url).build();mOkHttpClient.newCall(request).enqueue(new Callback(){
    @Override
    public void onFailure(Call call, IOException e){
        
    }
    
    @Override
    public void onResponse(Call call, Response response){
        InputStream inputStream = response.body().byteStream();
        FileOutputStream fileOutputStream = null;
        String fillePath = "";
        try{
            if(Environment.getExternalStorageState.equals(
            Environment.MEDIA_MOUNTED)){
                filePath = Environment.getExternalStorageDirectory().
                    getAbsolutePath();
            }else{
                filePPath = getFilesDir().getAbsolutePath();
            }
            File file = new File(filePath, "wangshu.jpg");
            if(null != file){
                fileOutputStream = new FileOutputStream(file);
                byte[] buffer = new byte[2048];
                int len = 0;
                while((len = inputStream.read(buffer)) != -1){
                    fileOutputStream.write(buffer, 0, len);
                }
                fileOutputStream.flush();
            }catch(IOException e){
                Log.d(TAG, "IOException");
                e.printStackTrace();
            }
        }
    }});

异步上传Multipart文件

private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");private void sendMultipart(){
	mOkHttpClient = new OkHttpClient();
    RequestBody requestBody = new MultipartBody.Builder().
        setType(MultipartBody.FROM).
        addFormDataPart("title", "wangshu"). // 上传文件的名字
        addFormDataPart("image", "wangshu.jpg",
                       RequestBody.create(MEDIA_TYPE_PNG, newFile("/sdcard/wangshu.jpg"))). // 上传的文件
        build();
    
    Request request = new Request.Builder().
        header("Authorization", "Client-ID" + "...").
        url("http://api.imgur.com/3/image").
		post(requestBody).
        build();
    
    mOkHttpClient.newCall(new Callback(){
        @Override
        public void onFailure(Call call, IOException e){
        }
        
        @Override
        public void onResponse(Call call, Response response)throws IOException{
            Log.d(TAG, response.body().toString());
        }
    });}

设置超时时间和缓存

File sdcache = getExternalCacheDir();int cacheSize = 10 * 1024 * 1024;OkHttpClient.Builder builder = new OkHttpClient.Builder()
    .connectTimeout(15, TimeUnit.SECONDS)
    .writeTimeout(20, TimeUnit.SECONDS)
    .readTimeout(20, TimeUnit.SECONDS)
    .cache(new Cache(sdcache.getAbsoluteFile(), cacheSize);
           mOkHttpClient = builder.build();

取消请求

private ScheduleExecutorService executor = Executors.newScheduledThreadPool(1);private void cancel(){
    final Request request = new Request.Builder()
        .url("http://www.baidu.com")
        .cacheControl(CacheControl.FORCE_NETWORK)
        .build();
    Call call = mOkHttpClient.newCall(request);
    final Call finalCall = call;
    executor.schedule(new Runnable(){
        @Override
        public void run(){
            finalCall.cancel();
        }
    }, 100, TimeUnit.MILLISECONDS);
    call.enqueue(new Callback(){
        @Override
        public void onFailure(Call call, IOException e){
            @Override
            public void onResponse(Call call, Response response)throws IOException{
                if(null != response.cacheResponse()){
                    String str = response.cacheResponse().toString();
                    Log.d(TAG, "cache---" + str);
                }else{
                    String str = response.networkResponse().toString();
                    Log.d(TAG, "network---" + str);
                }
            }
        }
    })}

封装

创建一个抽象类用于请求回调

public abstract class ResultCallback{
    public abstract void onError(Request request, Exception e);
    public abstract void onResponse(Response response)throws IOException;}

封装实现异步GET请求

public class OkHttpEngine{
    private static OkHttpEngine mInstance;
    private OkHttpClient mOkHttpClient;
    private Handler mHandler;
    
    public static OkHttpEngine getInstance(Context context){
        if(mInstance == null){
            synchronized{
                if(mInstance == null){
                    mInstance = new OkHttpEngine(context);
                }
            }
        }
        return mInstance;
    }
    
    private OkHttpEngine(Context context){
        File sdcache = context.getExternalCacheDir();
        int cacheSize = 10 * 1024 * 1024;
        OkHttpClient.Builder builder = new OkHttpClient.Builder()
            .connectTimeout(15, TimeUnit.SECONDS)
            .writeTimeout(20, TimeUnit.SECONDS)
            .readTimeout(20, TimeUnit.SECONDS)
            .cache(new Cache(sdcache.getAbsoluteFile(), cacheSize));
        mOkHttpClient = builder.build();
        mHandler = new Handler;
    }}public void getAsynHttp(String url, ResultCallback callback){
    final Request request = new Request.Builder()
        .url(url)
        .build();
    Call call = mOkHttpClient.newCall(request);
    dealResult(call, callback);}private void dealResult(Call call, final ResultCallback callback){
    call.enqueue(new Callback(){
        @Override
        public void onFailure(Call call, IOException e){
            sendFailedCallback(call.request(), e, callback);
        }
        
        @Override
        public void onResponse(Call call, Response response)throws IOException{
            sendSuccessCallback(response, callback);
        }
    });}private void sendSuccessCallback(final Response object, final ResultCallback callback){
    mHandler.post(new Runnable(){
        @Override
        public void run(){
            if(callback != null){
                try{
                    callback.onResponse(object);
                }catch(IOExcepption e){
                    e.printStackTrace();
                }
            }
        }
    });}private void sendFailureCallback(final Request request, final Exception e, final ResultCallback callback){
    mHandler.post(new Runnable(){
        @Override
        public void run(){
            if(callback != null){
                callback.onError(request, e);
            }
        }
    })}

在UI线程中的调用

OkHttpEngine.getInstance(MainActivity.this).getAsynHttp("http://www.baidu.com", new ResultCallback(){
    @Override
    public void onError(Request request, Exception e){
        
    }
    
    @Override
    public void onResponse(Response response)throws IOException{
        String str = response.body().toString();
        Log.d(TAG, str);
        Toast.makeText(getApplicationContext(), "请求成功", Toast.LENGTH_SHORT).show();
    }});

Retrofit

使用Retrofit的准备工作,配置build.gradle:

dependencies{
    ...
       implement 'com.squareup.retrofit2:retrofit:2.1.0'
    implement 'com.squareup.retrofit2:converter-gson:2.1.0'
}

GET请求访问网络

编写请求网络的接口

public interface IpService{
    @GET("getIpInfo.php?ip=59.108.54.37")
    Call<IpModel> getIpMsg();}

创建Retrofit,异步请求网络

String url = "http://ip.taobao.com/service/";Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(url) // 请求的url通过baseUrl传入的url和请求网络接口的@GET中的URL拼接而来。
    .addConverterFactory(GsonConverterFactory.create()) // 增加返回值为Json的支持
    .build();IpService ipService = retrofit.create(IpService.class);Call<IpModel> call = ipService.getIpMsg();

在UI线程中回调Callback,显示请求结果

call.enqueue(new Callback<IpModel>(){
    @Override
    public void onResponse(Call<IpModel> call, Response<IpModel> response){
        String country = response.body().getData().getCountry();
        Toast.makeText(getApplicationContext(), country, Toast.LENGTH_SHORT).show();
    }
    @Override
    public void onFailure(Call<IpModel> call, Throwable t){
        
    }});

动态配置URL地址:@Path

public interface IpServiceForPath{
    @GET("{path}/getIpInfo.php?ip=59.108.54.37")
    Call<IpModel> getIpMsg(@Path("path") String path);}
String url = "http://ip.taobao.com/";Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(url)
    .addConverterFactory(GsonConverterFactory.create())
    .build();IpServiceForrPath ipService = retrofit.create(IpServiceForPath.class);Call<IpModel> call = ipService.getIpMsg("service");

动态查询条件

动态指定查询条件:@Query

public interface IpService{
    @GET("getIpInfo.php")
    Call<IpModel> getIpMsg(@Query("ip")String ip);}

动态指定查询条件组:@QueryMap

public interface IpService{
    @GET("getIpInfo.php")
    Call<IpModel> getIpMsg(@QueryMap Map<String, String> options);}

POST请求访问网络

首先用@FormUrlEncoded 注解来标明这是一个表单请求,然后在 getIpMsg 方法中使用@Field注解来标示所对应的String类型数据的键,从而组成一组键值对进行传递。

public interface IpService{
    @FormUrlEncoded
    @POST("getIpInfo.php")
    Call<IpModel> getIpMsg(@Field("ip") String first);}
String url = "http://ip.taobao.com/service/";Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(url)
    .addConverterFactory(GsonConverterFactory.create())
    .build();IpServie ipService = retrofit.create(ipService.class);Call<IpModel> call = ipService.getIpMsg("59.108.54.37");

传输数据类型JSON字符串:@Body

public interface IpService{
    @Post("getIpInfo.php")
    Call<IpModel> getIpMsg(@Body Ip ip);}
public class Ip{
    private String ip;
    public Ip(String ip){
        this.ip = ip;
    }}

异步网络请求

Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(url)
    .addConverterFactory(GsonConverterFactory.create())
    .build();IpService ipService = retrofit.create(IpService.class);Call<IpModel> call = ipService.getIpMsg(new Ip(ip));

单个文件上传:@Part

public interface UploadFileForPart{
    @Multipart
    @POST("user/photo")
    Call<User> updateUser(@Part MultipartBody.Part photo, @Part("description") RequestBody description);}
File file = new File(Environment.getExternalStorageDirectory(), "wangshu.png");RequestBody photoRequestBody = RequestBody.create(MediaType.parse("image/png"), file);MultipartBody.Part photo = MultipartBody.Part.createFormData("photos", "wangshu.png", photoRequestBody);UploadFileForPart uploadFile = retrofit.create(UploadFileForPart.class);Call<User> call = uploadFile.updateUser(photo, RequestBody.create(null, "wangshu"));

多个文件上传:@PartMap

@Multipart@POST("user/photo")Call<User> updateUser(@PartMap Map<String, RequestBody> photos, @Part("description") RequestBody description);

消息报头Header

静态方式

public interface SomeService{
    @GET("some/endpoint")
    @Headers("Accept-Encoding: application/json")
    Call<ResponseBody> getCartType();}

静态方式添加多个消息

public interface SomeService{
    @GET("som/endpoint")
    @Headers({
        "Accept-Encoding: application/json",
        "User-Agent: MoonRetrofit"
    })
    Call<ResponseBody> getCarType();}

设计模式

设计模式的六大原则

单一职责原则:
不要让一个类承担过多的职责。

开放封闭原则:
类、模块、函数等应该是可以拓展的,但是不可以修改。

里氏替换原则:
在程序中尽量使用基类类型对对象定义,在运行时再确定其子类类型。注意以下几点:

  • 子类的所有方法必须在父类中声明。
  • 把父类设计成抽象类或者接口。

依赖倒置原则:
高层模块不应该依赖于低层模块,两者都应该依赖于抽象。细节应该依赖于抽象。
抽象是指接口或者抽象类,细节是指实现类,实现接口或者继承抽象类产生的就是细节。

迪米特原则:
一个实体应当尽量减少与其他实体发生相互作用。主要有以下几点:

  • 创建松耦合的类
  • 每个类应当降低其成员变量和成员函数的访问权限
  • 一个对象对其他对象的引用应该降到最低

接口隔离原则:
一个类对另一个类的依赖应该建立在最小的接口上。主要有以下几点:

  • 接口尽量小,但有一定限度
  • 为依赖的接口定制服务,只暴露给调用类需要的方法,不需要的方法则隐藏起来
  • 提高内聚,减少对外交互,接口方法尽量少用public修饰

设计模式分类

创建型设计模式
单例模式、工厂方法模式、抽象工厂模式、建造者模式、原型模式。

结构型设计模式
适配器模式、装饰模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型设计模式
策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

创建型设计模式

单例模式

单例模式使用场景

  • 整个项目需要一个共享访问点或共享数据。
  • 创建一个对象需要耗费的资源过多,比如访问I/O或者数据库等资源。
  • 工具类对象。

单例模式的结构

保证一个类仅有一个实例,并提供一个访问它的全局访问点。 Client为客户端,Singleton是单例类,通过调用Singleton.getInstance()来获取实例对象。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nJdrFHGS-1581561867053)(…/…/pic/image-20191122144013673.png)]

单例模式有以下几种写法:

饿汉模式

这种方式在类加载时完成初始化,因此类加载慢,没有达到懒加载的效果。如果从未使用这个实例,则会造成内存浪费。但获取对象速度快,避免多线程同步的问题。

public class Singleton{
    private static Singleton instance = new Singleton();
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        return instance;
    }}

懒汉模式(线程不安全)

懒汉模式声明了一个静态对象,在用户第一次调用时初始化。这虽然节约了资源,但第一次加载时需要实例化,反应稍慢一些,而且在多线程时不能正常工作。

public class Singleton{
    private static Singleton instance;
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }}

懒汉模式(线程安全)

能够在多线程中很好地工作,但是每次调用getInstance方法时都需要进行同步。这会造成不必要的同步开销,而且大部分时候我们是用不到同步的。所以不建议用这种模式。

public class Singleton{
    private static Singleton instance;
    private Singleton(){
        
    }
    public static synchronized Singleton getInstance(){
        if(instance == null){
            instance = new Singleton();
        }
        return instance;
    }}

双重检查模式(DCL)

DCL的优点是资源利用率高。第一次执行getInstance时单例对象才被实例化,效率高。
其缺点是第一次加载时反应稍慢一些,在高并发环境下也有一定的缺陷。DCL虽然在一定程度上解决了资源的消耗和多余的同步、线程安全等问题,但其还是在某些情况会出现失效的问题。

public class Singleton{
    private volatile static Singleton singleton;
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        if(instance == null){
            synchronized(Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return singleton;
    }
    }

静态内部类单例模式

第一次加载Singleton类时并不会初始化sInstance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder 并初始化 sInstance。这样不仅能确保线程安全,也能保证 Singleton 类的唯一性。

public class Singleton{
    private Singleton(){
        
    }
    public static Singleton getInstance(){
        return SingletonHolder.sInstance;
    }
    private static class SingletonHolder{
        private static final Singleton sInstance = new Singleton();
    }}

枚举单例

默认枚举实例的创建是线程安全的,并且在任何情况下都是单例。有一种情况(IPC)下其会重新创建对象,那就是反序列化:将一个单例实例对象写到磁盘再读回来,从而获得了一个实例。反序列化操作提供了readResolve方法,这个方法可以让开发人员控制对象的反序列化。

public enum Singleton{
    INSTANCE;
    public void doSomeThing(){
        
    }
    private Object readResolve() throws ObjectStreamException{
        return singleton;
    }}

简单工厂模式

定义
简单工厂模式属于创建型模式,其又被称为静态工厂方法模式,这是由一个工厂对象决定创建出哪一种产品类的实例。

使用场景以及优缺点
工厂类负责创建的对象比较少。
客户只需知道传入工厂类的参数,而无须关心创建对象的逻辑。

• 优点:使用户根据参数获得对应的类实例,避免了直接实例化类,降低了耦合性。

• 缺点:可实例化的类型在编译期间已经被确定。如果增加新类型,则需要修改工厂,这违背了开放封闭原则。简单工厂需要知道所有要生成的类型,其当子类过多或者子类层次过多时不适合使用。

简单工厂模式的结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jI7fehLz-1581561867055)(…/…/pic/image-20191122181747266.png)]

• Factory:工厂类,这是简单工厂模式的核心,它负责实现创建所有实例的内部逻辑。工厂类的创建产

品类的方法可以被外界直接调用,创建所需的产品对象。

• IProduct:抽象产品类,这是简单工厂模式所创建的所有对象的父类,它负责描述所有实例所共有的

公共接口。

• Product:具体产品类,这是简单工厂模式的创建目标。

简单工厂模式的实现

抽象产品类

public abstract class Computer{
    public abstract void start();}

具体的产品类

public class LenovoComputer extends Computer{
    @Override 
    public void start(){
        System.out.println("联想电脑启动");
    }}public class HpComputer extends Computer{
    @Override
    public void start(){
        System.out.println("惠普计算机启动");
    }}public class AsusComputer extends Computer{
    @Override
    public void start(){
        System.out.println("华硕计算机启动");
    }}

工厂类

public class ComputerFactory{
    public static Computer createComputer(String type){
        Computer mComputer = null;
        switch(type){
            case "lenovo":
                mComputer = new LenovoComputer();
                break;
            case "hp":
                mComputer = new HpComputer();
                break;
            case "asus":
                mComputer = new AsusComputer();
                break;
        }
        return mComputer;
    }}

客户端调用工厂类

public class CreateComputer{
    public static void main(String[] args){
        ComputerFactory.createComputer("hp").start();
    }}

工厂模式

工厂方法模式就没有违背这个开放封闭原则。如果我们需要生产苹果计算机,则无须修改工厂类,直接创建产品即可。

工厂模式的结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oMA0asmZ-1581561867057)(…/…/pic/image-20191125094146824.png)]

工厂模式的实现

创建抽象工厂

public abstract class ComputerFactory{
    public abstract <T extends Computer> T createComputer(Class<T> clz);}

创建具体的工厂

public class GDComputerFactory extends ComputerFactory{
    @Override
    public <T extends Computer> T createComputer(Class<T> clz){
        Computer computer = null;
        String className = clz.getName();
        try{
            computer = (Computer)Class.forName(className).newInstance();
        }catch(Exception e){
            e.printStackTrace();
        }
        return (T)computer;
    }}

客户端调用

public class Client{
    public static void main(String[] args){
        ComputerFactory computerFactory = new GDComputerFactory();
        LenovoComputer mLenovoComputer = computerFactory.createComputer(LenovoComputer.class);
        mLenovoComputer.start();
        HpComputer mHpComputer = computerFactory.createComputer(HpComputer.class);
        mHpComputer.start();
        AsusComputer mAsusComputer = computerFactory.createComputer(AsusComputer.class);
        mAsusComputer.start();
    }}

建造者模式

使用场景
当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时。
在创建一些复杂的对象时,这些对象的内部组成构件间的建造顺序是稳定的,但是对象的内部组成构件面临着复杂的变化。

优缺点

• 优点:
使用建造者模式可以使客户端不必知道产品内部组成的细节。
具体的建造者类之间是相互独立的,容易扩展。
由于具体的建造者是独立的,因此可以对建造过程逐步细化,而不对其他的模块产生任何影响。

• 缺点:
产生多余的Build对象以及导演类。

建造者模式的结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UK3XWevr-1581561867061)(…/…/pic/image-20191125100813564.png)]

• Director:导演类,负责安排已有模块的顺序,然后通知Builder开始建造。

• Builder:抽象Builder类,规范产品的组建,一般由子类实现。

• ConcreteBulider:具体建造者,实现抽象 Builder 类定义的所有方法,并且返回一个组建好的对象。

• Product:产品类。

建造者模式的实现

创建产品类

public class Computer{
    
    private String mCpu;
    private String mMainboard;
    private String mRam;
    
    public void setMCpu(String cpu){
        this.mCpu = cpu;
    }
    
    public void setMMainBoard(String mainboard){
        this.mMainboard = mainboard;
    }
    
    public void setMRam(String ram){
        this.mRam = ram;
    }}

创建Builder类规范产品的组件

public abstract class Builder{
    public abstract void buildCpu(String cpu);
    public abstract void buildMainboard(String mainboard);
    public abstract void buildRam(String ram);
    public abstract Computer create();}

MoonComputerBuilder实现组装计算机

public class MoonComputerBuilder extends Builder{
    
    private Computer mComputer = new Computer();
    
    @Override
    public void buildCpu(String cpu){
        mComputer.setMCpu(cpu);
    }
    
    @Override
    public void buildMainboard(String mainboard){
        mComputer.setMMainboard(mainboard);
    }
    
    @Override
    public void buildRam(String ram){
        mmComputer.setMRam(ram);
    }
    
    @Override
    public Computer create(){
        return mComputer;
    }}

导演类统一组装过程

public class Director{
	Builder mBuilder = null;
    
    public Director(Buidler build){
        this.mBuilder = build;
    }
    
    public Computer createComputer(String cpu, String mainboard, String ram){
        this.mBuild.buildMainboard(mainboard);
        this.mBuild.buildCpu(cpu);
        this.mBuild.buildRam(ram);
        return mBuild.create();
    }}

客户端调用导演类

public class CreateComputer{
    public static void main(String[] args){
        Builder mBuilder = new MoonComputerBuilder();
        Director  mDirector = new Director(mBuilder);
        mDirector.createComputer("i7-6700", "华擎玩家世尊", "三星 DDR4");
    }}

结构设计型模式

代理模式

代理模式的类型
代理模式从编码的角度来说可以分为静态代理和动态代理。
• 远程代理:
为一个对象在不同的地址空间提供局部代表,这样系统可以将 Server 部分的实现隐藏。
• 虚拟代理:
使用一个代理对象表示一个十分耗费资源的对象并在真正需要时才创建。
• 安全代理:
用来控制真实对象访问时的权限。一般用于真实对象有不同的访问权限时。
• 智能指引:
当调用真实的对象时,代理处理另外一些事,比如计算真实对象的引用计数,当该对象没有引用时,可以自动释放它;或者访问一个实际对象时,检查是否已经能够锁定它,以确保其他对象不能改变它。

代理模式的优点
• 真实主题类就是实现实际的业务逻辑,不用关心其他非本职的工作。
• 真实主题类随时都会发生变化;但是因为它实现了公共的接口,所以代理类可以不做任何修改就能够使用。

代理模式的结构图

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KVd4PV7x-1581561867065)(…/…/pic/image-20191125110045236.png)]

代理模式的实现

抽象主题类

public interface IShop{
    void buy();}

真实主题类

public class Lxy implements IShop{
    @Override
    public void buy(){
        System.out.println("购买");
    }} 

代理类

public class Purchasing implements IShop{
    
    private IShop mShop;
    
    public Purchasing(IShop shop){
        mShop = shop;
    }
    
    @Override
    public void buy(){
        mShop.buy();
    }}

客户端

public class Client{
    public static void main(String[] args){
        IShop liuwangshu = new LiuWangShu();
        IShop purchasing = new Purchasing(liuwangshu);
        purchasing.buy();
    }}

动态代理的实现

public class DynamicPurchasing implements InvocationHandler{
    private Object obj;
    public DynamicPurchasing(Object obj){
        this.obj = obj;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        Object result = method.invoke(obj,args);
        if(method.getName().equals("buy")){
            System.out.println("LiuWangShu are buying stuffs");
        }
        return result;
    }}

客户端

public class Client{
    public static void main(String[] args){
        IShop liuwangshu = new LiuWangShu();
        DynamicPurchasing mDynamicPurchasing = new DynamicPurchasing(liuwangshu);
        ClassLoader loader = liuwangshu.getClass().getClassLoader();
        IShop purchasing = (IShop)Proxy.newProxyInstance(loader, new Class[]{IShop.class}, mDynamicPurchasing);
        purchasing.buy();
    }}

外观模式

外观模式定义
外观模式也被称为门面模式。当我们开发Android的时候,无论是做SDK还是封装API,大多都会用到外观模式。
要求一个子系统的外部与内部的通信必须通过一个统一的对象进行。此模式提供一个高层的接口,使得子系统更易于使用。 这样能降低用户的使用成本。

使用场景
构建一个有层次结构的子系统时,使用外观模式定义子系统中每层的入口点。如果子系统之间是相互依赖的,则可以让其通过外观接口进行通信,减少子系统之间的依赖关系。
当维护一个遗留的大型系统时,可能这个系统已经非常难以维护和拓展;但因为它含有重要的功能,所以新的需求必须依赖于它,这时可以使用外观类,为设计粗糙或者复杂的遗留代码提供一个简单的接口,让新系统和外观类交互,而外观类负责与遗留的代码进行交互。

外观模式的优缺点

• 优点:
减少系统的相互依赖,所有的依赖都是对外观类的依赖,与子系统无关。
对用户隐藏了子系统的具体实现,减少用户对子系统的耦合。这样即使具体的子系统发生了变化,用户也不会感知到。
加强了安全性,子系统中的方法如果不在外观类中开通,就无法访问到子系统中的方法。

• 缺点:
不符合开放封闭原则。如果业务出现变更,则可能要直接修改外观类。

外观模式结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8aPUbURT-1581561867067)(…/…/pic/image-20191125134839137.png)]

外观模式的实现

子系统类

public class ZhaoShi{
    public void taiJiQuan(){
        System.out.print("使用招式太极拳");
    }
    public void qiShangQuan(){
        System.out.print("使用招式七伤拳");
    }
    public void shengHuo(){
        System.out.print("使用招式圣火令");
    }}public class NeiGong{
    public void jiuYang(){
        System.out.print("使用九阳神功");
    }
    public void qianKun(){
        System.out.print("使用乾坤大法");
    }}public class JingMai{
    public void jingMai(){
        System.out.print("开启经脉");
    }}

外观类

public class ZhangWuJi{
    private JingMai jingMai;
    private ZhaoShi zhaoShi;
    private NeiGong neiGong;
    public ZhaoWuJi(){
        jingMai = new JingMai();
        zhaoShi = new ZhaoShi();
        neiGong = new NeiGong(); 
    }
    
    public void QianKun(){
        jingMai.jingMai();
        neiGong.qianKun();
    }
    
    public void QiShang(){
        jingMai.jingMai();
        neiGong.jiuYang();
        zhaoshi.qiShangQuan();
    }}

客户端调用

public class Client{
    public static void main(String[] args){
        ZhangWuJi zhangwuji = new ZhangWuJi();
        zhangwuji.qianKun();
        zhangwuji.qiShang();
    }}

装饰模式

使用场景
在不影响其他对象的情况下,以动态、透明的方式给单个对象添加职责。
需要动态地给一个对象增加功能,这些功能可以动态地撤销。
当不能采用继承的方式对系统进行扩充或者采用继承不利于系统扩展和维护时。

优缺点

• 优点:
通过组合而非继承的方式,动态地扩展一个对象的功能,在运行时选择不同的装饰器,从而实现不同的行为。
有效避免了使用继承的方式扩展对象功能而带来的灵活性差、子类无限制扩张的问题。
具体组件类与具体装饰类可以独立变化,用户可以根据需要增加新的具体组件类和具体装饰类,在使用时再对其进行组合,原有代码无须改变,符合“开放封闭原则”。

• 缺点:
因为所有对象均继承于Component,所以如果Component内部结构发生改变,则不可避免地影响所有子类(装饰者和被装饰者)。如果基类改变,则势必影响对象的内部。
比继承更加灵活机动的特性,也同时意味着装饰模式比继承更加易于出错,排错也很困难。
对于多次装饰的对象,调试时寻找错误可能需要逐级排查,较为烦琐。所以,只在必要的时候使用装饰模式。 装饰层数不能过多,否则会影响效率。

装饰模式的结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5yOrQb3v-1581561867069)(…/…/pic/image-20191125115603345.png)]

抽象组件

public abstract class Swordsman{
    public abstract void attackMagic();}

组件的具体实现类

public class YangGuo extends Swordsman{
    @Override
    public void attackMagic(){
        System.out.println("杨过使用全真剑法");
    }}

抽象装饰类

public abstract class Master extends Swordsman{
    private Swordsman mSwordsman;
    public Master(Swordsman mSwordsman){
        this.mSwordsman = mSwordsman;
    }
    
    @Override
    public void attackMagic(){
        mSwordsman.attackMagic();
    }}

装饰类的具体实现

public class HongQiGong extends Master{
    public HongQiGong(Swordsman swordsman){
        super(swordsman);
    }
    
    public void teachAttackMagic(){
        System.out,println("洪七公教授打狗棒法");
        System.out.println("杨过使用打狗棒法");
    }
    
    @Override
    public void attackMagic(){
        super.attackMagic();
        teachAttackMagic();
    }}public class OuYangFeng extends Master{
    public OuYangFeng(Swordsman mSwordsman){
        super(mSwordsman);
    }
    
    public void teachAttackMagic(){
        System.out.println("欧阳锋教授蛤蟆功");
        System.out.println("杨过使用蛤蟆功");
    }
    
    @Override
    public void attackMagic(){
        super.attackMagic();
        teachAttackMagic();
    }}

客户端

public class Client{
    public static void main(String[] args){
        YangGuo mYangGuo = new YangGuo();
        HongQiGong mHongQiGong = new HongQiGong(mYangGuo);
        mHongQiGong.attackMagic();
        OuYangFeng mOuYangFeng = new OuYangFeng(mYangGuo);
        mOuYangFeng.attackMagic();
    }}

享元模式

享元模式的定义
享元模式是池技术的重要实现方式,它可以减少应用程序创建的对象,降低程序内存的占用,提高程序的性能。使用共享对象有效地支持大量细粒度的对象。
要求细粒度对象,那么不可避免地使得对象数量多且性质相近。这些对象分为两个部分:内部状态和外部状态。内部状态是对象可共享出来的信息,存储在享元对象内部并且不会随环境的改变而改变;而外部状态是对象依赖的一个标记,它是随环境改变而改变的并且不可共享的状态。

享元模式的结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bouu1XjR-1581561867070)(…/…/pic/image-20191125142243855.png)]
• Flyweight:抽象享元角色,同时定义出对象的外部状态和内部状态的接口或者实现。
• ConcreteFlyweight:具体享元角色,实现抽象享元角色定义的业务。
• FlyweightFactory:享元工厂,负责管理对象池和创建享元对象。

享元模式的使用场景
• 系统中存在大量的相似对象。
• 需要缓冲池的场景。

享元模式的实现

抽象享元角色

public interface IGoods{
    public void showGoodsPrice(String name);}

具体的享元角色

public class Goods implements IGoods{
    private String name;
    private String version;
    Goods(String name){
        this.name = name;
    }
    
    /*其中name为内部状态,version为外部状态。showGoodsPrice方法根据version的不同会打印出不同的价 格。*/
    @Override
    public void showGoodsPrice(String version){
        if(version.equals("32G")){
            System.out.println("价格为5199元");
        }else if{
            System.out.println("价格为5999元");
        }
    }}

享元工厂

public class GoodsFactory{
    private static Map<String, Goods>pool = new HashMap<String, Goods>();
    public static Goods getGoods(String name){
        if(pool.containsKey(name)){
            System.out.println("使用缓存,key为:"+name);
            return pool.get(name);
        }else{
            Goods goods = new Goods(name);
            pool.put(name,goods);
            System.out.println("创建商品,key为:"+name);
            return goods;
        }
    }}

客户端调用

public class Client{
    public static void main(String[] args){
        Goods goods1 = GoodsFactory.getGoods("iphone7");
        goods1.showGoodsPrice("32G");
        Goods goods2 = GoodsFactory.getGoods("iphone7");
        goods2.showGoodsPrice("32G");
        Goods goods3 = GoodsFactory.getGoods("iphone7");
        goods3.showGoodsPrice("128G");
    }}

行为型设计模式

策略模式

策略模式的定义
定义一系列的算法,把每一个算法封装起来,并且使它们可相互替换。策略模式使得算法可独立于使用它的客户而独立变化。

使用场景
对客户隐藏具体策略(算法)的实现细节,彼此完全独立。
针对同一类型问题的多种处理方式,仅仅是具体行为有差别时。
在一个类中定义了很多行为,而且这些行为在这个类里的操作以多个条件语句的形式出现。策略模式将相关的条件分支移入它们各自的 Strategy 类中,以代替这些条件语句。

优缺点

• 优点:
使用策略模式可以避免使用多重条件语句。多重条件语句不易维护,而且易出错。
易于拓展。当需要添加一个策略时,只需要实现接口就可以了。

• 缺点:
每一个策略都是一个类,复用性小。如果策略过多,类的数量会增多。
上层模块必须知道有哪些策略,才能够使用这些策略,这与迪米特原则相违背。

结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GljGDFwf-1581561867072)(…/…/pic/image-20191125145230928.png)]

实现

策略接口

public interface FightingStrategy{
    public void fighting();}

具体策略的实现

public class WeakRivalStrategy implements FightingStrategy{
    @Override
    public void fighting(){
        System.out.println("遇到较弱的对手,张无忌使用太极剑");
    }}public class CommonRivalStrategy implements FightingStrategy{
    @Override
    public void fighting(){
        System.out.println("遇到普通对手,张无忌使用圣火令");
    }}public class StrongRivalStrategy implements FightingStrategy{
    @Override
    public void fighting(){
        System.out.println("遇到强大对手,张无忌使用圣火令");
    }}

上下文角色

public class Context{
    private FightingStrategy fightingStrategy;
    public Context(FightingStrategy fightingStrategy){
        this.fightingStrategy = fightingStrategy;
    }
    public void fighting(){
        fightingStrategy.fighting();
    }}

客户端调用

public class ZhangWuJi{
    public static void main(String[] args){
        Context context;
        context = new Context(new WeakRivalStrategy());
        context.fighting();
        
        context = new Context(new CommonRivalStrategy());
        context.fighting();
        
        context = new Context(new StrongRivalStrategy());
        context.fighting();
    }}

模板方法模式

定义
定义一个操作中的算法框架,而将一些步骤延迟到子类中,使得子类不改变一个算法的结构即可重定义算法的某些特定步骤。

使用场景
多个子类有共有的方法,并且逻辑基本相同时。
面对重要、复杂的算法,可以把核心算法设计为模板方法,周边相关细节功能则由各个子类实现。

优缺点

• 优点:
模板方法模式通过把不变的行为搬移到超类,去除了子类中的重复代码。
子类实现算法的某些细节,有助于算法的扩展。

• 缺点:
每个不同的实现都需要定义一个子类,这会导致类的个数的增加,设计更加抽象。

结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8kKGtfxv-1581561867073)(…/…/pic/image-20191125151941579.png)]

实现

public abstract class AbstractSwordsman{
    public final void fighting(){
        neigong();
        meridian();
        if(hasWeapons()){
            weapons();
        }
        moves();
        hook();
    }
    
    protected void hook();
    protected abstract void neigong();
    protected abstract void weapons();
    protected abstract void moves();
    protected void meridian(){
        System.out.println("开启正经和奇经");
    }
    protected boolean hasWeapons(){
        return true;
    }}

具体实现类

public class ZhangWuJi extends AbstractSwordsman{
    @Override
    protected void neigong(){
        System.out.println("运行九阳神功");
    }
    @Override
    protected void weapons(){
        
    }
    @Override
    protected boolean hasWeapons{
        return false;
    }
    @Override
    protected void moves(){
        System.out.println("使用乾坤大挪移");
    }}
public class ZhangSanFeng extends AbstractSwordsman{
    @Override
    protected void neigong(){
        System.out.println("运行纯阳无极功");
    }
    @Overridezhengwujian
    protected void weapons(){
        System.out.println("使用真武剑");
    }
    @Override
    protected void moves(){
        System.out.println("使用招式神门十三剑");
    }
    @Override
    protected void hook(){
        System.out.println("突然肚子不舒服,老夫去趟厕所");
    }}

客户端调用

public class Client{
    public static void main(String[] args){
        ZhangWuJi zhangwuji = new ZhangWuJi();
        zhangeuji.fighting();
        ZhangSanFeng zhangsanfeng = new ZhangSanFeng();
        zhangsanfeng.fighting();
    }}

观察者模式

定义
定义对象间一种一对多的依赖关系,每当一个对象改变状态时,则所有依赖于它的对象都会得到通知并被自动更新。

使用场景
关联行为场景。需要注意的是,关联行为是可拆分的,而不是“组合”关系。
事件多级触发场景。
跨系统的消息交换场景,如消息队列、事件总线的处理机制。

优缺点

• 优点:
观察者和被观察者之间是抽象耦合,容易扩展。
方便建立一套触发机制。

• 缺点:
在应用观察者模式时需要考虑一下开发效率和运行效率的问题。
程序中包括一个被观察者、多个观察者,开发、调试等内容会比较复杂。
在 Java 中消息的通知一般是顺序执行的,那么一个观察者卡顿,会影响整体的执行效率,在这种情况下,一般会采用异步方式。

结构

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CHKWk2o4-1581561867076)(…/…/pic/image-20191125154717151.png)]

实现

抽象观察者

public interface Observer{
    public void update(String message);}

具体观察者

public class WeiXinUser implements Observer{
    private String name;
    public WeiXinUser(String name){
        this.name = name;
    }
    @Override
    public void update(String message){
        System.out.println(name + "-" + message);
    }}

抽象被观察者

public interface Subject{
    public void attach(Observer observer);
    public void detach(Observer observer);
    public void notify(String msg);}

具体被观察者

public class Client{
    public static void main(String[] args){
        SubscriptionSubject mSubscriptionSubject = new SubscriptionSubject();
        WeiXinUser user1 = new WeiXinUser("杨影枫");
        WeiXinUser user2 = new WeiXinUser("月眉儿");
        WeiXinUser user3 = new WeiXinUser("紫萱");
        mSubscriptionSubject.attach(user1);
        mSubscriptionSubject.attach(user2);
        mSubscriptionSubject.attach(user3);
        mSubscriptionSubject.notify("刘皇叔的专栏更新啦");
    }}

函数响应式编程

函数响应式编程可以极大地简化项目,特别是处理嵌套回调的异步事件、复杂的列表过滤和变换或者时间相关问题。

RxJava的基本用法

RxJava与观察者模式
RxJava的异步操作是通过扩展的观察者模式来实现的。RxJava有4个角色Observable、Observer、Subscriber和Suject。Observable和Observer 通过subscribe方法实现订阅关系,Observable就可以在需要的时候通知Observer。

Rxjava的实现

在Gradle文件中配置Rxjava

dependencies {
    ....
    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'
    implementation 'io.reactivex.rxjava2:rxjava:2.x.x'
    ...
}

创建观察者
它决定事件触发的时候将有怎样的行为

Observer<String> observer = new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                Log.d(TAG, "The message user subscribed is " + d.toString());
                text.setText(d.toString() + "\n");
            }

            @Override
            public void onNext(String s) {
                Log.d(TAG, "The next subscribed message is " + s);
                text.append(s + "\n");
            }

            @Override
            public void onError(Throwable e) {
                Log.d(TAG, "The error is " + e.getMessage());
                text.append(e.getMessage() + "\n");
            }

            @Override
            public void onComplete() {
                Log.d(TAG, "User's subscription has been completed");
                text.append("completed" + "\n");
            }
        };

创建被观察者
它决定什么时候触发事件以及触发怎样的事件。

Observable.just("kaji", "midouzi", "tanzhilang", "shanyi", "xiangyang")
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(observer);

RxJava的Subject

连接 Observer 和Observerable的桥梁。因此,Subject可以被理解为Subject=Observable+Observer。

PublishSubject
PublishSubject只会把在订阅发生的时间点之后来自原始Observable的数据发射给观察者。
在Subject被创建后到有观察者订阅它之前这个时间段内,一个或多个数据可能会丢失。如果要确保来自原始Observable的所有数据都被分发,则可以当所有观察者都已经订阅时才开始发射数据,或者改用ReplaySubject。

BehaviorSubject
开始发射原始Observable最近发射的数据。如果此时还没有收到任何数据,它会发射一个默认值,然后继续发射其他任何来自原始Observable的数据。
如果原始的Observable因为发生了一个错误而终止,BehaviorSubject将不会发射任何数据,但是会向Observer传递一个异常通知。

ReplaySubject
不管Observer何时订阅ReplaySubject,ReplaySubject均会发射所有来自原始Observable的数据给Observer。
有不同类型的ReplaySubject,它们用于限定Replay的范围,例如设定Buffer的具体大小,或者设定具体的时间范围。
如果使用ReplaySubject作为Observer,注意不要在多个线程中调用onNext、onComplete和onError方法。这可能会导致顺序错乱。

AsyncSubject
当Observable完成时,AsyncSubject只会发射来自原始Observable的最后一个数据。
如果原始的Observable 因为发生了错误而终止,AsyncSubject 将不会发射任何数据,但是会向Observer传递一个异常通知。

RxJava的操作符

创建操作符

interval

创建一个按固定时间间隔发射整数序列的Observable,相当于定时器。示例代码如下:

// 创建被注观察者Observable.interval(3, TimeUnit.SECONDS)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);// 创建观察者Observer<Long> observer = new Observer<Long>() {
            @Override
            public void onSubscribe(Disposable d) {
                text.setText("Let's start receiving message" + "\n");
            }

            @Override
            public void onComplete() {
                text.append("The subscription is completed" + "\n");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(Long l) {
                data.add("The next subscribed message is "+String.valueOf(l));
                adapter.notifyDataSetChanged();
                Log.d(TAG, "The next subscribed message is " + l);
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };

range

创建发射指定范围的整数序列的Observable,可以拿来替代for循环,发射一个范围内的有序整数序列。
 第一个参数是起始值,并且不小于0;第二个参数为终值,左闭右开。示例代码如下:

// 创建被观察者Observable.range(0,10)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);// 创建观察者Observer<Integer> observer = new Observer<Integer>() {
            @Override
            public void onSubscribe(Disposable d) {
                text.setText("Let's start receiving message" + "\n");
            }

            @Override
            public void onComplete() {
                text.append("\n"+"The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(Integer obj) {
                data.add("The next subscribed message is "+String.valueOf(obj));
                adapter.notifyDataSetChanged();
                Log.d(TAG, "The next subscribed message is " + obj);
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };

repeat

创建一个N次重复发射特定数据的Observable。
示例代码如下:

// 发送、订阅消息Observable.range(0,5)
                .repeat(10)
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);// 处理接收的消息Observer<Integer> observer = new Observer<Integer>() {
            @Override
            public void onSubscribe(Disposable d) {
                text.setText("Let's start receiving message" + "\n");
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(Integer obj) {
                data.add("The next subscribed message is " + obj);
                adapter.notifyDataSetChanged();
                Log.d(TAG, "The next subscribed message is " + obj);
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };

变换操作符

map

map操作符通过指定一个Func对象,将Observable转换为一个新的Observable对象并发射,观察者将收到新的Observable处理。
 假设我们要访问网络,Host地址时常是变化的,它有时是测试服务器地址,有时可能是正式服务器地址,但是具体界面的URL地址则是不变的。如下所示:

// 发送、订阅消息final static String host = "https://github.com/ReactiveX/RxAndroid";

    public static void subscribeMessage(Observer observer, String path) {
        Observable.just(path)
                .map(new Function<String, String>() {
                    @Override
                    public String apply(String s) throws Exception {
                        return host + s;
                    }
                })
                .subscribe(observer);

    }// 处理接收的消息Observer<String> observer = new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                text.setText("Let's start receiving message" + "\n");
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(String obj) {
                data.add("map : " + obj);
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj);
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };
        Message.subscribeMessage(observer, "/kaji");

flatMap、cast、concatMap

flatMap
flatMap操作符将Observable发射的数据集合变换为Observable集合,然后将这些Observable发射的数据平坦化地放进一个单独的 Observable。

cast
cast 操作符的作用是强制将 Observable 发射的所有数据转换为指定类型。

concatMap
flatMap的合并允许交叉,也就是说可能会交错地发送事件,最终结果的顺序可能并不是原始Observable发送时的顺序。
concatMap解决了flatMap交叉问题,提供了一种能够把发射的值连续在一起的函数,而不是合并它们。concatMap的使用方法和flatMap类似。

假设我们仍旧访问网络,但是要访问同一个Host的多个界面,我们可以使用for循环在每个界面的URL前添加Host。但是RxJava提供了一个更方便的操作,如下所示:

// 发送、订阅消息public static String[] strList = {"kaji", "tanzhilang", "kamiya"
            , "yizhizhu", "shanyi", "miyouzi", "ruyeziyou"};

    final static String host = "https://github.com/ReactiveX/RxAndroid";

    public static void subscribeMessage(Observer observer) {
        Observable.fromArray(strList)
                .flatMap(new Function<String, ObservableSource<?>>() {
                    @Override
                    public ObservableSource<?> apply(String s) throws Exception {
                        return Observable.just(host+"/"+s);
                    }
                })
                .cast(String.class)
                .subscribe(observer);

    }// 处理接收的消息Observer<String> observer = new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                text.setText("Let's start receiving message" + "\n");
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(String obj) {
                data.add("flatMap msg : " + obj);
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj);
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };
        Message.subscribeMessage(observer);

flatMapIterable

flatMapIterable操作符可以将数据包装成Iterable,在Iterable中我们就可以对数据进行处理了,如下所示:

// 发送、订阅消息public static String[] strList = {"kaji", "tanzhilang", "kamiya"
            , "yizhizhu", "shanyi", "miyouzi", "ruyeziyou"};

    final static String host = "https://github.com/ReactiveX/RxAndroid";

    public static void subscribeMessage(Observer observer) {
        Observable.fromArray(strList)
                .flatMapIterable(new Function<String, Iterable<String>>() {
                    @Override
                    public Iterable<String> apply(String s) throws Exception {
                        ArrayList<String>sList=new ArrayList<>();
                        sList.add(host+"/"+s);
                        return sList;
                    }
                })
                .cast(String.class)
                .subscribe(observer);

    }// 接收处理消息Observer<String> observer = new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
                text.setText("Let's start receiving message" + "\n");
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(String obj) {
                data.add("flatMap msg : " + obj);
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj);
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };
        Message.subscribeMessage(observer);

buffer

buffer操作符将源Observable变换为一个新的Observable,这个新的Observable每次发射一组列表值而不是一个一个发射。示例代码如下:

// 发送、订阅消息Observable.fromArray(strList)
                .buffer(3)
                .subscribe(observer);// 接收处理消息Observer<ArrayList<String>> observer = new Observer<ArrayList<String>>() {
            @Override
            public void onSubscribe(Disposable d) {
                text.setText("Let's start receiving message" + "\n");
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(ArrayList<String> obj) {
                data.add("buffer msg : " + obj);
                data.add("-----This array of data has been sent-----");
                adapter.notifyDataSetChanged();
                Log.d(TAG,obj.toArray().toString());
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };

groupBy

goupBy操作符用于分组元素,将源Observable变换成一个发射Observables的新Observable (分组后的)。它们中的每一个新Observable都发射一组指定的数据,如下所示:

static GhostDestruction gd1 = new GhostDestruction("炭治郎",
            "炭炭小天使","壬");
    static GhostDestruction gd2 = new GhostDestruction("祢豆子",
            "awsl awsl awsl","壬");
    static GhostDestruction gd3 = new GhostDestruction("善逸",
            "吾好梦中杀鬼;不做梦也能杀鬼","癸");
    static GhostDestruction gd4 = new GhostDestruction("伊之助",
            "猪突猛进;颜值无敌","癸");
    static GhostDestruction gd5 = new GhostDestruction("不死川",
            "死脸;兄控实锤,但还是死脸","甲");
    static GhostDestruction gd6 = new GhostDestruction("上三",
            "炭之郎给我往死里打给炎柱大哥报仇;三哥你太TM惨了......呜呜呜.....","甲");// 发送、订阅消息
    public static void subscribeMessage(Observer observer) {
        Observable<GroupedObservable<String, GhostDestruction>> grouped =
                Observable.just(gd1, gd2, gd3, gd4, gd5, gd6)
                        .groupBy(new Function<GhostDestruction, String>() {
                            @Override
                            public String apply(GhostDestruction ghostDestruction) throws Exception {
                                return ghostDestruction.getLevel();
                            }
                        });
        Observable.concat(grouped).subscribe(observer);

    }// 接收处理消息Observer<GhostDestruction> observer = new Observer<GhostDestruction>() {
            @Override
            public void onSubscribe(Disposable d) {
                text.setText("Let's start receiving message" + "\n");
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(GhostDestruction obj) {
                data.add("group by msg : " + obj.getLevel()+"---------"
                        +obj.getName() + ":" + obj.getDescription());
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj.toString());
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7XijYzCq-1581561867080)(…/…/pic/image-20191126160609844.png)]

filter

filter操作符是对源Observable产生的结果自定义规则进行过滤,只有满足条件的结果才会提交给订阅者,如下所示。

static GhostDestruction gd1 = new GhostDestruction("炭治郎",
            "炭炭小天使", "壬");
    static GhostDestruction gd2 = new GhostDestruction("祢豆子",
            "awsl awsl awsl", "壬");
    static GhostDestruction gd3 = new GhostDestruction("善逸",
            "吾好梦中杀鬼;不做梦也能杀鬼", "癸");
    static GhostDestruction gd4 = new GhostDestruction("伊之助",
            "猪突猛进;颜值无敌", "癸");
    static GhostDestruction gd5 = new GhostDestruction("不死川",
            "死脸;兄控实锤,但还是死脸", "甲");
    static GhostDestruction gd6 = new GhostDestruction("上三",
            "炭之郎给我往死里打给炎柱大哥报仇;三哥你太TM惨了......呜呜呜.....", "甲");
    static GhostDestruction[] ghostTeam = {gd1, gd2, gd3, gd4, gd5, gd6};// 发送、订阅消息public static void filterSubscribe(Observer observer, final String condition) {
        Observable.fromArray(ghostTeam).filter(new Predicate<GhostDestruction>() {
            @Override
            public boolean test(GhostDestruction gd) throws Exception {
                return gd.getLevel().equals(condition);
            }
        }).subscribe(observer);
    }// 接收处理消息Observer<GhostDestruction> observer = new Observer<GhostDestruction>() {
            @Override
            public void onSubscribe(Disposable d) {
                text.setText("Let's start receiving message" + "\n");
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(GhostDestruction obj) {
                data.add("filter msg : " + obj.getLevel()+"---------"
                        +obj.getName() + ":" + obj.getDescription());
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj.toString());
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };
        Message.filterSubscribe(observer,"癸");

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NSf6uQOv-1581561867081)(…/…/pic/image-20191126163211479.png)]

elementAt

elementAt操作符用来返回指定位置的数据。和它类似的有elementAtOrDefault(int,T),其可以允许默认值。具体代码如下所示:

static GhostDestruction gd1 = new GhostDestruction("炭治郎",
            "炭炭小天使", "壬");
    static GhostDestruction gd2 = new GhostDestruction("祢豆子",
            "awsl awsl awsl", "壬");
    static GhostDestruction gd3 = new GhostDestruction("善逸",
            "吾好梦中杀鬼;不做梦也能杀鬼", "癸");
    static GhostDestruction gd4 = new GhostDestruction("伊之助",
            "猪突猛进;颜值无敌", "癸");
    static GhostDestruction gd5 = new GhostDestruction("不死川",
            "死脸;兄控实锤,但还是死脸", "甲");
    static GhostDestruction gd6 = new GhostDestruction("上三",
            "炭之郎给我往死里打给炎柱大哥报仇;三哥你太TM惨了......呜呜呜.....", "甲");
    static GhostDestruction[] ghostTeam = {gd1, gd2, gd3, gd4, gd5, gd6};// 发送、订阅消息public static void elementAtSubscribe(Consumer<GhostDestruction> consumer, int index) {
        Observable.fromArray(ghostTeam)
                .elementAt(index)
                .subscribe(consumer);
    }// 接收处理消息Consumer<GhostDestruction> consumer = new Consumer<GhostDestruction>() {
            @Override
            public void accept(GhostDestruction obj) throws Exception {
                data.add("at element msg : " + obj.getLevel() + "---------"
                        + obj.getName() + ":" + obj.getDescription());
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj.toString());
            }
        };
        Message.elementAtSubscribe(consumer, 4);

distinct

distinct 操作符用来去重,其只允许还没有发射过的数据项通过。和它类似的还有distinctUntilChanged操作符,它用来去掉连续重复的数据。示例代码如下:

static GhostDestruction gd1 = new GhostDestruction("炭治郎",
            "炭炭小天使", "壬");
    static GhostDestruction gd2 = new GhostDestruction("祢豆子",
            "awsl awsl awsl", "壬");
    static GhostDestruction gd3 = new GhostDestruction("善逸",
            "吾好梦中杀鬼;不做梦也能杀鬼", "癸");
    static GhostDestruction gd4 = new GhostDestruction("伊之助",
            "猪突猛进;颜值无敌", "癸");
    static GhostDestruction gd5 = new GhostDestruction("不死川",
            "死脸;兄控实锤,但还是死脸", "甲");
    static GhostDestruction gd6 = new GhostDestruction("上三",
            "炭之郎给我往死里打给炎柱大哥报仇;三哥你太TM惨了......呜呜呜.....", "甲");
    static GhostDestruction gd7 = new GhostDestruction("上三",
            "炭之郎给我往死里打给炎柱大哥报仇;三哥你太TM惨了......呜呜呜.....", "甲");// 发送、订阅消息
    public static GhostDestruction[] ghostTeam = {gd1, gd2, gd7, gd3, gd4, gd5, gd6};public static void distinctSubscribe(Observer observer) {
        Observable.fromArray(new Integer[]{0,0,5,3,5,2,4,1,1,4,2,1,2,2,4,5,4,4,1,1})
                .distinct()
                .subscribe(observer);
    }// 接收处理消息Observer<Integer> observer = new Observer<Integer>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(Integer obj1) {
                GhostDestruction obj = Message.ghostTeam[obj1];
                data.add(obj.getLevel() + "---------" + obj.getName() + ":"
                        + obj.getDescription());
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj.toString());
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };

skip、take

skip操作符将源Observable发射的数据过滤掉前n项;而take操作符则只取前n项;另外还有skipLast和takeLast操作符,则是从后面进行过滤操作。
先来看skip操作符,如下所示:

// 发送、订阅消息static GhostDestruction gd1 = new GhostDestruction("炭治郎",
            "炭炭小天使", "壬");
    static GhostDestruction gd2 = new GhostDestruction("祢豆子",
            "awsl awsl awsl", "壬");
    static GhostDestruction gd3 = new GhostDestruction("善逸",
            "吾好梦中杀鬼;不做梦也能杀鬼", "癸");
    static GhostDestruction gd4 = new GhostDestruction("伊之助",
            "猪突猛进;颜值无敌", "癸");
    static GhostDestruction gd5 = new GhostDestruction("不死川",
            "死脸;兄控实锤,但还是死脸", "甲");
    static GhostDestruction gd6 = new GhostDestruction("上三",
            "炭之郎给我往死里打给炎柱大哥报仇;三哥你太TM惨了......呜呜呜.....", "甲");
    static GhostDestruction gd7 = new GhostDestruction("上三",
            "炭之郎给我往死里打给炎柱大哥报仇;三哥你太TM惨了......呜呜呜.....", "甲");
    public static GhostDestruction[] ghostTeam = {gd1, gd2, gd7, gd3, gd4, gd5, gd6};public static void skipSubscribe(Observer observer, int num) {
        Observable.fromArray(ghostTeam)
                .skip(num)
                .subscribe(observer);
    }// 接收处理消息Observer<GhostDestruction> observer = new Observer<GhostDestruction>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(GhostDestruction obj) {
                data.add(obj.getLevel() + "---------" + obj.getName() + ":"
                        + obj.getDescription());
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj.toString());
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };Message.skipSubscribe(observer, 3);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PTw0PswD-1581561867082)(…/…/pic/image-20191126172106401.png)]

take操作符的示例代码如下:

static GhostDestruction gd1 = new GhostDestruction("炭治郎",
            "炭炭小天使", "壬");
    static GhostDestruction gd2 = new GhostDestruction("祢豆子",
            "awsl awsl awsl", "壬");
    static GhostDestruction gd3 = new GhostDestruction("善逸",
            "吾好梦中杀鬼;不做梦也能杀鬼", "癸");
    static GhostDestruction gd4 = new GhostDestruction("伊之助",
            "猪突猛进;颜值无敌", "癸");
    static GhostDestruction gd5 = new GhostDestruction("不死川",
            "死脸;兄控实锤,但还是死脸", "甲");
    static GhostDestruction gd6 = new GhostDestruction("上三",
            "炭之郎给我往死里打给炎柱大哥报仇;三哥你太TM惨了......呜呜呜.....", "甲");
    public static GhostDestruction[] ghostTeam = {gd1, gd2, gd3, gd4, gd5, gd6};// 发送、订阅消息public static void takeSubscribe(Observer observer, int num) {
        Observable.fromArray(ghostTeam)
                .take(num)
                .subscribe(observer);
    }// 接收处理消息Observer<GhostDestruction> observer = new Observer<GhostDestruction>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(GhostDestruction obj) {
                data.add(obj.getLevel() + "---------" + obj.getName() + ":"
                        + obj.getDescription());
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj.toString());
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
            }
        };
        Message.takeSubscribe(observer, 3);

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-A4ZxhstG-1581561867083)(…/…/pic/image-20191126172733085.png)]

ignoreElements

ignoreElements操作符忽略所有源Observable产生的结果,只把Observable的onCompleted和onError事件通知给订阅者。

// 发送、订阅消息Observable.fromArray(ghostTeam)
                .ignoreElements()
                .subscribe(observer);// 处理接收消息CompletableObserver observer = new CompletableObserver() {
            @Override
            public void onSubscribe(Disposable d) {

            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                text.append("error message:" + e.getMessage() + "\n"
                        + "error cause:" + e.getCause());
            }
        };

throttleFirst

throttleFirst操作符则会定期发射这个时间段里源Observable发射的第一个数据,throttleFirst操作符默认在computation 调度器上执行(关于调度器后面会讲到)。

和 throttleFirst 操作符类似的有sample操作符,它会定时地发射源Observable最近发射的数据,其他的都会被过滤掉。

// 订阅、发送消息/**
每隔100ms发射一个数据。throttleFirst操作符设定的时间为200ms,因此,它会发射每个200ms内的第一 个数据
*/Observable.create(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
                for (GhostDestruction gd : ghostTeam) {
                    emitter.onNext(gd);
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                emitter.onComplete();
            }
        }).throttleFirst(200, TimeUnit.MILLISECONDS)
                .subscribe(observer);// 接收处理消息new Observer<GhostDestruction>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(GhostDestruction obj) {
                data.add(obj.getLevel() + "---------" + obj.getName() + ":"
                        + obj.getDescription());
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj.toString());
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                text.append("error message:" + e.getMessage() + "\n"
                        + "error cause:" + e.getCause());
            }
        };

throttleWithTimeout

通过时间来限流。

源Observable每次发射出来一个数据后就会进行计时。如果在设定好的时间结束前源Observable有新的数据发射出来,这个数据就会被丢弃,同时throttleWithTimeOut重新开始计时。如果每次都是在计时结束前发射数据,那么这个限流就会走向极端:只会发射最后一个数据。

// 发送、订阅数据/**
每隔100ms发射一个数据。当发射的数据是3的倍数的时候,下一个数据就延迟到300ms再发射。
这里设定的过滤时间是200ms,也就是说发射间隔小于200ms的数据会被过滤掉。
*/Observable.create(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
                for (int i = 0; i < ghostTeam.length; i++) {
                    emitter.onNext(ghostTeam[i]);
                    int sleep = 100;
                    if (i % 3 == 0) {
                        sleep = 300;
                    }
                    try {
                        Thread.sleep(sleep);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                emitter.onComplete();
            }
        }).throttleWithTimeout(200, TimeUnit.MILLISECONDS)
                .subscribe(observer);// 接收处理数据new Observer<GhostDestruction>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(GhostDestruction obj) {
                data.add(obj.getLevel() + " --------- " + obj.getName() + ":"
                        + obj.getDescription());
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj.toString());
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                text.append("error message:" + e.getMessage() + "\n"
                        + "error cause:" + e.getCause());
            }
        };

组合操作符

startWith

startWith操作符会在源Observable发射的数据前面插上一些数据

// 发送、订阅数据Observable.fromArray(ghostTeam)
                .startWith(ghostTeam[11])
                .subscribe(observer);// 接收处理数据new Observer<GhostDestruction>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(GhostDestruction obj) {
                data.add(obj.getLevel() + " --------- " + obj.getName() + ":"
                        + obj.getDescription());
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj.toString());
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                text.append("error message:" + e.getMessage() + "\n"
                        + "error cause:" + e.getCause());
            }
        };

merge

merge操作符将多个Observable合并到一个Observable中进行发射,merge可能会让合并的Observable发射的数据交错。

注意:merge的两个Observable数据必须是同一个类型,且接收Observer与Observable必须是同一个类型,以下示例代码中接收,发送的数据都是String类型。

// 发送、订阅消息Observable obs1 = Observable.fromArray(ghostTeam)
                .flatMap(new Function<GhostDestruction, ObservableSource<?>>() {
                    @Override
                    public ObservableSource<?> apply(GhostDestruction s) throws Exception {
                        Random random = new Random();
                        return Observable.just(s.toString() + "\n"
                                + "url:" + host + "/" + strList[random.nextInt(strList.length)]);
                    }
                }).cast(String.class);
        Observable obs2 = Observable.fromArray(strList)
                .flatMap(new Function<String, ObservableSource<?>>() {
                    @Override
                    public ObservableSource<?> apply(String s) throws Exception {
                        Random random = new Random();
                        GhostDestruction gd = ghostTeam[random.nextInt(ghostTeam.length)];
                        return Observable.just("\n" + "In Ghost Destruction" + "\n"
                                + "cv:" + s + "\n"
                                + "name:" + gd.getName() + "\n"
                                + "description:" + gd.getDescription() + "\n"
                                + "level:" + gd.getLevel()
                        );
                    }
                });
        Observable.merge(obs1, obs2).subscribe(observer);// 接收处理消息new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(String obj) {
                data.add(tip+"---------------------------------"
                        +obj);
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj.toString());
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                text.append("error message:" + e.getMessage() + "\n"
                        + "error cause:" + e.getCause());
            }
        };

concat

将多个 Obserbavle 发射的数据进行合并发射。concat 严格按照顺序发射数据,前一个Observable没发射完成是不会发射后一个Observable的数据的。

zip

zip操作符合并两个或者多个Observable发射出的数据项,根据指定的函数变换它们,并发射一个新值。

注意:进行zip的两个Observable不能是集合只能是单个对象

Observable obs1 = Observable.just(cvList[0],cvList[1],cvList[2]);
        Observable obs2 = Observable.just(ghostTeam[0],ghostTeam[1],ghostTeam[2]);
        Observable.zip(obs1, obs2, new BiFunction<String, GhostDestruction, String>() {
            @Override
            public String apply(String cv, GhostDestruction gd) throws Exception {
                return gd.toString()+"\n"+"cv character : "+cv;
            }
        }).subscribe(observer);

combineLastest

当两个Observable中的任何一个发射了数据时,使用一个函数结合每个Observable发射的最近数据项,并且基于这个函数的结果发射数据。

Observable obs1 = Observable.just(cvList[0], cvList[1], cvList[2]);
        Observable obs2 = Observable.just(ghostTeam[0], ghostTeam[1], ghostTeam[2]);
        Observable.combineLatest(obs1, obs2, new BiFunction<String, GhostDestruction                , String>() {
            @Override
            public String apply(String cv, GhostDestruction gd) throws Exception {
                StringBuilder result = new StringBuilder();
                result.append(gd.toString());
                result.append("\n" + "cv character : " + cv);
                return result.toString();
            }
        }).subscribe(observer);

辅助操作符

delay

delay操作符让原始Observable在发射每项数据之前都暂停一段指定的时间段。

Observable.create(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
                for(GhostDestruction gd:ghostTeam){
                    emitter.onNext(gd);
                }
                emitter.onComplete();
            }
        })
    			.delay(2, TimeUnit.SECONDS)
    			.subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);

暂停过长时间,如以上代码中的设置,则会抛出异常:CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

解决方法:让Observable在子线程运行,Observer在主线程运行即可。

do

Do系列操作符就是为原始Observable的生命周期事件注册一个回调,当Observable的某个事件发生时就会调用这些回调。

• doOnEach:为Observable注册这样一个回调,当Observable每发射一项数据时就会调用它一次,包括onNext、onError和 onCompleted。
• doOnNext:只有执行onNext的时候会被调用。
• doOnSubscribe:当观察者订阅Observable时就会被调用。
• doOnUnsubscribe:当观察者取消订阅Observable时就会被调用;Observable通过onError或者onCompleted结束时,会取消订阅所有的Subscriber。
• doOnCompleted:当Observable 正常终止调用onCompleted时会被调用。
• doOnError:当Observable 异常终止调用onError时会被调用。
• doOnTerminate:当Observable 终止(无论是正常终止还是异常终止)之前会被调用。
• finallyDo:当Observable 终止(无论是正常终止还是异常终止)之后会被调用。

以上各个方法的执行顺序:
doOnSubscribe->
doOnNext-> doOnEach

注意:每次数据发送都会调用这两个方法,在最后一条数据发送完之后,doOnEach中的Notification.toString()为“OnCompleteNotification”。

doOnComplete->doOnTerminate->doFinally

以下是示例代码:

Observable.fromArray(ghostTeam)
                .doOnNext(new Consumer<GhostDestruction>() {
                    @Override
                    public void accept(GhostDestruction ghostDestruction) throws Exception {
                        Log.d(TAG, " [doOnNext] " + ghostDestruction.toString());
                    }
                })
                .doOnEach(new Consumer<Notification<GhostDestruction>>() {
                    @Override
                    public void accept(Notification<GhostDestruction> gdNotification)
                            throws Exception {
                        Log.d(TAG, " [doOnEach] " + "the msg has been sent");
                        if (gdNotification.toString().equals("OnCompleteNotification")) {
                            Log.d(TAG, " [doOnEach] " + gdNotification.toString());
                        }
                    }
                }).doOnSubscribe(new Consumer<Disposable>() {
            @Override
            public void accept(Disposable disposable) throws Exception {
                Log.d(TAG, " [doOnSubscribe] " + " you are subscribing msg "
                        + disposable.toString());
            }
        }).doOnComplete(new Action() {
            @Override
            public void run() throws Exception {
                Log.d(TAG, " [doOnComplete] your subscription is completed");
            }
        }).doOnError(new Consumer<Throwable>() {
            @Override
            public void accept(Throwable throwable) throws Exception {
                Log.d(TAG, " [doOnError] the error msg is " + throwable.getMessage());
            }
        }).doOnTerminate(new Action() {
            @Override
            public void run() throws Exception {
                Log.d(TAG, " [doOnTerminate] the observer is terminated");
            }
        }).doFinally(new Action() {
            @Override
            public void run() throws Exception {
                Log.d(TAG, " [doFinally] finally ghost destruction story will be ended," +
                        "maybe happy ending?");
            }
        }).subscribe(observer);

subscribeOn、observeOn

subscribeOn操作符用于指定Observable自身在哪个线程上运行。如果Observable需要执行耗时操作,一般可以让其在新开的一个子线程上运行。

observerOn用来指定Observer所运行的线程,也就是发射出的数据在哪个线程上使用。Observer一般都是接收消息,需要做UI更新,则在主线程运行。

示例代码如下:

Observable.just("one", "two", "three", "four", "five")
        .subscribeOn(Schedulers.newThread())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(/* an Observer */);

timeout

设定超时后重新发送的数据必须和之前发送的数据为一个类型。示例代码如下:

Observable.fromArray(ghostTeam)
                .delay(30, TimeUnit.HOURS)
                .timeout(3L, TimeUnit.SECONDS, Observable.fromArray(ghostTeam))
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);

因为延时的时间较长,为避免抛出异常则将发送订阅数据的Observable放在子线程中执行,将Observer仍放于主线程执行,延迟30h发送数据,timeout在超时3s后即开始重新发送数据。

错误处理操作符

catch

catch操作符拦截原始Observable的onError通知,将它替换为其他数据项或数据序列,让产生的Observable能够正常终止或者根本不终止。

• onErrorReturn:Observable遇到错误时返回原有Observable行为的备用Observable,备用Observable会忽略原有Observable的onError调用,不会将错误传递给观察者。作为替代,它会发射一个特殊的项并调用观察者的onCompleted方法。

• onErrorResumeNext:Observable遇到错误时返回原有Observable行为的备用Observable,备用Observable会忽略原有Observable的onError调用,不会将错误传递给观察者。

• onExceptionResumeNext:它和onErrorResumeNext类似。不同的是,如果onError收到的Throwable不是一个Exception,它会将错误传递给观察者的onError方法,不会使用备用的Observable。

示例代码如下:

Observable obs1 = Observable.fromArray(cvList);
        Observable obs2 = Observable.fromArray(ghostTeam);
        Observable.zip(obs1, obs2, new BiFunction<String[], GhostDestruction[]
                , String>() {
            @Override
            public String apply(String[] cv, GhostDestruction[] gd) throws Exception {
                StringBuilder result = new StringBuilder();
                result.append(gd.toString());
                result.append("\n" + "cv character : " + cv);
                return result.toString();
            }
        })
                .onErrorReturn(new Function<Throwable, String>() {
                    @Override
                    public String apply(Throwable throwable) throws Exception {
                        throwable.printStackTrace();
                        Log.d(TAG, " [onErrorReturn] error msg : " + throwable.getMessage());
                        StringBuilder rs = new StringBuilder();
                        rs.append("Appearing error, resend data" + "\n");
                        rs.append("error message : " + "\n" + throwable.getMessage() + "\n" + "\n");
                        for (int i = 0; i < 16; i++) {
                            rs.append(ghostTeam[i].toString());
                            rs.append("\n" + "cv character : " + cvList[i] + "\n" + "\n");
                        }
                        return rs.toString();
                    }
                })
                .subscribe(observer);

如果不用onErrorReturn方法则会抛出java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.String[]异常

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wDS2hIuW-1581561867086)(…/…/pic/image-20191128111743754.png)]

retry

retry操作符不会将原始Observable的onError通知传递给观察者,它会订阅这个Observable,再给它一次机会无错误地完成其数据序列。
retry总是传递onNext通知给观察者,由于重新订阅,这可能会造成数据项重复。
retry指定最多重新订阅的次数,如果次数超了,它不会尝试再次订阅,而会把最新的一个onError通知传递给自己的观察者。

示例代码如下:

Observable.create(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
                for (int i = 0; i < 16; i++) {
                    if (i == 15 | i == 14) {
                        emitter.onError(new Throwable("appearing error, case this msg " +
                                "is episode rather than character"));
                    } else {
                        String str = ghostTeam[i].toString() + "\n" +
                                "cv character : " + cvList[i];
                        emitter.onNext(str);

                    }
                }
                emitter.onComplete();
            }
        }).retry(2).subscribe(observer);

按照以上代码的逻辑,数据会重新发送并没有重复发送,但由于抛出的异常并没有捕捉到,而出现异常:io.reactivex.exceptions.UndeliverableException: The exception could not be delivered to the consumer because it has already canceled/disposed the flow or the exception has nowhere to go to begin with.
从而导致主程序的崩溃。

条件操作符和布尔操作符

all

all操作符根据一个函数对源Observable发射的所有数据进行判断,最终返回的结果就是这个判断结果。

这个函数使用发射的数据作为参数,内部判断所有的数据是否满足我们定义好的判断条件。如果全部都满足则返回true,否则就返回false。示例代码如下:

Observable.fromArray(ghostTeam)
                .all(new Predicate<GhostDestruction>() {
                    @Override
                    public boolean test(GhostDestruction gd) throws Exception {
                        boolean rs = gd.getLevel().contains("甲");
                        Log.d(TAG, " all() ghost team contains extraodinarily " +
                                "powerful menber.That is " + rs);
                        return rs;
                    }
                }).subscribe(new Consumer<Boolean>() {
            @Override
            public void accept(Boolean aBoolean) throws Exception {
                Log.d(TAG,"accept() msg receive");
            }
        });

contains

contains 操作符用来判断源 Observable 所发射的数据是否包含某一个数据。如果包含该数据,会返回true;如果源Observable已经结束了却还没有发射这个数据,则返回false。示例代码如下:

注意:此时Observable不能用fromArray方法,否则在调用contains后,返回值默认为false。

Observable.create(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
                for (GhostDestruction gd : ghostTeam) {
                    emitter.onNext(gd);
                }
                emitter.onComplete();
            }
        })
                .contains(ghostTeam[1])
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean aBoolean) throws Exception {
                        Log.d(TAG, "accept() ghost team contains 灶门炭治郎 is " + aBoolean);
                    }
                });

isEmpty

isEmpty操作符用来判断源Observable 是否发射过数据。如果发射过该数据,就会返回 false;如果源Observable已经结束了却还没有发射这个数据,则返回true。 示例代码如下:

Observable.fromArray(ghostTeam).isEmpty()
                .subscribe(new Consumer<Boolean>() {
                    @Override
                    public void accept(Boolean aBoolean) throws Exception {
                        Log.d(TAG, "accept() ghost team has been sent all : " + aBoolean);
                    }
                });

amb

amb 操作符对于给定两个或多个 Observable,它只发射首先发射数据或通知的那个Observable的所有数据。示例代码如下所示

Observable obs1 = Observable.just(ghostTeam[14], ghostTeam[15])
                .delay(3, TimeUnit.SECONDS)
                .subscribeOn(Schedulers.newThread())
                .observeOn(AndroidSchedulers.mainThread());
        Observable obs2 = Observable.fromArray(ghostTeam)
                .skipLast(2);
        Observable.ambArray(obs1, obs2).subscribe(observer);

defaultEmpty

如果原始Observable没有发射数据,就发射一个默认数据,如下所示:

Observable.create(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(ObservableEmitter<Object> emitter) throws Exception {
                emitter.onComplete();
            }
        }).defaultIfEmpty(cvList).subscribe(observer);Observer<String[]> observer = new Observer<String[]>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(String[] strList) {
                for (String obj : strList) {
                    data.add(obj);
                    adapter.notifyDataSetChanged();
                    Log.d(TAG, obj);
                }
            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                text.append("\n" + "error message:" + e.getMessage() + "\n");
            }
        };

注意:defaultEmpty中发送什么类型数据,在Observer中也必须是对应类型的数据。如以上代码中cvlist是String[]则定义Observer<String[]>

转换操作符

toList

toList操作符将发射多项数据且为每一项数据调用onNext方法的Observable发射的多项数据组合成一个List,然后调用一次onNext方法传递整个列表。

注意:接收消息的是Consumer<? super List<T>>

Observable.fromArray(ghostTeam)
                .toList()
                .subscribe(consumer);Consumer<ArrayList<GhostDestruction>> consumer = new Consumer<ArrayList<GhostDestruction>>() {
            @Override
            public void accept(ArrayList<GhostDestruction> gdList) throws Exception {
                for (GhostDestruction gd : gdList) {
                    data.add(gd.toString());
                    adapter.notifyDataSetChanged();
                    Log.d(TAG, gd.toString());
                }
            }
        };

toSortedList

toSortedList操作符类似于toList操作符;不同的是,它会对产生的列表排序,默认是自然升序。

如果发射的数据项没有实现Comparable接口,会抛出一个异常。当然,若发射的数据项没有实现Comparable接口,可以使用toSortedList(Func2)变体,其传递的函数参数Func2会作用于比较两个数据项。

Observable.fromArray(ghostTeam)
                .toSortedList()
                .subscribe(consumer);new Consumer<ArrayList<GhostDestruction>>() {
            @Override
            public void accept(ArrayList<GhostDestruction> gdList) throws Exception {
                for (GhostDestruction gd : gdList) {
                    data.add(gd.toString());
                    adapter.notifyDataSetChanged();
                    Log.d(TAG, gd.toString());
                }
            }
        };public class GhostDestruction implements Comparable<GhostDestruction> {
    
    private char level;
    
    @Override
    public int compareTo(GhostDestruction gd) {
        if (level == gd.getLevel()) {
            return 0;
        } else {
            return level > gd.getLevel() ? 1 : -1;
        }
    }
    ...}

toMap

toMap操作符收集原始Observable发射的所有数据项到一个Map(默认是HashMap),然后发射这个Map。你可以提供一个用于生成Map的key的函数,也可以提供一个函数转换数据项到Map存储的值(默认数据项本身就是值)。

注意:key重复的数据会被删除

Observable.fromArray(ghostTeam)
                .toMap(new Function<GhostDestruction, Object>() {
                    @Override
                    public Object apply(GhostDestruction gd) throws Exception {
                        return gd.getLevel();
                    }
                }).subscribe(consumer);new Consumer<Map<Character, GhostDestruction>>() {
            @Override
            public void accept(Map<Character, GhostDestruction> gdMap) throws Exception {
                for (Map.Entry<Character, GhostDestruction> entry : gdMap.entrySet()) {
                    data.add("ghost team key : " + entry.getKey() + "\n"
                            + "---------- ghost team value ----------" + "\n" +
                            entry.getValue().toString());
                    adapter.notifyDataSetChanged();
                    Log.d(TAG, "ghost team key : " + entry.getKey() + "\n" +
                            "---------- ghost team value ----------" + "\n"
                            + entry.getValue().toString());
                }
            }
        };

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hOPQczwo-1581561867088)(…/…/pic/image-20191128162627684.png)]

RxJava的线程控制

内置的Scheduler

如果我们不指定线程,默认是在调用subscribe方法的线程上进行回调的。

Schedulers.immediate()
直接在当前线程运行,它是timeout、timeInterval和timestamp操作符的默认调度器。

Schedulers.newThread()
总是启用新线程,并在新线程执行操作。

Schedulers.io()
I/O操作(读写文件、读写数据库、网络信息交互等)所使用的 Scheduler。行为模式和 newThread()差不多,区别在于 io() 的内部实现是用一个无数量上限的线程池,可以重用空闲的线程,因此多数情况下 io() 比 newThread() 更有效率。

Schedulers.computation()
计算所使用的 Scheduler,例如图形的计算。这个 Scheduler使用固定线程池,大小为 CPU 核数。不要把 I/O 操作放在 computation() 中,否则 I/O操作的等待时间会浪费 CPU。它 是 buffer、debounce、delay、interval、sample 和 skip操作符的默认调度器。

Schedulers.trampoline()
当我们想在当前线程执行一个任务时,并不是立即时,可以用.trampoline()将它入队。这个调度器将会处理它的队列并且按序运行队列中的每一个任务。它是repeat和retry操作符默认的调度器。

AndroidSchedulers.mainThread()
它指定的操作在主线程中运行。

控制线程
在RxJava中用subscribeOn和observeOn操作符来控制线程。示例代码如下:

Observable.just("one", "two", "three", "four", "five")
        .subscribeOn(Schedulers.newThread()) // 发送、订阅消息在新创建的子线程
        .observeOn(AndroidSchedulers.mainThread()) // 接收、监听消息在主线程,一般是做UI更新
        .subscribe(/* an Observer */);

Rxjava的使用场景

Rxjava结合OkHttp访问网络

在app中的build.gradle配置Gradle文件,导入okhttp

implementation 'com.squareup.okhttp3:okhttp:4.2.2'

在AndroidManifest.xml申请网络访问权限

<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

在工具类internetUtil中编写封装GET请求和POST请求的方法,使用DCL模式获取internetUtil对象,调用方法实现网络请求数据。

将网络请求的数据,在Observable中订阅,请求和订阅这两个过程都放在io线程中执行。在Observer中接收网络请求的消息,在Observer的回调方法onNext中更新UI,处理接收数据在Main线程执行。

GET请求网络数据

public void getUrl(final String ip, Observer observer) {
        Observable.create(new ObservableOnSubscribe() {
            @Override
            public void subscribe(final ObservableEmitter emitter) throws Exception {
                mOkHttpClient = new OkHttpClient();
                Request request = new Request.Builder().url(ip).build();
                final Call call = mOkHttpClient.newCall(request);

                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
                        e.printStackTrace();
                        emitter.onError(new Exception(e.getCause()));
                    }

                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response)
                            throws IOException {
                        String str = response.body().string();
                        emitter.onNext(str);
                        emitter.onComplete();
                    }
                });
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }

POST请求网络数据

public void postToServer(final String ip, final String json, Observer observer) {
        Observable.create(new ObservableOnSubscribe<Object>() {
            @Override
            public void subscribe(final ObservableEmitter<Object> emitter) throws Exception {
                mOkHttpClient = new OkHttpClient();
                RequestBody body = RequestBody.create(JSON, json);
                Request request = new Request.Builder()
                        .url(ip)
                        .post(body)
                        .build();
                Call call = mOkHttpClient.newCall(request);
                call.enqueue(new Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
                        e.printStackTrace();
                        emitter.onError(new Exception(e.getCause()));
                    }

                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response)
                            throws IOException {
                        String str = response.body().string();
                        emitter.onNext(str);
                        emitter.onComplete();
                    }
                });
            }
        }).subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }

在Activity中调用internetUtil方法,在Observer回调方法中更新UI

internetUtil.getUrl("https://movie.douban.com/subject/26677934/comments?status=P",
                generateStrObserver());
        internetUtil.postToServer("https://www.douban.com/search?",
                DataCache.jsonData, generateStrObserver());public Observer<String> generateStrObserver() {
        Observer<String> observer = new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(String obj) {
                data.add(obj);
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj);

            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                text.append("\n" + "error message:" + e.getMessage() + "\n");
            }
        };
        return observer;
    }

RxJava结合Retrofit访问网络

在app中配置build.gradle文件,导入retrofit

implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.3.0'

注意:如果Rxjava结合Retrofit访问网络,则需要设置rxjava的适配器,才能让数据在Observable中传递,只有2.5.0版本的rxjava,RxJava2CallAdapterFactory才有效。

在AndroidManifest.xml文件中申请网络权限

<uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

创建访问网络的工具类,与OkHttp相同,订阅数据和访问网络在io线程中运行,接收数据和更新UI显示在主线程中运行

封装GET,POST请求

public Retrofit getRetrofit(String url) {
        return new Retrofit.Builder()
                .baseUrl(url)
                // 结果返回不是json格式,转换成其他类型(String)
                .addConverterFactory(new Converter.Factory() {
                    @Override
                    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
                        return new Converter<ResponseBody, String>() {
                            @Override
                            public String convert(ResponseBody value) throws IOException {
                                return value.string();
                            }
                        };
                    }
                })
                // 返回的数据可以结合rxjava的Observeble数据传递下去
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .build();
    }

    public void getIpInformation(String url, String path, Observer observer) {
        mRetrofit = getRetrofit(url);
        IpServiceForPost service = mRetrofit.create(IpServiceForPost.class);
        service.getIpMsg(path)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }

    public void postIpInformation(String url, String keyword, Observer observer) {
        mRetrofit = getRetrofit(url);
        IpServiceForPost service = mRetrofit.create(IpServiceForPost.class);
        service.postIpMsg(keyword)
                .subscribeOn(Schedulers.io())
                .observeOn(AndroidSchedulers.mainThread())
                .subscribe(observer);
    }

在主线程中访问网络,更新UI

// retrofit请求豆瓣长评页面数据
        internetUtil.getIpInformation("https://movie.douban.com",
                "/subject/26596140/reviews", generateStrObserver());
        // retrofit在豆瓣网post请求
        internetUtil.postIpInformation("https://www.douban.com/search/", "文豪", generateStrObserver());public Observer<String> generateStrObserver() {
        Observer<String> observer = new Observer<String>() {
            @Override
            public void onSubscribe(Disposable d) {
            }

            @Override
            public void onComplete() {
                text.append("\n" + "The subscription is completed");
                Log.d(TAG, "The subscription is completed");
            }

            @Override
            public void onNext(String obj) {
                data.add(obj);
                adapter.notifyDataSetChanged();
                Log.d(TAG, obj);

            }

            @Override
            public void onError(Throwable e) {
                e.printStackTrace();
                text.append("\n" + "error message:" + e.getMessage() + "\n");
            }
        };
        return observer;
    }

相关文章推荐