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