六、Android 异步处理技术

2018-09-29  本文已影响0人  锦文豪武

移动应用开发要求我们正确的处理好主线程和子线程之间的关系,耗时的操作应该放到子线程中,避免阻塞主线程,导致ANR。异步处理技术是提高应用性能,解决主线程和子线程通信问题的关键。
在Android中,异步处理技术有很多种,常见的有Thread、AsyncTask、Handler、Looper、Executors等。

一个完整的异步处理技术继承树: 微信图片_20180929164441.png

A、Thread(线程)

线程是Java语言的一个概念,他实际执行任务的基本单元,上图可以看出Thread是Android 中异步处理技术的基础。

(1)创建线程的两种方式:

a、继承Thread类并重写run方法

public class MyThread extends Thread {
@Override
public void run() {
    super.run();
    /**实现业务逻辑,文件读取,网络请求等*/
  }
}
   /**
    * 启动Thread
    */
public void startThread(){
MyThread myThread = new MyThread();
/**使用start启动线程*/
myThread.start();
}

b、实现Runable 接口并实现run方法

public class MyRunable implements Runnable {
@Override
public void run() {
    /**实现具体业务逻辑,文件读写,网络请求*/
  }
}
 /***
  * 启动Runnable
 */
public void startRunble(){
     new Thread(new MyRunable()).start();
} 

c、线程三种类型

Android应用中各类的线程本质上都基于Linux系统的pthreads,在应用层可以分为三种类型的线程

(1)主线程(mainThread)

主线程又叫ui线程,随应用的启动而启动,主线程是用来运行Android 组件,同时刷新屏幕上的UI元素。Android 系统如果检测到非线程更新UI组件,那么就会抛出CalledFromWrongThreadException异常,只有主线程才能操作UI,是因为Android的UI工具包不是线程安全.主线程创建的handler会顺序执行接收到的消息,包括从其他线程发送的消息。因此,如果消息队列中前面的消息没有执行完,那么它可能会阻塞队列中的其他消息的及时处理。

(2)Binder线程

Binder线程用于不同进程之间线程的通信,每个线程都维护了一个线程池,用来处理其他进程中线程发送的消息,这些进程包括系统服务、Intents、ContentProvider和Service等。在大部分情况下,应用不需要关心Binder线程,因为系统会优先将请求转换为使用主线程。一个典型的需要使用Binder 线程的场景是应用提供一个给其他进程通过AIDL接口绑定的Service.

(3)后台线程

在应用中显示创建的线程都是后台线程,也就是当刚创建出来时,这些线程的执行体是空的,需要手动添加任务。在Linux系统层面,主线程和后台线程是一样的,在Android 框架中,通过WindowManager赋予了主线程只能处理UI更新以及后台线程不能直接操作UI的限制。

B、HandlerThread(继承了Thread)

HandlerThread 是个集成了Looper和MessageQueue的线程,当启动HandlerThread时,会同时生成Looper和MessageQueue,然后等待消息进行处理,run方法如下:

@Override
    public void run() {
   mTid = Process.myTid();
   Looper.prepare();
synchronized (this) {
    mLooper = Looper.myLooper();
    notifyAll();
}
Process.setThreadPriority(mPriority);
onLooperPrepared();
Looper.loop();
mTid = -1;
}

好处就是我们不需要自己去创建和维护Looper,用法和普通线程一样,语句如下:

   /**
    * HandlerThread
    */
public void startHandlerThread(){
    HandlerThread handlerThread = new HandlerThread("HandlerThread");
    handlerThread.start();
new Handler(handlerThread.getLooper()){
    @Override
      public void handleMessage(Message msg) {
        super.handleMessage(msg);
        /**处理收到的消息*/
        }
 };
}
   /**
    * HandlerThread
   */
public void startHandlerThread(){
      HandlerThread handlerThread = new HandlerThread("HandlerThread");
     handlerThread.start();
     new Handler(handlerThread.getLooper()){
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        /**处理收到的消息*/
         }
    };
}

HandlerThread 中只有一个队列消息,队列中的教习是顺序执行的,因此是线程安全的,当然吞吐量自然受到一定的影响,队列中的任务可能会被前面没有执行完的任务阻塞。HandlerThread内部机制确保了在创建Looper和发送消息之间不存竞态条件,这个通过将HandlerThread.getLooper()实现为一个阻塞操作实现的,只有当HandlerThread准备好接受消息之后曹辉返回,源码如下:

 * This method returns the Looper associated with this thread. If this thread not  been   started
  * or for any reason isAlive() returns false, this method will return null. If this thread
  * has been started, this method will block until the looper has been initialized.  
 * @return The looper.
  */
  public Looper getLooper() {
if (!isAlive()) {
    return null;
}

// If the thread has been started, wait until the looper has been created.
synchronized (this) {
    while (isAlive() && mLooper == null) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }
}
return mLooper;
}

如果具体业务要求在HandlerThread 开始接收消息之前要进行某些初始化的操作的话,可以重写HandlerThread的onLooperPrepared函数,例如可以在这个函数中创建与HandlerThread关联的Handler实例,这同时也可以对外隐藏我们的Handler实例,语句:

 import android.os.Handler;
 import android.os.HandlerThread;
 import android.os.Message;
 import android.os.Process;

 /**
  * Created by chaohao.zhao on 2018/9/25.
   */

public class MyHandlerThread extends HandlerThread {
private Handler handler;
public MyHandlerThread() {
    /**Process.THREAD_PRIORITY_BACKGROUND:后台线程建议设置这个优先级,值为10。*/
   super("MyHandlerThread", Process.THREAD_PRIORITY_BACKGROUND);
}

   @Override
   public void run() {
       super.run();
}

  @Override
  protected void onLooperPrepared() {
    super.onLooperPrepared();
    handler = new Handler(getLooper())
    {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                    break;
                case 2:
                    break;
            }
        }
    };
}
public void method1(){
    handler.sendEmptyMessage(1);
}
   public void method(){
    handler.sendEmptyMessage(2);
   }
}

C、AsyncQueryHandler(继承Handler)

AsyncQueryHandler是用于在ContentProvider上面执行异步的CRUD(Create,Read,Update,Delete)操作工具类,CRUD操作会被放到一个单独的子线程中执行,当操作结束获取到结果后,将通过消息的方式传递给调用AsyncQueryHandler的线程,通常就是主线程,AtyncQueryHandler是一个抽象类,继承Handler,通过封装ContentResolver、HandlerThread、Handler等实现对ContentProvider的异步操作,

原理图:


微信图片_20180929165334.png

(1)AsyncQueryHandler四个操作方法

AsyncQueryHandler封装了四个方法来操作ContentProvider,分别对应上面说到的CRUD操作。

a、Delete:

 /**
  * This method begins an asynchronous delete. When the delete operation is
  * done {@link #onDeleteComplete} is called.
  *
  * @param token A token passed into {@link #onDeleteComplete} to identify
  *  the delete operation.
  * @param cookie An object that gets passed into {@link #onDeleteComplete}
  * @param uri the Uri passed to the delete operation.
  * @param selection the where clause.
  */
 public final void startDelete(int token, Object cookie, Uri uri,
    String selection, String[] selectionArgs) {
// Use the token as what so cancelOperations works properly
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_DELETE;

WorkerArgs args = new WorkerArgs();
args.handler = this;
args.uri = uri;
args.cookie = cookie;
args.selection = selection;
args.selectionArgs = selectionArgs;
msg.obj = args;

mWorkerThreadHandler.sendMessage(msg);
}

b、Update:

 /**
  * This method begins an asynchronous update. When the update operation is
  * done {@link #onUpdateComplete} is called.
  *
  * @param token A token passed into {@link #onUpdateComplete} to identify
  *  the update operation.
  * @param cookie An object that gets passed into {@link #onUpdateComplete}
  * @param uri the Uri passed to the update operation.
  * @param values the ContentValues parameter passed to the update operation.
  */
 public final void startUpdate(int token, Object cookie, Uri uri,
    ContentValues values, String selection, String[] selectionArgs) {
// Use the token as what so cancelOperations works properly
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_UPDATE;

WorkerArgs args = new WorkerArgs();
args.handler = this;
args.uri = uri;
args.cookie = cookie;
args.values = values;
args.selection = selection;
args.selectionArgs = selectionArgs;
msg.obj = args;

mWorkerThreadHandler.sendMessage(msg);
}

c、Read(Query)

 /**
  * This method begins an asynchronous query. When the query is done
  * {@link #onQueryComplete} is called.
  *
  * @param token A token passed into {@link #onQueryComplete} to identify
  *  the query.
  * @param cookie An object that gets passed into {@link #onQueryComplete}
  * @param uri The URI, using the content:// scheme, for the content to
  *         retrieve.
  * @param projection A list of which columns to return. Passing null will
  *         return all columns, which is discouraged to prevent reading data
  *         from storage that isn't going to be used.
  * @param selection A filter declaring which rows to return, formatted as an
  *         SQL WHERE clause (excluding the WHERE itself). Passing null will
  *         return all rows for the given URI.
  * @param selectionArgs You may include ?s in selection, which will be
  *         replaced by the values from selectionArgs, in the order that they
  *         appear in the selection. The values will be bound as Strings.
  * @param orderBy How to order the rows, formatted as an SQL ORDER BY
  *         clause (excluding the ORDER BY itself). Passing null will use the
  *         default sort order, which may be unordered.
  */
  public void startQuery(int token, Object cookie, Uri uri,
    String[] projection, String selection, String[] selectionArgs,
    String orderBy) {
// Use the token as what so cancelOperations works properly
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_QUERY;

WorkerArgs args = new WorkerArgs();
args.handler = this;
args.uri = uri;
args.projection = projection;
args.selection = selection;
args.selectionArgs = selectionArgs;
args.orderBy = orderBy;
args.cookie = cookie;
msg.obj = args;

mWorkerThreadHandler.sendMessage(msg);
}

d、Create(Insert)

 /**
  * This method begins an asynchronous insert. When the insert operation is
  * done {@link #onInsertComplete} is called.
  *
  * @param token A token passed into {@link #onInsertComplete} to identify
  *  the insert operation.
  * @param cookie An object that gets passed into {@link #onInsertComplete}
  * @param uri the Uri passed to the insert operation.
  * @param initialValues the ContentValues parameter passed to the insert operation.
  */
 public final void startInsert(int token, Object cookie, Uri uri,
    ContentValues initialValues) {
// Use the token as what so cancelOperations works properly
Message msg = mWorkerThreadHandler.obtainMessage(token);
msg.arg1 = EVENT_ARG_INSERT;

WorkerArgs args = new WorkerArgs();
args.handler = this;
args.uri = uri;
args.cookie = cookie;
args.values = initialValues;
msg.obj = args;

mWorkerThreadHandler.sendMessage(msg);
}

AsyncQueryHandler的子类可以根据实际需求实现下面的回调函数,从而得到上面的操作的返回结果。

 /**
  * Created by chaohao.zhao on 2018/9/25.
  *
  */

 public class MyAsyncQueryHandler extends AsyncQueryHandler{
public MyAsyncQueryHandler(ContentResolver cr) {
    super(cr);
}

@Override
protected void onDeleteComplete(int token, Object cookie, int result) {
    super.onDeleteComplete(token, cookie, result);
}

@Override
protected void onInsertComplete(int token, Object cookie, Uri uri) {
    super.onInsertComplete(token, cookie, uri);
}

@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
    super.onQueryComplete(token, cookie, cursor);
}

@Override
protected void onUpdateComplete(int token, Object cookie, int result) {
    super.onUpdateComplete(token, cookie, result);
}
}

D、Intentservice

我们知道Service的各个生命周期函数试运行在主线程的,因此它本身并不是一个异步处理技术。为了能够在Service 中做耗时操作,android引入了一个Service的子类:IntentService。
IntentService具有Service一样的生命周期,同时也提供在后台线程处理异步任务的机制。与HandlerThread类似,IntentService也是一个后台线程中顺序执行所有的任务,我们通过Context.startService传递一个Intent类型的参数可以启动IntentService的异步执行,如果此时IntenService正在运行中,那么这个新的Intent将会进入队列进行排队,直到后台线程处理完队列前面的任务;如果此时IntentService没有在运行,那么将会启动一个新的IntentService,当后台线程队列中所有任务处理完成后,Intentservice 将会结束它的生命周期,因此IntenceService不需要我们手动结束。
IntentService 本身是一个抽象类,因此使用前需要继承并实现onHandlerIntent方法,这个方法中实现具体的后台业务处理逻辑,同时在子类的结构构造方法中调用super(String name)传入子类的名字。

(1)例子语句

 public class MyIntentService extends IntentService {
     public MyIntentService(){
         super(MyIntentService.class.getName());
    /**如果设置为true,那么IntentService的onStartCommand 方法将返回START_REDELIVER_INTENT
     * 这时,如果onHandlerIntent方法返回之前的进程死掉了,那么进程将会重新启动,intent将会重新投递
     * */
    setIntentRedelivery(true);
}

/**
 * Creates an IntentService.  Invoked by your subclass's constructor.
 *
 * @param name Used to name the worker thread, important only for debugging.
 */
public MyIntentService(String name) {
    super(name);
}

@Override
protected void onHandleIntent(@Nullable Intent intent) {
    /**这个方法是后台线程中调用的*/

}
 }

(2)在清单文件中注册:

 <!--注册service-->
 <service android:name=".handle.MyIntentService"/>

(3)IntentService源码(继承Service)

IntentService是通过handlerThread来实现后台任务处理的

 public abstract class IntentService extends Service {
 private volatile Looper mServiceLooper;
 private volatile ServiceHandler mServiceHandler;
 private String mName;
 private boolean mRedelivery;

 private final class ServiceHandler extends Handler {
    public ServiceHandler(Looper looper) {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg) {
        onHandleIntent((Intent)msg.obj);
        stopSelf(msg.arg1);
    }
}

/**
 * Creates an IntentService.  Invoked by your subclass's constructor.
 *
 * @param name Used to name the worker thread, important only for debugging.
 */
public IntentService(String name) {
    super();
    mName = name;
}

/**
 * Sets intent redelivery preferences.  Usually called from the constructor
 * with your preferred semantics.
 *
 * <p>If enabled is true,
 * {@link #onStartCommand(Intent, int, int)} will return
 * {@link Service#START_REDELIVER_INTENT}, so if this process dies before
 * {@link #onHandleIntent(Intent)} returns, the process will be restarted
 * and the intent redelivered.  If multiple Intents have been sent, only
 * the most recent one is guaranteed to be redelivered.
 *
 * <p>If enabled is false (the default),
 * {@link #onStartCommand(Intent, int, int)} will return
 * {@link Service#START_NOT_STICKY}, and if the process dies, the Intent
 * dies along with it.
 */
public void setIntentRedelivery(boolean enabled) {
    mRedelivery = enabled;
}

@Override
public void onCreate() {
    // TODO: It would be nice to have an option to hold a partial wakelock
    // during processing, and to have a static startService(Context, Intent)
    // method that would launch the service & hand off a wakelock.

    super.onCreate();
    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
    thread.start();

    mServiceLooper = thread.getLooper();
    mServiceHandler = new ServiceHandler(mServiceLooper);
}

@Override
public void onStart(@Nullable Intent intent, int startId) {
    Message msg = mServiceHandler.obtainMessage();
    msg.arg1 = startId;
    msg.obj = intent;
    mServiceHandler.sendMessage(msg);
}

/**
 * You should not override this method for your IntentService. Instead,
 * override {@link #onHandleIntent}, which the system calls when the IntentService
 * receives a start request.
 * @see android.app.Service#onStartCommand
 */
@Override
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
    onStart(intent, startId);
    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}

@Override
public void onDestroy() {
    mServiceLooper.quit();
}

/**
 * Unless you provide binding for your service, you don't need to implement this
 * method, because the default implementation returns null.
 * @see android.app.Service#onBind
 */
@Override
@Nullable
public IBinder onBind(Intent intent) {
    return null;
}

  /**
  * This method is invoked on the worker thread with a request to process.
 * Only one Intent is 
 at a time, but the processing happens on a
 * worker thread that runs independently from other application logic.
 * So, if this code takes a long time, it will hold up other requests to
 * the same IntentService, but it will not hold up anything else.
 * When all requests have been handled, the IntentService stops itself,
 * so you should not call {@link #stopSelf}.
 *
 * @param intent The value passed to {@link
 *               android.content.Context#startService(Intent)}.
 *               This may be null if the service is being restarted after
 *               its process has gone away; see
 *               {@link android.app.Service#onStartCommand}
 *               for details.
 */
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}

E、Executor Framework

我们知道,创建和销毁对象(例如线程),是存在开销的,如果应用中频繁出现线程的创建和销毁,那么会影响到应用的性能。使用Java Executor框架可以通过线程池等机制解决这个问题。改善应用的体验。

(1)Executora框架为我们提供的能力

创建工作线程池,同时通过队列来控制能够在这些线程的任务个数
检测导致线程意外终止的错误
等待线程执行完成并获取执行结果
批量执行线程,并通过固定的顺序获取执行结果。
在合适时机启动后台线程,从而保证线程执行结果可以很快反馈给用户

Executor 框架的基础是一个名为Executor的接口定义,Executor的主要目的是分离任务的创建和执行,最终是实现上述的功能点。

a、源码:

 public interface Executor {

/**
 * Executes the given command at some time in the future.  The command
 * may execute in a new thread, in a pooled thread, or in the calling
 * thread, at the discretion of the {@code Executor} implementation.
 *
 * @param command the runnable task
 * @throws RejectedExecutionException if this task cannot be
 * accepted for execution
 * @throws NullPointerException if command is null
 */
void execute(Runnable command);
}

b、简单的例子:

通过实现Executor接口并重写executor方法从而实现自己的Executor类,

 public class MyExecutorFramework implements Executor {

@Override
public void execute(@NonNull Runnable command) {
    new Thread(command).start();

}

}

当然那么简单的例子很少有的,通常要增加类似的队列,任务的优先级等功能,最终实现一个线程池,线程池是任务队列和工作现成的集合,这两者组合起来实现生产者消费者模式。
Executor框架为开发者提供了预定义的线程池实现,内容如下:


微信图片_20180929170129.png

代码例子:

 /**
  * 固定大小的线程池
  * @return
  */
 public Executor executorsSize(){
return Executors.newFixedThreadPool(2);
}

 /**
  * 可变大小的线程池
  * @return
  */
 public Executor executorsChangeSize(){
     return Executors.newCachedThreadPool();
 }

 /**
  * 单个线程的线程池
  * @return
  */
 public Executor executorSigle(){
     return Executors.newSingleThreadExecutor();
 }

预定义的线程池都是基于ThreadPoolExecutor类之上构建的,而通过ThreadPoolExecutor开发者可以自定义线程池的一些行为,源码中构造函数的定义:

 public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
     Executors.defaultThreadFactory(), defaultHandler);
 }
微信图片_20180929170301.png

F、AsyncTask

从开始的继承树流程图中可以看出AsyncTask是在Executor框架基础上进行封装的,将耗时任务移动到工作线程中执行,同时提供方便的接口实现工作线程和主线程的通信,使用AsyncTask一般会用到如下方法

(1)抽象类AsyncTask 的泛型的参数

AsyncTAsk(Params,Progress,Result)


微信图片_20180929170418.png

(2)回调方法

注:只有doInbackground在工作线程中执行,其他的都在主线程中执行,
具体方法描述:


微信图片_20180929170512.png

(3)例子

/**
 * Created by chaohao.zhao on 2018/9/26.
 * 
 */

public class MyAsyncTask extends AsyncTask<String,Void,String> {
@Override
protected String doInBackground(String... params) {
    /**在工作线程中执行,进行一步任务处理,譬如进行数据请求*/
    return null;
}

/**
 * 异步执行前的操作
 * 譬如显示加载框
 * */
@Override
protected void onPreExecute() {
    super.onPreExecute();
}


/**
 * 关闭
 * @param result
 */
@Override
protected void onCancelled(String result) {
    super.onCancelled(result);
}


/**
 * 用于更新UI,关闭加载框
 * @param result 是doInBackground方法返回值
 */
@Override
protected void onPostExecute(String result) {
    super.onPostExecute(result);
  }
}

/**
 *获取doInBackground执行进度
 */
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}

调用:

String params= "";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_parent_layout);
new MyAsyncTask().execute(params);
}

G、Loader(异步数据加载框架)

Loader是Android3.0开始引入的一个异步数据加载框架,它使得Activity,Fragment中异步加载数据变得很简单,同时它在数据根源发生变化时,能够发出消息通知。Loader框架涉及的API

(1)Loader框架涉及的API

微信图片_20180929170928.png

LoaderManager.LoaderCallbacks:LoaderManager的回调接口,

三个方法:


微信图片_20180929171026.png

(2)Loader例子:

public class ParentLayoutActivity extends AppCompatActivity implements LoaderManager.LoaderCallbacks{

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

}

/**
 * 创建Loader的地方,此处使用CursorLoader
 * @param id
 * @param args
 * @return
 */
@Override
public Loader onCreateLoader(int id, Bundle args) {
    return null;
}

/**
 * 关闭
 * @param loader
 * @param data
 */
@Override
public void onLoadFinished(Loader loader, Object data) {

}

/**
 * 加载无效时。数据会回调这个
 * @param loader
 */
@Override
public void onLoaderReset(Loader loader) {

  }
}
上一篇下一篇

猜你喜欢

热点阅读