AndroidWiki - 异步加载原生方案和轻量化封装

一,背景

正确的掌握Android异步编码能有效提高APP的性能和体验,但同时也带来了更多的编码工作,而如果编码不当,不仅使得代码难于维护,还可能引入内存开销等问题。

 

二,为什么需要异步机制

1,  基于Android系统机制要求

不要阻塞UI线程(即主线程),因此非UI任务尽可能的走子线程;

不要在UI线程之外访问UI组件,因此子线程的执行结果需要抛转到UI线程。

 

2, 基于性能响应和用户体验

通过多线程提升业务处理和响应能力

避免UI线程的高负荷带来的卡顿甚至阻塞,提升交互体验

 

三,什么时候需要到异步加载

理论上,只要有耗时的任务,必须要抛到工作线程中去执行;然后把在工作线程中执行的任务结果返回到UI线程中去更新组件。

比较密集的场景包括APP的启动,组件的初始化,用户交互业务中有耗时请求,特别是有网络、文件、DB等访问场景时。

四,JDK、SDK典型异步机制

1,Thread、Runnable结合主Looper的handler

最常见最原始的异步操作方式

 

示例:

private void test(){
   
new Thread(mRunnable).start();
}

Handler
mHandler = new Handler(){
   
@Override
   
public void handleMessage(Message msg){
       
if(msg.what == 0x123){
            textView.setText(
"Task Done!!");
       
}
    }
}
;

mRunnable = new Runnable() {
   
@Override
   
public void run() {
        SystemClock.sleep(
5000);    //模拟耗时处理
       
mHandler.sendEmptyMessage(0x123)

   
}
}
;

优点:

l 操作简单,基本无学习成本

缺点:

l 代码规范性较差,不易维护

l 每次都开辟一个线程,开启新线程的以及调度多个线程的开销是非常大的,这往往会带来严重的性能问题,在实际开发中,该方式是很不被推荐的。

 

2,Runnable结合View.post、Activity.runOnUIThread

这其实是第一种异步方式的扩展,差别就是这种方法不需要去定义Handler,优缺点雷同。

 

示例:

只需要将上述的

mHandler.sendEmptyMessage(0x123);

 

改成:

runOnUiThread(new Runnable() {
   
@Override
   
public void run() {
        text.setText(
"Task Done!!");
   
}
})
;

text.post(new Runnable() {
   
@Override
   
public void run() {
        text.setText(
"Task Done!!");
   
}
})
;

 

3,AsyncTask

较为轻量级的异步类,封装了FutureTask的线程池、LinkedBlockingQueue和handler进行调度。

可以直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步执行的程度,最后反馈执行的结果给UI主线程。

基本使用方法,doInBackground(Params…)方法里执行耗时逻辑,然后在onPostExecute(Result) 中将结果更新回UI组件。

 

示例:


/**
 *
生成该类的对象,并调用execute方法之后 
 * 首先执行的是onProExecute方法 
 * 其次执行doInBackgroup方法 
 *
 */
public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {

   
private TextViewtextView;
    private
ProgressBar progressBar;


    public
ProgressBarAsyncTask(TextView textView, ProgressBarprogressBar) {
       
super();
        this
.textView= textView;
        this
.progressBar= progressBar;
   
}

   
/**
     *
这里的Integer参数对应AsyncTask中的第一个参数  
     * 这里的String返回值对应AsyncTask的第三个参数 
     * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改 
     * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作 
     */
   
@Override
   
protected StringdoInBackground(Integer... params) {
        NetOperator netOperator =
new NetOperator();
        int
i = 0;
        for
(i = 10; i <= 100; i+=10) {
            netOperator.operator()
;
           
publishProgress(i);
       
}
       
return i +params[0].intValue() + "";
   
}
   
   
/**
     *
这里的String参数对应AsyncTask中的第三个参数(也就是接收doInBackground的返回值) 
     * 在doInBackground方法执行结束之后在运行,并且运行在UI线程当中 可以对UI空间进行设置 
     */
   
@Override
   
protected void onPostExecute(String result){
       
textView.setText("异步操作执行结束" +result);
   
}
   
   
//该方法运行在UI线程当中,并且运行在UI线程当中可以对UI空间进行设置 
    @Override
   
protected void onPreExecute() {
       
textView.setText("开始执行异步线程");
   
}
   
   
/**
     *
这里的Intege参数对应AsyncTask中的第二个参数 
     * 在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行 
     * onProgressUpdate是在UI线程中执行,所有可以对UI空间进行操作 
     */
   
@Override
   
protected void onProgressUpdate(Integer...values) {
       
int vlaue= values[0];
       
progressBar.setProgress(vlaue);
   
}

 

优点:

l 结构清晰,功能定义明确,尤其适用后台进度任务的交互

缺点:

l 在单个后台异步处理时,显得代码过多,结构过于复杂

l Task实例只能被执行一次,否则多次调用时将会出现异常

4,HandlerThread

HandlerThread可以创建一个带有looper的线程,looper对象可以用于创建Handler类来进行来进行调度,其中创建looper时,start()方法必须先被调用。

创建出来的Handler对象,使用方法同主线程Handler使用方式一样,但此时handleMessage()方法执行在子线程中。

异步交互的一般方式是,在构造传入一个UIHandler对象,将数据返回主线程

 

示例:

handlerThread = new HandlerThread("MyWorkThread");//自定义线程名称 
handlerThread.start();
mWorkHandler = new Handler(handlerThread.getLooper()){
   
@Override
   
public void handleMessage(Message msg){

       
if (msg.what== 0x123){
           
try {
                Log.d(
"HandlerThread", Thread.currentThread().getName());
               
Thread.sleep(5000);//模拟耗时任务 
           
} catch (InterruptedExceptione) {
                e.printStackTrace()
;
           
}
        }

    }
}
;

 

优点:

l 开销较少,仅一个Thread体量可以连续处理多个后台任务

l 兼有handler和Thread的优点,适合队列性中轻量的子线程任务

 

缺点:

l 没有提供返回主线程的接口,需要自行封装

l 通常需要交叉维护多个handler

 

5,IntentService

内部封装了HandlerThread的实现,同时Service的子类,用法跟Service也差不多,实现耗时逻辑应放在onHandleIntent(Intent intent){}的方法体里,它同样有着退出启动它的Activity后不会被系统杀死的特点,而且当任务执行完后会自动停止,无须手动去终止它。

 

示例:

实现onHandleIntent方法即可,具体略。

 

优点:

l 使用方式熟悉

l 兼有HandlerThread和Service的优点,适合单个重度分叉性的后台任务

缺点:

l 没有提供返回主线程的接口,不适合异步交互

l 需要像Service一样在清单配置,开销体量较大

 

6,AsyncQueryHandler

在一般的应用中我们可以使用ContentProvider去操作其它进程的数据库,为了避免ANR,还需要配合上述的异步任务机制,但Android已经为此封装了异步查询框架AsyncQueryHandler。

AsyncQueryHandler类封装了调用者线程与工作线程的交互过程。交互的主体是两个Handler,一个运行在调用者线程中,一个运行在工作者线程中。通过提供onXXXComplete的回调接口,返回主线程实现事件的完成处理。

示例:以查询为例:

定义自己的AsyncQueryHandler,同时定义一个监听器进行设置。

/**
 * 异步的查询操作帮助类,可以处理增删改(ContentProvider提供的数据)
 */
public class CommonQueryHandler extends AsyncQueryHandler {
    public CommonQueryHandler(ContentResolver cr) {
        super(cr);
    }

    /**
     * 当查询完成后,回调的方法
     */
    @Override
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {

        //触发监听事件
        if(cursorChangedListener!=null){
            cursorChangedListener.onCursorChanged(token, cookie, cursor);
        }
    }


    public OnCursorChangedListener getCursorChangedListener() {
        return cursorChangedListener;
    }

    public void setOnCursorChangedListener(OnCursorChangedListener cursorChangedListener) {
        this.cursorChangedListener = cursorChangedListener;
    }

    private OnCursorChangedListener cursorChangedListener;

    /**
     * 定义cursor改变时的事件监听
     * @author leo
     *
     */
    public interface OnCursorChangedListener{
        void onCursorChanged(int token, Object cookie, Cursor cursor);
    }
}

调用:

使用时直接调用startXXX方法即可。

new CommonQueryHandler(getContentResolver()).startQuery(token, cookie, uri, projections, selection selectinArgs, orderBy);

传入的通用参数如下:

l  Int token,一个令牌,主要用来标识查询,保证唯一即可。

l  Object cookie,你想传给onXXXComplete方法使用的一个对象。(没有的话传递null即可)

l  Uri uri(进行查询的通用资源标志符):

l  String[] projections 查询的列

l  String selection 限制条件

l  String[] selectionArgs 查询参数

l  String orderBy 排序条件

优点:

l 实现了异步调度的封装

l 接口简单易用

缺点:

l  封装了ContentResolver,应用场景定向,只适用于数据库操作

 

五,异步线程的抉择

根据上述,我们需要根据不同异步方式优缺点和具体的使用场景进行抉择,一般来说:

1,  大量分散性的轻量化异步任务,可以考虑对Runnable进行线程池的统一管理使用;

2,  频率不高,比较重度,且有进度交互的,可以考虑使用AsyncTask,同时注意执行时,调用其并发性执行接口;

3,  中轻度队列性子任务,考虑使用HandlerThread;

4,  分叉性的重度后台任务,考虑使用IntentService;

5,  涉及ContentProvider的数据库操作,考虑使用AsyncQueryHandler。

六,封装与扩展

对于上述策略和原有的异步类,往往不足以使用,我们一般还需要进一步的封装,其中最常见的包括对HandlerThread执行接口的封装,Runnable和全局线程池的封装。

1,HandlerThread使用进阶

思路:

l HandlerThread最简单的使用方法如普通主线程handler一般,但为了维护代码风格的一致性,不建议在一个组件中显性的操作多个handler。

l 为了维护编码结构,作为一个逻辑处理类,应该转化为一般性的业务接口供外部组件进行调用。

示例:

protected void initWorkerThread() {
    mDBHandlerThread =
new DBHandlerThread("Handler Thread");
   
mDBHandlerThread.setUIHandler(mUIHandler);

mDBHandlerThread.setWorkCallBack(mThreadCallback);

    //start()后会执行Thread的run()方法,回调onLooperPrepared()
   
mDBHandlerThread.start();}

 

 

public class DBHandlerThread extends HandlerThreadimplements Handler.Callback {
   
public static final int MSG_QUERY_FRIENDS = 100;
    private
Handler mWorkerHandler; //与工作线程相关联的Handler
   
private WorkCallbackmWorkback; //提供回调接口
   
private HandlermUIHandler; //与UI线程相关联的Handler
    public DBHandlerThread(String name) {
       
super(name);
   
}
   
public DBHandlerThread(String name, int priority){
       
super(name, priority);
   
}

   
public void setUIHandler(Handlerhandler) {
       
this.mUIHandler = handler;
   
}
   
public void setWorkCallBack(WorkCallbackcb) {
       
this.mWorkback = cb;
   
}
   
public HandlergetWorkerHandler() {
       
return mWorkerHandler;
   
}
   
@Override
   
protected void onLooperPrepared() {
       
mWorkerHandler = new Handler(getLooper(), this);
   
}
   
@Override
   
public boolean handleMessage(Message msg) {
       
switch (msg.what) {
           
case MSG_QUERY_FRIENDS:
               
//...查询数据库操作...

               
if (mUIHandler!= null) {
                   
//回调到主线程
                   
mUIHandler.post(new Runnable() {
                       
@Override
                       
public void run() {
                           
if (mWorkback!= null) {
                               
mWorkback.onQueryFriendsCallback("Friends");
                           
}
                        }
                    })
;
               
}
               
break;
       
}
       
return true;
   
}

   
/**
     *
最终提供给外部组件的接口
     */
   
public void queryFriends() {
        Message msg = Message.obtain(
null, MSG_QUERY_FRIENDS);
       
mWorkerHandler.sendMessage(msg);
   
}

   
/**
     *
释放资源的接口
     */
   
public void release(){
       
mUIHandler = null;
        this
.quit();
   
}

   
/**
     *
自定义的回调
     */
   
interface WorkCallback{
       
void onQueryFriendsCallback(Object object);
        void
onUpdateFriendesCallback();
   
}
}

 

2,Runnable轻量级异步调度类的封装

思路:

l 维持Runnable的易用性

l 对线程开辟使用线程池统一管理

l 构建类似于AsyncTask类的交互接口,方便调用

l 对可能存在的线程强持外部实例的问题,使用弱引用关系进行维护

 

示例:

封装扩展之后的工具类如下:


单后台任务示例:

private staticclass  MyBackTask extendsBackTask<MainMvpActivity, Void> {
   
MyBackTask() {
    }

   
MyBackTask(MainMvpActivityactivity) {
       
super(activity);
   
}

   
@Override
   
protected VoiddoInTheBack() {
        //
子线程
        return null;
   
}

   
@Override
   
protected void doneInTheBack(MainMvpActivityoutInstance, Void doneValue) {
       
// 子线程
    }
}

 

 

异步交互示例:

    private static class AsyncImageTaskextends BackForeTask<MainMvpActivity, ImageEntity>{

       
AsyncImageTask(MainMvpActivity activity) {
           
super(activity);
       
}

       
@Override
       
protected ImageEntitydoInBack() {
            //
子线程
           
return null;
       
}

       
@Override
       
protected void postedToFore(MainMvpActivityoutInstance, ImageEntity postedValue) {
            
// 主线程
           
}
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值