转载请注明原创出处,谢谢!
- GitHub: @Ricco
命令模式:Handler.post
享元模式:Message.obtain
模板方法模式:AsyncTask
责任链模式:OkHttp
建造者模式:Response、Request
原型模式:OkHttpClient
观察者模式:RxJava
分析源码,我还没那个实力,就不写了,但原理还是要知道的。如果被面试官问了,说两个就可以了,别太认真,小心hold不住!
Android中的线程通信机制:消息机制
Android 中消息模型伪代码实现?
class Message{
Object obj;用于存储对象类型数据
int what;表示你要做什么
...
}
class MessageQueue{
Object[] messages;
void put(Message msg){}
Message take(){}
}
class Looper{
MessageQueue msgQ;
void loop(){
while(isLoop){}
}
void cancel(){}
...
}
class Handler{
void sendMessage(Message msg){}
void handleMessage(Message msg){}
...
}
在Android系统中,主线程不可以执行耗时操作,子线程可以执行耗时却不可以直接更新UI,所以,当子线程在执行耗时操作的过程中,如果需要更新UI,则可以发出一个“消息”给主线程,当主线程收到该“消息”后,对“消息”进行处理,即更新UI,从而完成主线程和子线程的协作。
在消息机制中,会使用到:
- Handler消息的发送者和处理者
- Message消息的载体
- MessageQueue消息队列,是若干条消息的容器
- Looper轮循者
Handler
通过调用Handler对象的sendMessage(Message)方法可以在子线程中向主线程发送消息。
通过重写void handleMessage(Message)方法可以决定如何处理消息,Handler默认是运行在主线程的,所以,可以直接更新UI。
- 使用Handler发送消息
- boolean sendMessage(Message msg)
- boolean sendMessageDelayed(Message msg, long delayMillis)延迟delayMillis毫秒后发出消息
- boolean sendMessageAtTime(Message msg, long uptimeMillis)在指定的时间点发出消息,其中,参数uptimeMillis表示从Java元年(1970-01-01 00:00:00)至今经历的毫秒数
- boolean sendEmptyMessage(int what)发送空的消息,本质上并不是空消息,而是该方法可以帮助开发者获取消息对象,使得开发者在调用该方法之前,不用手动创建消息对象,参数int what表示消息对象的what属性
- boolean sendEmptyMessageDelayed(int what, long delayMillis)延迟delayMillis毫秒后发出空消息
- boolean sendEmptyMessageAtTime(int what, long uptimeMillis)在指定的时间点发出消息
- post???()系列方法
- 处理消息的方式
- 自定义类继承自Handler,重写void handleMessage(Message msg)方法,在该方法中决定如何处理消息
- 自定义类实现Handler.Callback,重写boolean handleMessage(Message msg)方法,在该方法中决定如何处理消息,然后创建出自定义的对象,用于new Handler(Handler.Callback callback)构造方法的参数
- 在获取Message对象时,使用Message类的静态方法obtain(Handler handler, Runnable callback),该方法的参数Runnable callback就是处理消息的对象
- 消息的分发:处理消息的优先级
public void dispatchMessage(Message msg) {
if (msg.callback != null) {
handleCallback(msg);
} else {
if (mCallback != null) {
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);
}
}
Handler类有send???()系列方法、sendEmpty???()系列方法、post???()系列方法,都可以实现发送消息,具体的使用应该根据实际情况来进行选取,在不熟练的情况下,始终使用sendMessage(Message)即可。
有3种处理消息的方式,其中,在获取Message对象时,指定Message中的Runnable callback这种方式使用相对比较麻烦,一般不推荐,至于是继承自Handler,还是实现Handler.Callback可以自由选择。
Handler在哪个线程处理消息?
在默认情况下,Handler是在主线程处理消息的,所以,可以直接更新UI。
不可以直接在子线程中创建Handler对象,必须先调用过Looper.prepare()方法,且,Looper必须调用了loop()方法才会轮循消息队列,取出消息交给Handler处理。
在默认情况下,主线程已经存在调用了prepare()和loop()方法的Looper,所以,在主线程中创建Handler对象时不需要考虑Looper的问题。
Handler在哪个线程处理消息,完全取决于该Handler对象关联到的是哪个线程的Looper对象!即:如果在子线程创建Handler,关联到的是主线程的Looper对象,该Handler会在主线程处理线程。
在没有显式的指定Looper对象时,会默认获取创建Handler对象的线程的Looper,可表现为:在哪个线程创建Handler,就在哪个线程处理消息。
如果已经显式的指定Looper对象,则由Looper对象的线程来决定在哪个线程处理消息。
Message
- Message的常用属性有:
- int arg1用于在消息中封装int类型的数据
- int arg2同上
- Object obj用于在消息中封装任意类型的数据
- int what用于标识消息的类型,通常使用静态常量
- Message类静态obtain()方法
Message类静态obtain()方法可以从消息池中获取已有的、空闲的消息对象,如果消息中没有消息对象,或没有空闲的对象,则创建新的对象。
该obtain()方法被重载了多次:- Message obtain()
- Message obtain(Message msg)
- Message obtain(Handler handler)
- Message obtain(Handler handler, int what)
- Message obtain(Handler handler, Runnable callback)
- Message obtain(Handler handler, int what, Object obj)
- Message obtain(Handler handler, int what, int arg1, int arg2)
- Message obtain(Handler handler, int what, int arg1, int arg2, Object obj)
如果在使用obtain()系列方法获取消息对象时指定了Handler对象,则在发送消息时,应该使用消息对象的sendToTarget()方法实现消息的发送。
通过Handler对象的obtainMessage()系列方法也可以获取Message对象,这一系列方法的本质依然是通过Message类的静态obtain()方法实现的,所以,如果没有必要的话,就始终使用Message类的obtain()系列方法获取Message对象即可。
永远不要new Message(),而是使用Message类的静态方法obtain()获取消息对象。
HandlerThread与IntentService
HandlerThread是一个线程类,它的作用是便捷的获取一个运行在子线程的Looper对象。
在使用HandlerThread时,必须遵循以下流程,不可调整代码的先后顺序:
- 创建HandlerThread的线程对象
- 开启线程
- 获取Looper对象
使用HandlerThread的目的只是为了获取运行在子线程的Looper,进而,可以通过该Looper创建出在子线程处理消息的Handler,这样的Handler在接收到消息后可以直接执行耗时操作,当然,这样的Handler是不可以直接更新UI的。
普通的Service由于进程优先级较高,所以适合执行耗时操作,但是,Service也是运行在主线程的,所以在执行耗时操作时,也是必须开启子线程的,同时,每个Service在使用完成后应该停止。总结的结果就是:使用Service多半需要另启子线程,且使用完成后停止该Service。
- IntentService的特点:
- 可以不用开发者显式的开启线程而直接编写耗时任务的
- 任务执行完毕后自我销毁的
- 可以一次性“计划”多项任务,这些任务会依次执行
- 使用IntentService时的注意事项:
- 由于onHandleIntent()是运行在子线程的,所以,可以直接编写耗时任务,但是,不可以更新UI
- 自定义的IntentService终究是Service类,必须注册
- 自定义的IntentService的子类,必须提供无参数的构造方法,否则,系统将无法对自定义的IntentService的子类进行管理和维护
- 基于IntentService的工作特性,这样的Service是必须通过startService()方式激活,而不可以使用bindService()方法来激活
- 如果需要重写IntentService中的生命周期方法,切记在重写时必须调用父类的对应的生命周期方法
使用线程的方式
- Thread + 消息机制
- 优点:可以多项任务同时执行,使用非常灵活,可以只在Activity里使用,也可以Activity+Service使用……
- 缺点:不宜同时开启过多的急剧消耗性能的任务,如果需要保障生命力,则还需要配合Service
- IntentService + ResultReceiver
- 优点:生命力顽强,多项任务依次执行
- 缺点:代码略多 / 使用相对繁琐 / 必须有Service
- AsyncTask
- 优点:可以在任何位置使用,例如在Activity中,或在Service中……代码的管理非常集中,多项任务依次执行
- 缺点:多项任务依次执行
AsyncTask:异步任务
AsyncTask
在使用AsyncTask时,需要自定义类继承自AsyncTask,首要任务是确定3个泛型:
- Params当执行任务之前,是否需要准备某些参数,如果需要的话,这些参数应该是哪种数据类型的
- Progress当执行任务的过程中,是否需要以任何形式显示进度,如果需要的话,用哪种数据类型可以表示进度值
- Result当任务执行完毕以后,是否需要获取结果,如果需要的话,获得到表示结果的数据应该是哪种数据类型的
在自定义的AsyncTask类中,可能涉及的方法有: - Result doInBackground(Params... parmas)该方法是抽象方法,需要被重写,且该方法默认运行在子线程,参数是Params类型的可变参数,返回值是Result类型
- void publishProgress(Progress... values)该方法是用于提交进度的,通常是在doInBackground()中调用的,参数提Progress类型的可变参数
- void onProgressUpdate(Integer... values)该方法是更新进度的方法,每当publishProgress()方法被调用一次,当前onProgressUpdate()方法就会被对应的回调一次,且该方法的参数就是publishProgress()方法的参数,该方法是运行在主线程的,可以直接更新UI
- void onPostExecute(Result result)该方法是处理结果的方法,会在doInBackground()执行完毕后被回调1次,且方法的参数是doInBackground()方法的返回值,该方法是运行在主线程的,可以直接更新UI
执行AsyncTask时,调用AsyncTask对象的execute(Params... params)方法即可
使用AsyncTask时,必须自定义子类实现继承,并且优先确定3个泛型,在这3个泛型中,不需要使用的类型可以使用Void表示,其中,doInBackground()方法是默认运行在子线程的,可以执行耗时操作,publishProgress()方法是用于在doInBackground()的执行过程中提交进度的,如果需要处理进度的显示,必须重写onProgressUpdate()方法,每次执行publishProgress()都会导致onProgressUpdate()被回调,onProgressUpdate()是运行在主线程的,可以直接更新UI,当doInBackground()执行完毕,onPostExecute()将被回调1次,该方法也是运行在主线程的,可以直接更新UI。
当需要执行任务时,先创建任务对象,然后调用execute()方法执行任务。
当需要终止任务时,可以调用任务对象的cancel(true)方法以实现终止。
假设有任务A、任务B均是继承AsyncTask实现的,那么,不管创建多少个任务A、任务B的对象,这所有的任务都将是依次执行的。
终止AsyncTask
调用任务对象的cancel(true)方法可以终止任务,实际表现为onProgressUpdate()方法和onPostExecute()方法将不再被回调,而doInBackground()并不会停止后台的运行!
不推荐使用这样的方式终止任务,在条件允许的情况下,应该使得任务自然运行结束以实现终止的效果
重点来了OkHttp、RxJava
在写OkHttp、RxJava前,我想问大家,你有多长时间没写过Handler,AsyncTask了?我在Android技术群进行询问的时候,没想到引发了一场口水仗,但有一位的观点,我很认同。
- 如果现在项目还用Handler那么只能说项目比较老,如果是新项目,那么就是开发者不爱学习!
- 项目不一定会用到Rx,EventBus,但是对于开发者是标配了!
当然,也有很多反对的意见。
所以,现在每当看到有关Handler,AsyncTask的技术帖,我都很反感,可这又是面试高频率题,没办法,Handler还是要了解。
OkHttp使用
compile 'com.squareup.okhttp3:okhttp:3.8.1'
public class HttpUtils {
public static void sendOkHttpRequest(String address, Callback callback) {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(address).build();
client.newCall(request).enqueue(callback);
}
}
HttpUtils.sendOkHttpRequest(address, new Callback() {
@Override
public void onResponse(Call call, Response response) throws IOException {
final String responseText = response.body().string();
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
@Override
public void onFailure(Call call, IOException e) {
runOnUiThread(new Runnable() {
@Override
public void run() {
}
});
}
});
POST请求
try {
OkHttpClient okHttpClient = new OkHttpClient();
RequestBody requestBody = new FormBody.Builder().add("username", "admin").add("password", "123456").build();
Request request = new Request.Builder().url("http://www.baidu.com").post(requestBody).build();
Response response = okHttpClient.newCall(request).execute();
String responseText = response.body().string();
} catch (IOException e) {
e.printStackTrace();
}
RxJava使用
老罗RxJava视频百度云
链接:http://pan.baidu.com/s/1pLPp4RD 密码:zx9h
public class RxUtils {
private static final String TAG = RxUtils.class.getSimpleName();
/**
* 使用create方式
*/
public static void createObserable() {
//定义被观察者,
Observable<String> observable = Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
if (!subscriber.isUnsubscribed()) {
subscriber.onNext("hello");
subscriber.onNext("world");
subscriber.onCompleted();
}
}
});
Subscriber<String> showsub = new Subscriber<String>() {
@Override
public void onCompleted() {
Log.i(TAG, "onCompleted");
}
@Override
public void onError(Throwable e) {
Log.i(TAG, e.getMessage());
}
@Override
public void onNext(String s) {
Log.i(TAG, "result-->>" + s);
}
};
observable.subscribe(showsub);//关联被观察者
}
}
RxJava+OkHttp实现登录
public class LoginUtils {
private OkHttpClient client;
public LoginUtils() {
client = new OkHttpClient();
}
/**
* 定义了login操作,使用RxAndroid的编程思想
* @param url
* @param params
* @return Json字符串
*/
public Observable<String> login(String url,Map<String,String> params){
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
if (!subscriber.isUnsubscribed()){
FormBody.Builder builder = new FormBody.Builder();
if (params!=null&&!params.isEmpty()){
for(Map.Entry<String,String> entry:params.entrySet()){
builder.add(entry.getKey(),entry.getValue());
}
}
RequestBody requestBody = builder.build();
//构建post请求
Request request = new Request.Builder().url(url).post(requestBody).build();
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
subscriber.onError(e);
}
@Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()){
subscriber.onNext(response.body().string());
}
subscriber.onCompleted();//访问结束
}
});
}
}
});
}
}
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Map<String, String> params = new HashMap<String, String>();
params.put("username", "admin";
params.put("password", "123456");
new LoginUtils().login(URL_LOGIN, params).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
dialog.dismiss();
}
@Override
public void onError(Throwable e) {
Log.i(TAG, e.getMessage());
}
@Override
public void onNext(String s) {
dialog.show();
Log.i(TAG,s);
}
}
});
}
});
网友评论