热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

android动态创建线程池,Android多线程处理之多线程详解

handler.post(r)其实这样并不会新起线程,只是执行的runnable里的run()方法,却没有执行start()方法,所以ru

handler.post(r)其实这样并不会新起线程,只是执行的runnable里的run()方法,却没有执行start()方法,所以runnable走的还是UI线程。

1.如果像这样,是可以操作ui,但是run还是走在主线程,见打印出来的Log线程名字是main,说明是主线程。

这就是为什么可以直接在run方法里操作ui,因为它本质还是ui线程

handler.post(new Runnable(){

public void run(){

Log.e("当前线程:",Thread.currrentThread.getName());//这里打印de结果会是main

setTitle("哈哈");

}

});

2.通过HandlerThread获取到looper却是可以新起线程,但是在这里的run方法里操作ui是不可能的,但是这显然有个缺点,如果你执行多次post(r)方法其实走的还是HandlerThread线程。假如你执行5次,n次,其实还是一次并且它们是串行的。假如下载5张图片,你会看到图片是下完第一张,才会去下第二张。

实践证明,只有是拥有主线程looper的handler才可以操作ui,而在主线程操作ui可以在handler的handlerMessage()方法中操作Ui,也可以在handler的post(r)的run方法里操作Ui.

HandlerThread ht = new HandlerThread("handler thread");

ht.start();

handler = new Handler(ht.getLooper());

handler.post(new Runnable(){//这里run()方法其实还是在等ht.start()调用

public void run(){

Log.e("当前线程:",Thread.currrentThread.getName());//这里打印的会是handler thread

setTitle("哈哈");//这样必定报错

//android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

}

});

这样该怎么办呢,呵呵,可以无参构建一个handler。用这个handler来发送消息和处理消息,用上面的handler来开启新线程。

mainHandler = new Handler(){

protecket void handlerMessage(Message msg){

setTitle("哈哈");//这样就不会报错啦

}

}

handler.post(new Runnable(){//这里run()方法其实还是在等ht.start()调用

public void run(){

Log.e("当前线程:",Thread.currrentThread.getName());//这里打印的会是handler thread

mainHandler.sendEmpetMessage();//用mainHandler来发送消息

//setTitle("哈哈");//这样必定报错

//android.view.ViewRoot$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

}

});

打印Log:

5eb2316008fb6c893dddc33d84a5cdec.png

3.其实第2个方法显得麻烦而且低效,用了2个handler,一个用来发起线程,一个用于处理消息。发起线程的handler必须拥有looper,所以还要实例化一个HanderThread;而处理消息的handler则不需要looper,因为它默认拥有主线程的looper,所以可以在这个handler处理ui。

其实可以只需要实例化一个handler,在主线程里构建一个无参的handler,然后由它发送和处理消息。而创建线程的任务就不用handler了,直接用new Thread(r).start();然后在r的run()方法里面处理逻辑事务。

用这样的模式下载5张图片,你就可能不会看到图片一张挨着一张展示出来,可能第2张先出来,也可能同时出来3张,5条线程很随机的。

private void loadImagesByThread(final String url,final int id){//通过Thread来new 出多个线程

new Thread(new Runnable(){

@Override

public void run() {

// TODO Auto-generated method stub

Log.e("当前线程:", ""+Thread.currentThread().getName());

Drawable drawable = null;

try {

drawable = Drawable.createFromStream(new URL(url).openStream(), "image.gif");

} catch (MalformedURLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

Message msg = mainHandler.obtainMessage();

msg.what = 2012;

msg.arg1 = id;

msg.obj = drawable;

msg.sendToTarget();

}

}).start();

}

打印Log:

ae12aa99e6c732d20b64ea3d24163afd.png

4.AsyncTask

用异步任务架构多任务模型其实也不是很健壮,得创建多个AsyncTask实例。一个AsyncTask仅执行一次,不能重复执行,快餐类的线程,一次用完。

实现AsyncTask子类,最重要的两个方法,一个是doInBackground(params);一个是onPostExecute(result)。在doInBackground()方法里处理耗时事务,并把结果返回,返回的值将在onPostExecute方法作为参数,然后就可以在onPostExecute()把结果展示在ui上面了。

步骤:

①实例化AsyncTask:

实例化AsyncTask然后通过task.exec(pamas);传进去参数,这个参数列表是动态的,可以是一个也可以使多个,长度可变。

AsyncTask,第一个参数会传进去这个方法doInBackground(params),第二个参数是数据更新的值,第三个是处理事务返回的结果。

②onPreExecute方法:

这个方法没有参数,也没有返回值,可以在这个方法里,做一些提醒。比如show一个Dialog,或者弹个Toast告诉用户开始下载啦。

③doInBackground(params)方法:

进入AsyncTask内部结构,首先将执行reslut doInBackground(params)方法,这个方法将处理耗时事务,exec()的参数将会传进这个方法做参数,而返回值将会作为onPostExecute()的参数。如果要更新进度的话,需执行publicProgress()方法。

④onProgressUpdate(values)方法:

这个方法的参数必须在doInBackground()方法里执行publicProgress()方法,这个方法将会把参数传递进onProgressUpdate()方法里,然后可以在这个方法做一些ui上的更新展示,比如进度条的值就可以通过这个values值动态改变。

⑤onPostExecute(result)方法:

这里就是事务处理完毕的走的方法,doInBackground方法执行的结果将传到这里,如果这个方法返回了数据。在这个方法里可以处理Ui,可以把处理完的数据展示在ui上。比如图片啊,文字啊,一切你想要的结果。

private void loadImageByAsyncTask(final String url,final int id){//构建异步任务,这样就不用handler来处理消息了

DownloadTask task = new DownloadTask();

task.execute(""+id,url);//AsyncTask不可重复执行

}

class DownloadTask extends AsyncTask{

int id;

@Override

protected Drawable doInBackground(String... params) {//params保存url和控件id两个数据

// TODO Auto-generated method stub

Log.e("当前线程:", ""+Thread.currentThread().getName());

Drawable drawable = null;

this.id = Integer.parseInt(params[0]);

try {

drawable = Drawable.createFromStream(new URL(params[1]).openStream(), "image.gif");

} catch (MalformedURLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return drawable;

}

@Override

protected void onPostExecute(Drawable result) {

// TODO Auto-generated method stub

super.onPostExecute(result);

((ImageView)MainActivity.this.findViewById(id)).setImageDrawable(result);

}

@Override

protected void onPreExecute() {

// TODO Auto-generated method stub

super.onPreExecute();

}

@Override

protected void onProgressUpdate(Integer... values) {

// TODO Auto-generated method stub

super.onProgressUpdate(values);

}

}

这里打印的log

e0ae1155119ac0cc6ea56313686fd170.png

5.ExecutorServie线程池

通过Executors的静态方法来创建,一般有三种:

1.单线程 :Executors.newSingleThreadExecutor();

2.固定数量线程 :Executors.newFixedThreadPool();

3.动态线程 :Executors.newCachedThreadPool();

这里我们用固定5个线程来应用,使用方法是创建ExecutorService对象,然后执行submit(r)可以发起一个Runnable对象。用线程池来管理的好处是,可以保证系统稳定运行,适用与有大量线程,高工作量的情景下使用,假如要展示1000张图片如果创建1000个线程去加载,保证系统会死掉。用线程池就可以避免这个问题,可以用5个线程轮流执行,5个一组,执行完的线程不直接回收而是等待下次执行,这样对系统的开销就可以减小不少。

private void loadImagesByExecutors(final String url,final int id){

service.submit(new Runnable(){

@Override

public void run() {

// TODO Auto-generated method stub

Log.e("当前线程:", ""+Thread.currentThread().getName());

try {

final Drawable drawable = Drawable.createFromStream(new URL(url).openStream(), "image.gif");

mainHandler.post(new Runnable(){

@Override

public void run() {//这将在主线程运行

// TODO Auto-generated method stub

((ImageView)MainActivity.this.findViewById(id)).setImageDrawable(drawable);

}

});

} catch (MalformedURLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

});

}

Log:

a017c06d2675846e490c91d21c71760c.png

其实可能没有说清楚,第一种不算多线程。

1.loadImagesByHandler()是通过Handler.post()方法,构建两个Handler进行通信。

2.loadImagesByThread(),这个是直接new Thread()发起线程,在主线程的handler处理消息

3.loadImageByAsyncTask(),这个用的是异步任务,所有实现在它的内部结构里,可以在里头操作Ui.

4.loadImagesByExecutors()用的是线程池,使得线程可控,保证稳定运行。

其实常用的就是后三种,第二个用法灵活,简单,但不适宜大数量任务;第三个一般适用于单个任务,一次性任务;第四个一般用于大数量,高密度执行的使用情景,比如批量加载图片,批量下载文件等。

看一眼图吧:

c37e7306b35dd6e609008c7c060ea478.png

全部源码:

package com.bvin.exec;

import java.io.IOException;

import java.net.MalformedURLException;

import java.net.URL;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import android.app.Activity;

import android.graphics.drawable.Drawable;

import android.os.AsyncTask;

import android.os.Bundle;

import android.os.Handler;

import android.os.HandlerThread;

import android.os.Message;

import android.util.Log;

import android.view.View;

import android.widget.Button;

import android.widget.ImageView;

public class MainActivity extends Activity {

/** Called when the activity is first created. */

private Handler handler ;

private Button bt;

private Handler mainHandler = new Handler(){

@Override

public void handleMessage(Message msg) {

// TODO Auto-generated method stub

super.handleMessage(msg);

if(msg.what == 2012){

//只要在主线程就可以处理ui

((ImageView)MainActivity.this.findViewById(msg.arg1)).setImageDrawable((Drawable)msg.obj);

}

}

};

private ExecutorService service = Executors.newFixedThreadPool(5);

@Override

public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.main);

initViews();

HandlerThread ht = new HandlerThread("down image thread");

ht.start();

handler = new Handler(ht.getLooper()){//如果有了looper那么这个handler就不可以处理ui了

@Override

public void handleMessage(Message msg) {

// TODO Auto-generated method stub

super.handleMessage(msg);

}

};

}

private void initViews(){

bt = (Button)findViewById(R.id.bt);

bt.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

// TODO Auto-generated method stub

loadImagesByExecutors("http://news.baidu.com/z/resource/r/image/2012-11-23/23c1625aca99f02c50d8e510383a34e7.jpg",R.id.iv1);

loadImagesByExecutors("http://news.baidu.com/z/resource/r/image/2012-11-23/c4698d97ef6d10722c8e917733c7beb3.jpg",R.id.iv2);

loadImagesByExecutors("http://news.baidu.com/z/resource/r/image/2012-11-23/f332ffe433be2a3112be15f78bff5a40.jpg",R.id.iv3);

loadImagesByExecutors("http://news.baidu.com/z/resource/r/image/2012-11-23/6ff8a9c647a1e80bc602eeda48865d4c.jpg",R.id.iv4);

loadImagesByExecutors("http://news.baidu.com/z/resource/r/image/2012-11-23/f104d069f7443dca52a878d779392874.jpg",R.id.iv5);

}

});

}

private void loadImagesByHandler(final String url,final int id){//通过拥有looper的handler.post(runnable),新建线程

handler.post(new Runnable(){//如果handler没有Looper那么它就不能构建新线程了

@Override

public void run() {

// TODO Auto-generated method stub

Log.e("当前线程:", ""+Thread.currentThread().getName());

Drawable drawable = null;

try {

drawable = Drawable.createFromStream(new URL(url).openStream(), "image.gif");

} catch (MalformedURLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

//SystemClock.sleep(2000);

//((ImageView)MainActivity.this.findViewById(id)).setImageDrawable(drawable);

Message msg = mainHandler.obtainMessage();

msg.what = 2012;

msg.arg1 = id;

msg.obj = drawable;

msg.sendToTarget();

}

});

}

private void loadImagesByThread(final String url,final int id){//通过Thread来new 出多个线程

new Thread(new Runnable(){

@Override

public void run() {

// TODO Auto-generated method stub

Log.e("当前线程:", ""+Thread.currentThread().getName());

Drawable drawable = null;

try {

drawable = Drawable.createFromStream(new URL(url).openStream(), "image.gif");

} catch (MalformedURLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

Message msg = mainHandler.obtainMessage();

msg.what = 2012;

msg.arg1 = id;

msg.obj = drawable;

msg.sendToTarget();

}

}).start();

}

private void loadImageByAsyncTask(final String url,final int id){//构建异步任务,这样就不用handler来处理消息了

DownloadTask task = new DownloadTask();

task.execute(""+id,url);//AsyncTask不可重复执行

}

private void loadImagesByExecutors(final String url,final int id){

service.submit(new Runnable(){

@Override

public void run() {

// TODO Auto-generated method stub

Log.e("当前线程:", ""+Thread.currentThread().getName());

try {

final Drawable drawable = Drawable.createFromStream(new URL(url).openStream(), "image.gif");

mainHandler.post(new Runnable(){

@Override

public void run() {//这将在主线程运行

// TODO Auto-generated method stub

((ImageView)MainActivity.this.findViewById(id)).setImageDrawable(drawable);

}

});

} catch (MalformedURLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

}

});

}

class DownloadTask extends AsyncTask{

int id;

@Override

protected Drawable doInBackground(String... params) {//params保存url和控件id两个数据

// TODO Auto-generated method stub

Log.e("当前线程:", ""+Thread.currentThread().getName());

Drawable drawable = null;

this.id = Integer.parseInt(params[0]);

try {

drawable = Drawable.createFromStream(new URL(params[1]).openStream(), "image.gif");

} catch (MalformedURLException e) {

// TODO Auto-generated catch block

e.printStackTrace();

} catch (IOException e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

return drawable;

}

@Override

protected void onPostExecute(Drawable result) {

// TODO Auto-generated method stub

super.onPostExecute(result);

((ImageView)MainActivity.this.findViewById(id)).setImageDrawable(result);

}

@Override

protected void onPreExecute() {

// TODO Auto-generated method stub

super.onPreExecute();

}

@Override

protected void onProgressUpdate(Integer... values) {

// TODO Auto-generated method stub

super.onProgressUpdate(values);

}

}

}

以上就是对Android 多线程的资料整理,后续继续补充相关知识,谢谢大家对本站的支持!



推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • 生成式对抗网络模型综述摘要生成式对抗网络模型(GAN)是基于深度学习的一种强大的生成模型,可以应用于计算机视觉、自然语言处理、半监督学习等重要领域。生成式对抗网络 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 本文介绍了Android 7的学习笔记总结,包括最新的移动架构视频、大厂安卓面试真题和项目实战源码讲义。同时还分享了开源的完整内容,并提醒读者在使用FileProvider适配时要注意不同模块的AndroidManfiest.xml中配置的xml文件名必须不同,否则会出现问题。 ... [详细]
  • 基于PgpoolII的PostgreSQL集群安装与配置教程
    本文介绍了基于PgpoolII的PostgreSQL集群的安装与配置教程。Pgpool-II是一个位于PostgreSQL服务器和PostgreSQL数据库客户端之间的中间件,提供了连接池、复制、负载均衡、缓存、看门狗、限制链接等功能,可以用于搭建高可用的PostgreSQL集群。文章详细介绍了通过yum安装Pgpool-II的步骤,并提供了相关的官方参考地址。 ... [详细]
  • PHP图片截取方法及应用实例
    本文介绍了使用PHP动态切割JPEG图片的方法,并提供了应用实例,包括截取视频图、提取文章内容中的图片地址、裁切图片等问题。详细介绍了相关的PHP函数和参数的使用,以及图片切割的具体步骤。同时,还提供了一些注意事项和优化建议。通过本文的学习,读者可以掌握PHP图片截取的技巧,实现自己的需求。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
author-avatar
钓鱼翁玩围脖
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有