提出问题:

1、Android中的异步处理方式?

2、如何使用Handler以及在使用过程中如何避免Handler引起的内存泄露?

3、从源码角度分析MessageQueue,Message,handler,looper,主线程,子thread之间的关系

4、Handler通过sendMessage以及post Runnable对象有什么区别

5、如何给一个线程建立消息循环,即如何构建一个looper线程?

6、Asynctask中有哪些方法,分别如何使用,哪些方法在主线程执行,哪些方法在子线程执行,Asynctask中的参数关系

7、Asynctask与使用Handler+thread的优缺点对比(区别)


解决问题:

问题1在Android中的异步处理方式?

Android中的异步处理方式可以采用

a:Handler+Thread

b:异步任务Asynctask

c:Thread+回调

d:a+c

问题2如何使用Handler以及在使用过程中如何避免Handler引起的内存泄露?

问题4 Handler通过sendMessage以及post Runable对象有什么区别

public class ThreadHandlerActivity extends Activity {private static final int MSG_SUCCESS = 0;// 获取图片成功的标识private static final int MSG_FAILURE = 1;// 获取图片失败的标识private ImageView mImageView;private Button mButton;private Thread mThread;private Handler mHandler;@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.thread_layout);mHandler = new MyHandler(this);mImageView = (ImageView) findViewById(R.id.imageView);// 显示图片的ImageViewmButton = (Button) findViewById(R.id.button);mButton.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {if (mThread == null) {mThread = new Thread(runnable);mThread.start();// 线程启动} else {Toast.makeText(getApplication(),getApplication().getString(R.string.thread_started),Toast.LENGTH_LONG).show();}}});}Runnable runnable = new Runnable() {@Overridepublic void run() {// run()在新的线程中运行HttpClient hc = new DefaultHttpClient();HttpGet hg = new HttpGet("http://csdnimg.cn/www/images/csdnindex_logo.gif");// 获取csdn的logofinal Bitmap bm;try {HttpResponse hr = hc.execute(hg);bm = BitmapFactory.decodeStream(hr.getEntity().getContent());} catch (Exception e) {mHandler.obtainMessage(MSG_FAILURE).sendToTarget();// 获取图片失败return;}mHandler.obtainMessage(MSG_SUCCESS, bm).sendToTarget();// 获取图片成功,向ui线程发送MSG_SUCCESS标识和bitmap对象// mImageView.setImageBitmap(bm); //出错!不能在非ui线程操作ui元素            /* *  * 另外一种更简洁的发送消息给ui线程的方法。mHandler.post(new Runnable() {//其实这个Runnable并没有创建什么线程,而是发送了一条消息 * 通过源码可以看出其实post出的runnable对象最终也是转化成message加入到队列@Overridepublic void run() {    // run()方法会在ui线程执行mImageView.setImageBitmap(bm);}});**/}};    /** * @author ss * 问题:如何避免Handler引起的内存泄露 * 1、不使用非静态内部类,继承Handler的时候,要么放在单独的类文件中,要么就使用静态内部类 * why----->在java中,非静态的内部类和匿名内部类都会隐式地持有其外部类的引用,而静态的内部类 * 不会持有外部类的引用。 * 假如在线程中执行如下方法:           mHandler.postDelayed(new Runnable() {           这里的runnable如果不采用匿名内部类,而是在外面声明,则也应该设置成静态                 @Override                 public void run() {                  ...                 } }, 1000 * 60 * 10); 外部调用finish()销毁Activity * 如果我们使用非静态的MyHandler, 当我们的代码执行finish方法时,被延迟的消息会在被处理之前存在于 * 主线程消息队列中10分钟,我们发送一个target为这个Handler的消息到Looper处理的消息队列时,实际上 * 已经发送的消息已经包含了一个Handler实例的引用,只有这样Looper在处理到这条消息时才可以 * 调用Handler#handleMessage(Message)完成消息的正确处理。 * 但是非静态的MyHandler持有外部ThreadHandlerActivity的引用 * 所以这导致了ThreadHandlerActivity无法回收,进行导致ThreadHandlerActivity持有的很多资源都 * 无法回收,这就是我们常说的内存泄露。 *  */private static class MyHandler extends Handler {//2、当你需要在静态内部类中调用外部的Activity时,我们可以使用弱引用来处理。WeakReference<ThreadHandlerActivity> thisLayout;MyHandler(ThreadHandlerActivity layout) {thisLayout = new WeakReference<ThreadHandlerActivity>(layout);}public void handleMessage(Message msg) {// 此方法在ui线程运行final ThreadHandlerActivity theLayout = thisLayout.get();if (theLayout == null) {return;}switch (msg.what) {case MSG_SUCCESS:theLayout.mImageView.setImageBitmap((Bitmap) msg.obj);// imageview显示从网络获取到的logoToast.makeText(theLayout,theLayout.getString(R.string.get_pic_success),Toast.LENGTH_LONG).show();break;case MSG_FAILURE:Toast.makeText(theLayout,theLayout.getString(R.string.get_pic_failure),Toast.LENGTH_LONG).show();break;}}}}

问题3 从源码角度分析MessageQueue,Message,handler,looper,主线程,子thread之间的关系

问题5 如何给一个线程建立消息循环,即如何构建一个looper线程?

public class LooperThreadActivity extends Activity{ /** * MessageQueue,Message,handler,looper,主线程,子thread之间的关系 *  * MessageQueue:消息队列,每个线程最多拥有一个 *  * Message 消息对象 *  * Looper:与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper也只有一个 * MessageQueue不断从MessageQueue中去取消息,交给消息的target属性对应的Handler的dispatchMessage * 去处理 *  * Looper是MessageQueue的管理者。每一个MessageQueue都不能脱离Looper而存在,Looper * 对象的创建是通过prepare函数来实现的。同时每一个Looper对象和一个线程关联。通过调用 * Looper.myLooper()可以获得当前线程的Looper对象 。创建一个Looper对象时,会同时创建 * 一个MessageQueue对象。除了主线程有默认的Looper,其他线程默认是没有MessageQueue对象的 * ,所以,不能接受Message。如需要接受,自己定义 一个Looper对象(通过prepare函数), * 这样该线程就有了自己的Looper对象和MessageQueue数据结构了。  Looper从MessageQueue中 * 取出Message然后,交由Handler的handleMessage进行处理。处理完成后,调用 * Message.recycle()将其放入Message Pool中。 * */           private final int MSG_HELLO = 0;      private Handler mHandler;           @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.main);          new CustomThread().start();//新建并启动CustomThread实例                    findViewById(R.id.send_btn).setOnClickListener(new OnClickListener() {                            @Override              public void onClick(View v) {//点击界面时发送消息                   String str = "hello";                  Log.d("Test", "MainThread is ready to send msg:" + str);                  mHandler.obtainMessage(MSG_HELLO, str).sendToTarget();//发送消息到CustomThread实例                                 }          });                }            class CustomThread extends Thread {          @Override         public void run() {              //建立消息循环的步骤              Looper.prepare();//1、初始化Looper              //Handler的构造方法,会首先得到当前线程中保存的Looper实例,            //进而与Looper实例中的MessageQueue关联。            mHandler = new Handler(){//2、绑定handler到CustomThread实例的Looper对象                   public void handleMessage (Message msg) {//3、定义处理消息的方法                       switch(msg.what) {                      case MSG_HELLO:                         Log.d("Test", "CustomThread receive msg:" + (String) msg.obj);                      }                  }              };              Looper.loop();//4、启动消息循环           }      }  } 

问题6 Asynctask中有哪些方法,分别如何使用,哪些方法在主线程执行,哪些方法在子线程执行,Asynctask中的参数关系

轻量级的异步抽象类
定义了三种泛型类型Params,Progress和Result,都是Object类型
onPreExecute() 当任务执行之前开始调用此方法,可以在这里显示进度对话框。
doInBackground(Params...) 此方法在后台线程执行,完成任务的主要工作,通常需要较长的时间。在执行过程中可以调用publicProgress(Progress...)来更新任务的进度。
onProgressUpdate(Progress...) 此方法在主线程执行,用于显示任务执行的进度。
onPostExecute(Result) 此方法在主线程执行,任务执行的结果作为此方法的参数返回。
onCancelled() 用户调用取消方法时,将回调该函数。

实例必须在UI主线程中创建
execute方法必须在主线程中调用
永远不要手动的调用
onPreExecute(),
onPostExecute(Result),
doInBackground(Params...),
onProgressUpdate(Progress...)这几个方法
一个AsyncTask实例只能被Execute执行一次,否则多次Execute时将会出现异常
配置了5个可用线程,
Executor(Executors.newCachedThreadPool())

/**   * 生成该类的对象,并调用execute方法之后   * 首先执行的是onProExecute方法   * 其次执行doInBackgroup方法     */  public class ProgressBarAsyncTask extends AsyncTask<Integer, Integer, String> {        private TextView textView;      private ProgressBar progressBar;                  public ProgressBarAsyncTask(TextView textView, ProgressBar progressBar) {          super();          this.textView = textView;          this.progressBar = progressBar;      }          /**       * 这里的Integer参数对应AsyncTask中的第一个参数        * 这里的String返回值对应AsyncTask的第三个参数       * 该方法并不运行在UI线程当中,主要用于异步操作,所有在该方法中不能对UI当中的空间进行设置和修改       * 但是可以调用publishProgress方法触发onProgressUpdate对UI进行操作       */      @Override      protected String doInBackground(Integer... params) {      //Integer... params代表0或者N个Integer类型的值        NetOperator netOperator = new NetOperator();          int i = 0;          for (i = 10; i <= 100; i+=10) {              netOperator.operator();              publishProgress(i);          }         //这里面params[0]即为execute(1000)中的1000        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);      }    }  /** * 模拟网络耗时 * */class NetOperator {        public void operator(){        try {              //休眠1秒              Thread.sleep(1000);          } catch (InterruptedException e) {              // TODO Auto-generated catch block              e.printStackTrace();          }      } }  
public class AsyncTaskActivity extends Activity { private Button startTaskBtn;      private ProgressBar progressBar;      private TextView progress_info;            @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.asyntask_layout);                    startTaskBtn = (Button)findViewById(R.id.startTaskBtn);          progressBar = (ProgressBar)findViewById(R.id.progressBar);                progress_info = (TextView)findViewById(R.id.progress_info);                startTaskBtn.setOnClickListener(new OnClickListener() {            @Override              public void onClick(View v) {                  ProgressBarAsyncTask asyncTask = new ProgressBarAsyncTask(progress_info, progressBar);                  asyncTask.execute(1000);              }          });      }  }  

问题7 Asynctask与使用Handler+thread的优缺点对比(区别)

采用线程+Handler实现异步处理时,当每次执行耗时操作都创建一条新线程进行处理,性能开销会比较大, 如果耗时操作执行的时间比较长,就有可能同时运行着许多线程,系统终将不堪重负. 为了提高性能我们使用AsyncTask实现异步处理,事实上其内部也是采用线程+handler来实现异步处理的.只不过是其内部使用了JDK5提供的线程池技术,有效的降低了线程创建数量及限定了同时运行的线程数,还有一些针对性的对池的优化操作.

AsyncTask规定同一时刻能够运行的线程数为5个,线程池总大小为128。也就是说当我们启动了10个任务时,只有5个任务能够立刻执行,另外的5个任务则需要等待,当有一个任务执行完毕后,第6个任务才会启动,以此类推。而线程池中最大能存放的线程数是128个,当我们尝试去添加第129个任务时,程序就会崩溃。

因此在3.0版本中AsyncTask的改动还是挺大的,在3.0之前的AsyncTask可以同时有5个任务在执行,而3.0之后的AsyncTask同时只能有1个任务在执行。为什么升级之后可以同时执行的任务数反而变少了呢?这是因为更新后的AsyncTask已变得更加灵活,如果不想使用默认的线程池,还可以自由地进行配置


AsyncTask
优点:
1.简单,快捷,只需要在doInBackground ()中处理业务,在onPostExecute()方法中更新UI,代码层次结构简单
2.当相同异步任务的数据特别庞大,AsyncTask这种线程池结构的优势会充分体现出来
缺点:
1.AsyncTask类包含一个全局静态的线程池,默认配置了5个可用线程,如果超过5个线程则会进入到缓冲队列中等待。缓冲队列最多有128个等中的线程
2.AsyncTask可能存在新开大量线程消耗系统资源和导致应用FC(Force Close)的风险
3.AsyncTask在处理大量多种不同异步任务的时候显得不适合。

Handler
优点:
Handler原理是仅仅就是发送了一个消息队列,并没有新开线程带来的资源消耗,对于纷繁复杂的不同小异步任务处理起来相对简单。
缺点:
相对AsyncTask来说显得代码过多,过于臃肿,结构过于复杂,层次不明朗


Demo下载地址:

https://github.com/feifei003603/CustomAsyncTask.git


终于整理完了,好困,觉得有用的小伙伴给个评论吧,有什么不对的地方欢迎拍砖,欢迎补充!

更多相关文章

  1. Android(安卓)Handler 机制
  2. Flutter 与 Android(安卓)的交互
  3. Android(安卓)Studio—— 关于在Android(安卓)Studio中使用Asset
  4. Android(安卓)网络处理
  5. Android(安卓)中 startService()启动service的过程分析
  6. 说说 Android(安卓)的内容提供器(ContentResolver )
  7. Android(安卓)Tab切换之Fragment方法
  8. Android视图绘制流程解析(二)
  9. Android(安卓)MediaPlayer和VideoView的使用

随机推荐

  1. Android颜色渐变的分隔线(ListView)
  2. android 中 checkBox 的使用
  3. Android:霓虹灯
  4. 修改进度条ProgressBar颜色
  5. 三步搞定:Vue.js调用Android原生方法
  6. Android拦截、监听系统返回键事件
  7. 2011.07.11——— android 自定义toast
  8. 读取Android设备的MAC地址
  9. Android FFMPEG音视频开发(四)
  10. 使用FragmentTabHost时,tabwidget被framen