Android 开发之线程交互
白羽 2018-07-03 来源 :网络 阅读 1031 评论 0

摘要:本文将带你了解Android 开发之线程交互,希望本文对大家学Android有所帮助。




 在Android开发过程中,耗时操作是不允许写在主线程(UI线程)中的,以免由于等待时间过长而发生ANR。所以耗时操作需要创建子线程来完成,然而往往这些操作都需要与主线程进行通讯交互(例如更新主线程的UI),但android规定除了UI线程外,其他线程都不可以对UI控件进行访问或操控,所以我们需要通过一些方法来实现这些功能。

 

1. Handler:

 

handler是android中专门用来在线程之间传递信息类的工具。


1、在B线程中调用Looper.prepare和Looper.loop。(主线程不需要)
2、编写Handler类,重写其中的handleMessage方法。
3、创建Handler类的实例,并绑定looper
4、调用handler的sentMessage方法发送消息。

 

· 子线程更新主线程(UI)

因为主线程自带Looper机制,所有我们不用创建Looper:

 

Handler mHandler = new Handler(){  
 
@Override  public void handleMessage(Message msg) {  
    super.handleMessage(msg);  
    switch (msg.what) {  
        case 1:  
            //do something,refresh UI;  
            break;  
        default:  
            break;  
        }  
    }    
   
};  
 
 
然后开启一个子线程,在子线程里直接使用Handler发送消息:
 
new Thread() {
    public void run() {
    Message message = new Message();    message.what = 1;
    message.obj = "子线程发送的消息Hi~Hi";
    mHandler .sendMessage(message);  };
}.start();
 
 
· 子线程之间交互
 
public class ThreadActivity extends AppCompatActivity {
  private final String TAG = "ThreadActivity";
  // 子线程Handler  private Handler mSubHandler = null;
  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_thread1);    new MyThread().start();    createThread();  }
  /**   * 创建子线程,用于发送消息   */  private void createThread() {    new Thread() {      @Override      public void run() {        int count = 0;        while (count < 10) {          Message msg = new Message();          msg.obj = "子线程计时器:" + count;          msg.what = 1;          // 使用子线程Handler发送消息          mSubHandler.sendMessage(msg);          try {            Thread.sleep(1000);          } catch (InterruptedException e) {            e.printStackTrace();          }          count++;        }      }    }.start();  }
  /**   * 用于接收子线程发送过来的消息   */  class MyThread extends Thread {
    @Override    public void run() {      Looper.prepare();      mSubHandler = new Handler() {        @Override        public void handleMessage(Message msg) {          switch (msg.what) {            case 1:            Log.i(TAG, (String) msg.obj);            break;          }        }      };      Looper.loop();    }  }}
 
 
2. HandlerThread:
HandlerThread是一个包含Looper的Thread,我们可以直接使用这个 Looper 创建 Handler。
API参考:https://developer.android.google.cn/reference/android/os/HandlerThread
 
HandlerThread适用于单线程+异步队列模型场景,相对Handler + Thread简洁。
 
// 也可实现run方法HandlerThread mHandlerThread = new HandlerThread("HandlerThread_Test");
mHandlerThread.start();
 
Handler mThreadHandler = new Handler(mHandlerThread.getLooper()) {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 1:
            Log.i(TAG, "threadName--" + Thread.currentThread().getName() + (String) msg.obj);
            break;
        }
    }
};   
// 发送消息至HanderThread
mThreadHandler.sendMessage(msg);
 
 
3. runOnUiThread
Activity 里面的runOnUiThread( Runnable )方法使子线程更新UI更为简洁。另外还有View.Post(Runnable)和View.PostDelayed(Runnabe,long)方法,用法与runOnUiThread基本相同。
 
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            Thread.sleep( 1000 );
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
 
        runOnUiThread(new Runnable() {
            @Override
            public void run() {
                // 执行UI操作
                Toast.makeText(MainActivity.this, "Test", Toast.LENGTH_SHORT).show();
            }
        });
    }
}).start();
 
 
4. AsyncTask
API参考:https://developer.android.google.cn/reference/android/os/AsyncTask
AsyncTask是一个抽象类,它是由Android封装的一个轻量级异步类。它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。AsyncTasks应该用于短操作(最多几秒)。如果您需要保持线程长时间运行,强烈建议您使用java.util.concurrent包提供的各种API,例如Executor,ThreadPoolExecutor和FutureTask。 异步任务由3个泛型类型定义:Params,Progress和Result,以及4个步骤:onPreExecute,doInBackground,onProgressUpdate和onPostExecute组成。
使用AsyncTask时需要继承AsyncTask类并必须实现doInBackground(params...)方法,大多数情况下还需要实现onPostExecute(Result)方法。
 
 
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
    protected Long doInBackground(URL... urls) {
        int count = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) ((i / (float) count) * 100));
            // Escape early if cancel() is called
            if (isCancelled()) break;
        }
        return totalSize;
    }
 
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }
 
    protected void onPostExecute(Long result) {
        showDialog("Downloaded " + result + " bytes");
    }
}
 
 
创建成功后执行任务非常简单:
new DownloadFilesTask().execute(url1, url2, url3); // Params
 
AsyncTask的泛型类型:
· Params:在执行时传递给任务的参数类型;
· Progress:异步任务执行过程中,返回执行进度值的类型;
· Result:后台任务执行结果类型。
并非所有类型都必须在异步任务中使用,如果不需要使用,则用Void来代替。
rivate class MyTask extends AsyncTask<Void, Void, Void> { ... }
 
当执行一个异步任务时,需要经历4个步骤:
· onPreExecute():在异步任务执行前,在UI主线和中调用。此步骤通常用于设置任务,例如在用记界面中显示进度条。
· doInBackground(Params...):onPreExecute()执行完后,立即在后台线程中调用此方法。此步骤用于执行运行时间可能较长的后台任务,参数由execute(params...)传入。通过此步骤得到结果并返回到最后一步。在计算过程中,可通过调用publishProgress(Progress...)方法,并通过onProgressUpdate(Progress...)更新UI显示任务执行进度。
· onProgressUpdate(Progress...):当publishProgress(Progress...)方法执行后,此步骤在UI主线程中被调用,并更新UI当前任务进度。
· onPostExecute(Result):在后台线程计算完成后在UI线程上调用。 后台线程计算的结果作为参数传递给此步骤。
AsyncTask还提供了cancelled(boolean)方法,它同样在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是,AsyncTask中的cancel(boolean)方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。参数true表示立即取消任务(不一定成功),false则表示允许任务执行完成后再取消。
使用AsyncTask还需要注意以下问题:
· 异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建。
· execute(Params...) 必须在UI线程上执行。
· 不要手动调用onPreExecute(), onPostExecute(Result), doInBackground(Params...), onProgressUpdate(Progress...)方法。
· 该任务只能执行一次(如果尝试执行第二次,则会引发异常)。
· 不能在doInBackground(Params... params)中更改UI组件的信息。
· AsyncTask不与任何组件绑定生命周期,所以在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);
· 如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。
· 屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。
AsyncTask里面有两种线程池供我们调用,默认使用SERIAL_EXECUTOR。AsyncTask的核心线程是5,队列容量是128,最大线程数是9。
· THREAD_POOL_EXECUTOR, 异步线程池。
· SERIAL_EXECUTOR,同步线程池。
 
 一个简单的AsyncTask例子:
自定义AsyncTask:
 
class TestAsyncTask extends AsyncTask<Integer, Integer, Integer> {
 
    @Override
    protected Integer doInBackground(Integer... integers) {
        int count = integers[0];
        int len = integers[1];
        while (count < len) {
            int pro = 100 * count / len;
            Log.i(TAG, "----------" + pro + ":" + count + ":" + len);
            publishProgress(pro);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            count++;
        }
        return count;
    }
 
    @Override
    protected void onProgressUpdate(Integer... values) {
        mText.setText("Progress:" + values[0] + "%");
    }
 
    @Override
    protected void onPostExecute(Integer integer) {
        mText.setText("Finished:" + integer + "%");
    }
}
 
 
创建TestAsyncTask实例:
mAsyncTask.execute(0, 100);

 


本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!


本文由 @白羽 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式IT培训就业服务领导者 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved

208小时内训课程