2011-03-27 14:32:00 wayne512 阅读数 277
  • 精通Android多线程视频教程

    通过本课程的学习,让你透彻精通Android多线程编程,课程内容包括: 1.UI线程和非UI线程 2.使用Handler发送post请求 3.使用Handler处理Message消息 4.Handler、Looper、MessageQueue三者的关系 5.UI线程Handler和工作线程Handler 6.使用HandlerThread 7.更新UI的方法 8.使用AsyncTask

    4437 人正在学习 去看看 郭宏志

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://myqdroid.blog.51cto.com/2057579/392157
1、问题提出
1)为何需要多线程?
2)多线程如何实现?
3)多线程机制的核心是啥?
4)到底有多少种实现方式?
 
2、问题分析
1)究其为啥需要多线程的本质就是异步处理,直观一点说就是不要让用户感觉到“很卡”。
eg:你点击按钮下载一首歌,接着该按钮一直处于按下状态,那么用户体验就很差。
 
2)多线程实现方式implements Runnable 或 extends Thread
 
3)多线程核心机制是Handler
 
4)提供如下几种实现方式
----1-----Handler
————————————说明1
创建一个Handler时一定要关联一个Looper实例,默认构造方法Handler(),它是关联当前Thread的Looper。
eg:
我们在UI Thread中创建一个Handler,那么此时就关联了UI Thread的Looper!
这一点从源码中可以看出!
精简代码如下:
public Handler() {
        mLooper = Looper.myLooper();
//当前线程的Looper,在Activity创建时,UI线程已经创建了Looper对象
//在Handler中机制中Looper是最为核心的,它一直处于循环读MessageQueue,有
//要处理的Message就将Message发送给当前的Handler实例来处理

        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
//从以上可以看出,一个Handler实例必须关联一个Looper对象,否则出错

        mQueue = mLooper.mQueue;
//Handler的MessageQueue,它是FIFO的吗?不是!我感觉应该是按时间先后排列
//的!Message与MessageQueue到底是啥关系?感兴趣可以研究一下源码!

        mCallback = null;
    }
 
 
在创建一个Handler的时候也可以指定Looper,此时的Looper对象,可以是当前线程的也可以是其它线程的!
Handler只是处理它所关联的Looper中的MessageQueue中的Message,至于它哪个线程的Looper,Handler并不是很关心!
eg:
我们在UI线程中创建了Handler实例,此时传进Worker线程的Looper,此时依然可以进行业务操作!
eg:
--------------------创建工作者线程
private static final class Worker implements Runnable
 {
  private static final Object mLock = new Object() ;
  private Looper mLooper ;
  
  public Worker(String name)
  {
   final Thread thread = new Thread(null,this,name) ;
   thread.setPriority(Thread.MIN_PRIORITY) ;
   thread.start() ;
   
   synchronized(mLock)
   {
    while(mLooper == null)
    {
     try 
     {
      mLock.wait() ;
     } 
     catch (InterruptedException e) 
     {
      e.printStackTrace();
     }
    }
   }
  }
  
  @Override
  public void run() {
   synchronized(mLock)
   {    
    //该方法只能执行一次,一个Thread只能关联一个Looper
       Looper.prepare() ;
       mLooper = Looper.myLooper() ;
       mLock.notifyAll() ;
    }
     Looper.loop() ;
  }
  
  public Looper getLooper()
  {
   return mLooper ;
  }
  
  public void quit()
  {
     mLooper.quit() ;
  }
 }
 
我们可以在UI线程中创建一个Handler同时传入Worker的Looper
eg:
----------------定义自己的Handler
private final class MyHandler extends Handler
 {
  private long id ;
  
  public MyHandler(Looper looper)
  {
   super(looper) ;
  }
  
  @Override
  public void handleMessage(Message msg) {
   switch(msg.what)
   {
   case 100 :
    mTv.setText("" + id) ;
    break ;
   }
  }
 }
 
---------在Activity中创建Handler
this.mWorker = new Worker("workerThread") ;
this.mMyHandler = new MyHandler(this.mWorker.getLooper()) ;
 
---------创建Message
final Message msg = this.mMyHandler.obtainMessage(100);
msg.put("test" , "test") ;
msg.sendToTarget() ;
 
需要注意的是,每一个Message都必须要有自己的Target即Handler实例!
源码如下:
public final Message obtainMessage(int what)
    {
        return Message.obtain(this, what);
    }
 
public static Message obtain(Handler h, int what) {
        Message m = obtain();
        m.target = h;//可以看出message关联了当前的Handler
        m.what = what;
        return m;
    }
 
以上只是作了一点原理性的说明!
 
    我们平时使用Handler主要是用来处理多线程的异步交互问题!
    由于Android规定只有UI线程才能更新用户界面和接受用户的按钮及触摸事件!
那么就必须保证UI线程不可以被阻塞,从而耗时操作必须要开启一个新的线程来处理!
    那么问题就来了,等耗时操作结束以后,如何把最新的数据反馈给用户呢?而我们目前工作Worker线程中,从而不可以进行UI更新。
    那么怎么办呢?必须要把最新的数据传给UI线程能处理的地方!现在就派到Handler出场了!可Handler到底干了啥呢?简要说明如下:
   Activity所在的UI线程在创建的时候,就关联了Looper和MessageQueue,那么我们又在UI线程里创建了自己的Handler,那么Handler是属于UI线程的,从而它是可以和UI线程交互的!
    UI线程的Looper一直在进行Loop操作MessageQueue读取符合要求的Message给属于它的target即Handler来处理!所以啊,我们只要在Worker线程中将最新的数据放到Handler所关联的Looper的MessageQueue中,然而Looper一直在loop操作,一旦有符合要求的Message,就第一时间将Message交给该Message的target即Handler来处理!所以啊,我们在创建Message的时候就应该指定它的target即Handler!
  但我们也可以,new Message() -- > mHandler.sendMessage(msg) ;这是特例!
  如果我们通过obtainMessage()方法获取Message对象,此时Handler就会自动设置Message的target。可以看源码!
 
简单一点说就是:
UI线程或Worker线程提供MessageQueue,Handler向其中填Message,Looper从其中读Message,然后交由Message自己的target即Handler来处理!!最终被从属于UI线程的Handler的handlMessag(Message msg)方法被调用!!
 
这就是Android多线程异步处理最为核心的地方!!
有点罗嗦啊!!
 
*******************************************************************
在UI线程中创建Handler[一般继承HandleMessage(Message msg)]
                                           |
                                           |
            Looper可以属于UI线程或Worker线程
                                           |
                                           |
从属于Looper的MessgeQueue,Looper一直在loop()操作,在loop()中执行msg.target.dispatchMessage(msg);调用Handler的handleMessage(Message msg)
                                           |
                                           |
在 Worker线程中获取Message,然后通过Handler传入MessageQueue
*******************************************************************
 
-----------------在创建一个Looper时,就创建了从属于该Looper的MessageQueue
 private Looper() {
        mQueue = new MessageQueue();
        mRun = true;
        mThread = Thread.currentThread();
    }
 
----2-----View
post(Runnable action)
postDelay(Runnable action , long miliseconds)
 
-----3-----Activity
runOnUiThread(Runnable action)
该方法实现很简单:
public final void runOnUiThread(Runnable action) {
         if (Thread.currentThread() != mUiThread) {
             //如果当前线程不是UI线程
             mHandler.post(action);
         } else {
             action.run();
         }
      }
其中:
 mUiThread = Thread.currentThread() ;
 mHandler = new Handler()    
 
-----4-----AsyncTask<Params,Progress,Result>
Params,Progress,Result都是数据类型,
Params要处理的数据的类型
Progress处理进度的类型
Result处理后返回的结果
 
它是一个异步处理的简单方法!
方法的执行顺序:
1)
onPreExecute() --在UI线程中执行,作一些初始化操作
 
2)
doInBackground(Params... params) --在Worker线程中执行,进行耗时的后台处理,在该方法中可以调用publishProgress(Progress progress) 进行进度处理
 
3)
onProgressUpdate(Progress progress) --在UI线程中执行,进行进度实时处理
 
4)onPostExecute(Result result) --在UI线程中执行, 在doInBackground(Params ... params)返回后调用
 
5)
onCancelled() --在UI线程中执行,在AsyncTask实例调用cancle(true)方法后执行,作一些清理操作
 
几点注意:
AsyncTask必须在UI线程中创建,
asyncTask.execute(Params... params) ;在UI线程中执行,且只能执行一次
要想再次调用execute(Params... params),必须重新创建AsyncTask对象
 
后3种方法本质上都是利用Handler来实现的!
 
3、一点说明
1)具体使用还是要自己去摸索!只作抛砖吧!
2)一些使用的注意之处可以参看API Reference!
2)最好是跟踪分析一下源码!

本文出自 “苗运齐的博客” 博客,请务必保留此出处http://myqdroid.blog.51cto.com/2057579/392157

2018-05-08 16:49:12 weixin_40600325 阅读数 666
  • 精通Android多线程视频教程

    通过本课程的学习,让你透彻精通Android多线程编程,课程内容包括: 1.UI线程和非UI线程 2.使用Handler发送post请求 3.使用Handler处理Message消息 4.Handler、Looper、MessageQueue三者的关系 5.UI线程Handler和工作线程Handler 6.使用HandlerThread 7.更新UI的方法 8.使用AsyncTask

    4437 人正在学习 去看看 郭宏志

Android多线程编译的问题一直都是HR青睐的面试题,因为多线程编译在Android开发过程中占有一定的地位。如果我们把一个耗时操作放在主线程中,就会导致主线程被阻塞,影响软件的正常使用。所以我们一般会把一个耗时任务放在一个子线程中,那如果是多个耗时任务呢?所以就有了多线程编译。

Android中是怎么创建一个线程的呢?办法有三个:

1.新建一个类继承自Thread,然后重写父类的run()方法,然后把耗时逻辑编写在里面,如:

class MyThread extends Thread{
        @Override
        public void run() {
            super.run();
            //处理逻辑写这里
        }
    }

然后我们通过new一个MyThread的实例,调用start()方法:new MyThread().start();

2.我们还可以通过去实现Runnable接口的方法去定义一个线程,如:

 class MyThread implements Runnable{
        @Override
        public void run() {
            //处理逻辑写这里
        }
    }

因为是这个方法是实现了接口,所以启动线程的方法有所调整:

MyThread  mythread = new MyThread()

new Thread(mythread).start();

3.最常见的方法就是使用匿名类去创建一个线程,如:

      new Thread(new Runnable() {
            @Override
            public void run() {
                //处理逻辑写这里
            }
        }).start();

       通过上面的学习你已经对线程的创建和使用有了一定的了解,那么你是否知道在Android中,UI的更新是不能在子线程中进行的。Android的UI线程是不安全的,我们要更新应用程序的UI元素,必须在主线程中进行,否侧就会出现异常。那么我们怎么样才能在子线程中进行UI操作呢?

        对于以上的问题Android提供了一套异步消息处理机制,完美地解决了在子线程中进行UI操作,那就是大家都应该有所耳闻的异步消息处理机制。下面是异步消息处理机制流程示意图:


此图是百度找的,如若有侵权请联系版主删除图片

        从图中可以看出Android异步消息处理机制主要由4个部分组成:Message、Handler、MessageQueue、Looper。现在我们来认识认识这四个部分先。

1.Message

        Message是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。它能使用的字段有what、arg1、arg2、obj这四个,用法如:Message.what。

2.Handler

        Handler主要是用于发送和处理消息的。Handler提供sendMessage()方法让开发者发送信息并最终传递到Handler的handlerMessage()方法中。

3.MessageQueue

        MessageQueue主要是用于存放所有通过Handler发送的消息,它相当于一个消息队列。要记住的是每个线程只会有一个MessageQueue对象。

4.Looper

        Looper是用于管理MessageQueue,它提供了一个loop()方法无限循环的在MessageQueue中取出消息,并把消息传递到Handler的handlerMessage()方法中。而且每个线程中也只会有一个Looper对象。

        现在我们应该对这四个部分有了初步的了解了,现在我们来捋捋Android异步消息处理机制的整个流程。首先要在主线程当中创建一个Handler对象,并重写handlerMessage()方法。然后当子线程中有需要进行UI操作时,就要创建一个Message对象,并通过Handler对象的sendMessage()方法把这条消息发送出去。之后这调消息会存储在MessageQueue的队列中等待被处理,而Looper此时会一直尝试从MessageQueue中取出待处理的消息,最后分发回Handler的handlerMessage()方法中。但是由于Handler是在主线程中创建的,所以此时handlerMessage()方法中的代码也会在主线程中运行,这样就能达到了我们想要的效果,这就是异步消息处理的核心思想。弄懂了原理后我们在代码上实现一下看看效果是不是如我们所想,上个最简单的例子,我们做一个能通过点击按钮然后改变文本控件显示的内容的功能来试试。上代码图:

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    final static int CHANGE_TEXT = 1;//先定义一个整型变量来表示更新TextView这个动作
    private Button bt_change;
    private TextView tv_texr;
    Handler handler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case CHANGE_TEXT:
                    tv_texr.setText("我变了");//接收到消息后改变textview里面的文本
            }
        }
    };//创建Handler对象在主线程
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bt_change = (Button) findViewById(R.id.bt_change);
        tv_texr = (TextView)findViewById(R.id.tv_text);
        bt_change.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()){
            case R.id.bt_change:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();//实例化一个Message对象
                        message.what = CHANGE_TEXT;//通过what字段存储消息
                        handler.sendMessage(message);//使用handler对象的sendMessage方法把这条消息发送到handlerMessage方法中
                    }
                }).start();//通过创建一个匿名类的方式去创建一个子线程
                break;

        }
    }


}

        上面代码的基本思路是:先定义一个整型变量CHANGE_TEXT,用于表示更新TextView的文本内容这个动作。然后实例化一个Handler对象,并重写handlerMessage()方法,对发送的Message的what字段进行处理,如果等于CHANGE_TEXT,就将TextView显示的文本内容进行相应的变化。那我们的Message是从哪来的呢?这里我实现了点击监听事件的接口,当我们点击bt_change这个按钮时,我们就以匿名类的方式创建一个子线程,并在子线程中实例化一个Message对象。之后我们把Message对象的what字段的值指向了CHANGE_TEXT,然后调用Handler的sendMessage()方法把这个消息发送出去。这样就完美的解决了在子线程中更新UI界面的功能了。

        当然大家如果有兴趣的话,其实Android还提供了另外一个可以处理异步消息的工具——AsyncTask,你们可以去了解了解一下这个工具。它不需要你去深入了解异步消息处理机制,也可以在子线程切换到主线程。但是它其实实现的原理都一样,只是Android帮我们做好了封装,我们可以直接使用它而不需要懂其中的运行原理,我在这里就不详细介绍了。

转载的小伙伴请表明原创出处:原创链接


2017-03-21 20:16:18 CodeFarmerCXY 阅读数 7418
  • 精通Android多线程视频教程

    通过本课程的学习,让你透彻精通Android多线程编程,课程内容包括: 1.UI线程和非UI线程 2.使用Handler发送post请求 3.使用Handler处理Message消息 4.Handler、Looper、MessageQueue三者的关系 5.UI线程Handler和工作线程Handler 6.使用HandlerThread 7.更新UI的方法 8.使用AsyncTask

    4437 人正在学习 去看看 郭宏志
Android 多线程详解
一.多线程介绍
在学习多线程之前我们首先要了解几个与多线程有关的概念。
进程:进程指正在运行的程序。确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能。
线程:线程是进程中的一个执行单元,负责当前进程中程序的执行,一个进程中至
少有一个线程。一个进程中是可以有多个线程的,这个应用程序也可以称之为多线程程序。
简而言之:一个程序运行后至少有一个进程,一个进程中可以包含多个线程。
什么是多线程呢?即就是一个程序中有多个线程在同时执行。
单线程程序:若有多个任务只能依次执行。
多线程程序:若有多个任务可以同时执行。
二.获取线程名称
static Thread [currentThread()] 返回对当前正在执行线程对象的引用
String [getName()] 返回该线程的名称
Thread.currentThread() 获取当前线程对象
Thread.currentThread().getName(); 获取当前线程对象的名称
主线程的名称:main;自定义的线程:Thread-0,线程多个时,数字顺延。如Thread-1......
进行多线程编程时,不要忘记了Java程序运行是从主线程开始,main方法就是主线程的线程执行内容。

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //实例化对象
        MyThread myThread=new MyThread("陈箫阳");
        //开启子线程,调用Start方法切记不要调用Run方法
        myThread.start();
        for(int x=0; x<50; x++){
            System.out.println("main"+x);
        }
        //获取子线程名字
        System.out.println(myThread.getName());
        //获取主线程名字
        System.out.println(Thread.currentThread().getName());
    }
}
三.创建线程
创建线程的方式有三种:
1.继承Thread类
2.实现Runnable接口
3.实现Callable接口
Thread类
构造方法摘要:
Thread()分配新的Thread对象
Thread(String name)分配新的Thread对象,将指定的name作为其线程名称
常用方法
voidstart() 使该线程开始执行,Java虚拟机调用该线程的run方法
void  run()  该线程要执行的操作,如循环输出打印变量的值
static void  sleep(毫秒数)   在指定的毫秒数内让当前正在执行的线程休眠,暂停执行
创建新执行线程有两种方法。
1.一种方法是将类声明为 Thread 的子类。该子类应重写 Thread 类的 run 方法。创建对象,开启线程。run方法相当于其他线程的main方法。
2.另一种方法是声明一个实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。
创建线程的步骤:
1 定义一个类继承Thread。
2 重写run方法。
3 创建子类对象,就是创建线程对象。
4 调用start方法,开启线程并让线程执行,同时还会告诉jvm去调用run方法。
测试类
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //实例化对象
        MyThread myThread=new MyThread("陈箫阳");
        //开启子线程,调用Start方法切记不要调用Run方法
        myThread.start();
        for(int x=0; x<50; x++){
            System.out.println("main"+x);
        }
        //获取子线程名字
        System.out.println(myThread.getName());
        //获取主线程名字
        System.out.println(Thread.currentThread().getName());
    }
}
自定义线程类
public class MyThread extends Thread{

    public MyThread(String name){
        super(name);
    }

    @Override
    public void run() {
        for(int x=0; x<50; x++){
            System.out.println("Thread"+x);
            Log.i("thread","==="+x);
        }
    }
}
创建线程的目的是什么?
是为了建立程序单独的执行路径,让多部分代码实现同时执行。也就是说线程创建并执行需要给定线程要执行的任务。
对于之前所讲的主线程,它的任务定义在main函数中。自定义线程需要执行的任务都定义在run方法中。
Thread类run方法中的任务并不是我们所需要的,只有重写这个run方法。既然Thread类已经定义了线程任务的编写位置(run方法),那么只要在编写位置(run方法)中定义任务代码即可。所以进行了重写run方法动作。
Runnable接口
创建线程的另一种方法是声明实现 Runnable 接口的类。该类然后实现 run 方法。然后创建Runnable的子类对象,传入到某个线程的构造方法中,开启线程。

方法摘要
void [run()] 使用实现接口Runnable的对象创建一个线程时,启动该线程将导致在独立执行的线程中调用对象的run
创建线程的步骤。
1、定义类实现Runnable接口
2、覆盖接口中的run方法
3、创建Thread类的对象
4、将Runnable接口的子类对象作为参数传递给Thread类的构造函数
5、调用Thread类的start方法开启线程
测试类
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建线程执行目标类对象
        Runnable runnable = new MyRunnable();
        //将Runnable接口的子类对象作为参数传递给Thread类的构造函数
        Thread thread = new Thread(runnable);
        Thread thread2 = new Thread(runnable);
        //开启线程
        thread.start();
        thread2.start();
        for (int i = 0; i < 10; i++) {
            System.out.println("main线程:正在执行!" + i);
        }
    }
}
自定义线程执行任务类
public class MyThread implements Runnable {
    //定义线程要执行的run方法逻辑
    @Override
    public void run() {

        for (int i = 0; i < 10; i++) {
            System.out.println("我的线程:正在执行!" + i);
        }
    }
}
实现Runnable的原理
实现Runnable接口,避免了继承Thread类的单继承局限性。覆盖Runnable接口中的run方法,将线程任务代码定义到run方法中。
创建Thread类的对象,只有创建Thread类的对象才可以创建线程。线程任务已被封装到Runnable接口的run方法中,而这个run方法所属于Runnable接口的子类对象,所以将这个子类对象作为参数传递给Thread的构造函数,这样,线程对象创建时就可以明确要运行的线程的任务。
实现Runnable的好处
实现Runnable接口避免了单继承的局限性,所以较为常用。实现Runnable接口的方式,更加的符合面向对象,线程分为两部分,一部分线程对象,一部分线程任务。继承Thread类,线程对象和线程任务耦合在一起。一旦创建Thread类的子类对象,既是线程对象,有又有线程任务。实现runnable接口,将线程任务单独分离出来封装成对象,类型就是Runnable接口类型。Runnable接口对线程对象和线程任务进行解耦。
Callable接口
Callable接口:与Runnable接口功能相似,用来指定线程的任务。其中的call()方法,用来返回线程任务执行完毕后的结果,call方法可抛出异常。
在此处需要用到线程池
线程池概念
线程池,其实就是一个容纳多个线程的容器,其中的线程可以反复使用,省去了
频繁创建线程对象的操作,无需反复创建线程而消耗过多资源。

为什么要使用线程池?
在java中,如果每个请求到达就创建一个新线程,开销是相当大的。在实际使用中,创建和销毁线程花费的时间和消耗的系统资源都相当大,甚至可能要比在处理实际的用户请求的时间和资源要多的多。除了创建和销毁线程的开销之外,活动的线程也需要消耗系统资源。如果在一个jvm里创建太多的线程,可能会使系统由于过度消耗内存或“切换过度”而导致系统资源不足。为了防止资源不足,需要采取一些办法来限制任何给定时刻处理的请求数目,尽可能减少创建和销毁线程的次数,特别是一些资源耗费比较大的线程的创建和销毁,尽量利用已有对象来进行服务。线程池主要用来解决线程生命周期开销问题和资源不足问题。通过对多个任务重复使用线程,线程创建的开销就被分摊到了多个任务上了,而且由于在请求到达时线程已经存在,所以消除了线程创建所带来的延迟。这样,就可以立即为请求服务,使用应用程序响应更快。另外,通过适当的调整线程中的线程数目可以防止出现资源不足的情况。
使用线程池方式--Runnable接口和Callable接口
自定义线程Callable接口
public class MyCallable implements Callable<String>{
    @Override
    public String call() throws Exception {
        Thread.sleep(2000);
        System.out.println("MyCallable");
        System.out.println(Thread.currentThread().getName());
        System.out.println("Callable");
        return null;
    }
}
自定义线程Runnable接口
public class MyRunnable implements Runnable{
    @Override
    public void run() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
        System.out.println("Runnable");
    }
}
测试类
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //创建线程池对象
        ExecutorService service = Executors.newFixedThreadPool(2);//包含2个线程对象
        //创建Runnable实例对象
        MyRunnable myRunnable = new MyRunnable();
        //自己创建线程对象的方式
        //Thread t = new Thread(r);
        //t.start(); ---> 调用MyRunnable中的run()
        //从线程池中获取线程对象,然后调用MyRunnable中的run()
        service.submit(myRunnable);
        //再获取个线程对象,调用MyRunnable中的run()
        service.submit(myRunnable);
        service.submit(myRunnable);
        service.submit(myRunnable);
        //注意:submit方法调用结束后,程序并不终止,是因为线程池控制了线程的关闭。将使用完的线程又归还到了线程池中
        //创建Callable实例对象
        MyCallable myCallable = new MyCallable();
        //从线程池中获取线程对象,然后调用MyRunnable中的run()
        service.submit(myCallable);
        //再获取个教练
        service.submit(myCallable);
        service.submit(myCallable);
        //关闭线程池
        service.shutdown();
    }
}

四.线程的匿名内部类使用
使用线程的内匿名内部类方式,可以方便的实现每个线程执行不同的线程任务操作。
方式1:创建线程对象时,直接重写Thread类中的run方法
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread() {
            @Override
            public void run() {
                for (int x = 0; x < 50; x++) {
                    Log.i("Thread", "===" + x);
                }
            }
        }.start();

        for (int x = 0; x < 50; x++) {
            Log.i("Thread", "===" + x);
        }
    }
}
方式2:使用匿名内部类的方式实现Runnable接口,重新Runnable接口中的run方法
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int x = 0; x < 50; x++) {
                    Log.i("Thread", "===" + x);
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();

        for (int x = 0; x < 50; x++) {
            Log.i("Thread", "===" + x);
        }
    }
}
方式三:直接创建thread对象与Runnable对象
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                for (int x = 0; x < 50; x++) {
                    Log.i("Thread", "===" + x);
                }
            }
        }).start();

        for (int x = 0; x < 50; x++) {
            Log.i("Thread", "===" + x);
        }
    }
}
2015-03-26 16:36:04 soblu_e 阅读数 422
  • 精通Android多线程视频教程

    通过本课程的学习,让你透彻精通Android多线程编程,课程内容包括: 1.UI线程和非UI线程 2.使用Handler发送post请求 3.使用Handler处理Message消息 4.Handler、Looper、MessageQueue三者的关系 5.UI线程Handler和工作线程Handler 6.使用HandlerThread 7.更新UI的方法 8.使用AsyncTask

    4437 人正在学习 去看看 郭宏志

今天测试部发来一个bug,一眼就看出来是ArrayList数组出了问题,测试部的就喜欢没事测试手指的点击速度,一秒钟能点五六次(佩服一下他们的手指),不过也多亏了他们才能发现这个bug。

先说明下问题吧,我正在做的云相册有一个按钮,点击会创建一个gridview,在gridview里面我发现原来的代码很不客气的每次把一个静态变量给重新new了一遍(变量是ArrayList形式的),然后每次创建对象后都会重现开启一个线程进行xml文件下载,下载完毕后会发一个消息,根据消息内容执行ArrayList的内容添加。问题来了,如果同时点很多次,就会出现下载没完成时再点一次,这样静态变量就会重新创建空间(其实是重置首指针,ArrayList是链表形式),当第一次下载结束时发送消息,此时添加的数据就会在新的空间中,而当第二次也下载完毕时还会在新的空间中再次添加一遍。

解决办法:有一个简单的办法,貌似把静态变量换成非静态就OK了(没试过,不过应该可以);二:就是控制多线程,在新创建时kill掉上一个线程

三:(最好的办法)保留第一个线程,再次创建线程时判断第一个是否完成,若完成则重现创建,不完成则不响应创建线程。

2012-07-12 11:28:58 iteye_13045 阅读数 17
  • 精通Android多线程视频教程

    通过本课程的学习,让你透彻精通Android多线程编程,课程内容包括: 1.UI线程和非UI线程 2.使用Handler发送post请求 3.使用Handler处理Message消息 4.Handler、Looper、MessageQueue三者的关系 5.UI线程Handler和工作线程Handler 6.使用HandlerThread 7.更新UI的方法 8.使用AsyncTask

    4437 人正在学习 去看看 郭宏志

  Android是单线程模型,这意味着Android UI操作并不是线程安全的并且这些操作必须在UI线程中执行,所以你单纯的new一个Thread并且start()是不行的,因为这违背了Android的单线程模型。那么如何用好多线程呢?总结一下:

     

事件处理的原则:所有可能耗时的操作都放到其他线程去处理。

  Android中的Main线程的事件处理不能太耗时,否则后续的事件无法在5秒内得到响应,就会弹出ANR对话框。那么哪些方法会在 Main线程执行呢?

  1) Activity的生命周期方法,例如:onCreate()、onStart()、onResume()等

  2) 事件处理方法,例如onClick()、onItemClick()等

  通常Android基类中以on开头的方法是在Main线程被回调的。

  提高应用的响应性,可以从这两方面入手。

  一般来说,Activity的onCreate()、onStart()、onResume()方法的执行时间决定了你的应用首页打开的时间,这里要尽量把不必要的操作放到其他线程去处理,如果仍然很耗时,可以使用SplashScreen。使用SplashScreen最好用动态的,这样用户知道你的应用没有死掉。

 

 当用户与你的应用交互时,事件处理方法的执行快慢决定了应用的响应性是否良好,一般分为同步和异步两种情况:

  1) 同步,需要等待返回结果。例如用户点击了注册按钮,需要等待服务端返回结果,那么需要有一个进度条来提示用户你的程序正在运行没有死掉。一般与服务端交互的都要有进度条,例如系统自带的浏览器,URL跳转时会有进度条。

  2) 异步,不需要等待返回结果。例如微博中的收藏功能,点击完收藏按钮后是否成功执行完成后告诉我就行了,我不想等它,这里最好实现为异步的。

  无论同步异步,事件处理都可能比较耗时,那么需要放到其他线程中处理,等处理完成后,再通知界面刷新。

  这里有一点要注意,不是所有的界面刷新行为都需要放到Main线程处理,例如TextView的setText()方法需要在Main线程中,否则会抛出CalledFromWrongThreadException,而ProgressBar的setProgress()方法则不需要在Main线程中处理。

  当然你也可以把所有UI组件相关行为都放到Main线程中处理,没有问题。可以减轻你的思考负担,但你最好了解他们之间的差别,掌握事物之间细微差别的是专家。把事件处理代码放到其他线程中处理,如果处理的结果需要刷新界面,那么需要线程间通讯的方法来实现在其他线程中发消息给Main线程处理。

 

  3. 如何实现线程间通讯

  在Android中有多种方法可以实现其他线程与Main线程通讯,我们这里介绍常见的两种。

  1) 使用AsyncTask

  AsyncTask是Android框架提供的异步处理的辅助类,它可以实现耗时操作在其他线程执行,而处理结果在Main线程执行,对于开发者而言,它屏蔽掉了多线程和后面要讲的Handler的概念。你不了解怎么处理线程间通讯也没有关系,AsyncTask体贴的帮你做好了。使用他你会发现你的代码很容易被理解,因为他们都有一些具有特定职责的方法,尤其是AsyncTask,有预处理的方法onPreExecute,有后台执行任务的方法doInBackground,有更新进度的方法publishProgress,有返回结果的方法onPostExecute等等,这就不像post这些方法,把所有的操作都写在一个Runnable里。不过封装越好越高级的API,对初级程序员反而越不利,就是你不了解它的原理。当你需要面对更加复杂的情况,而高级API无法完成得很好时,你就杯具了。所以,我们也要掌握功能更强大,更自由的与Main线程通讯的方法:Handler的使用。

 

  2) 使用Handler

  这里需要了解Android SDK提供的几个线程间通讯的类。

  2.1 Handler

  Handler在android里负责发送和处理消息,通过它可以实现其他线程与Main线程之间的消息通讯。

  2.2 Looper

  Looper负责管理线程的消息队列和消息循环

  2.3 Message

  Message是线程间通讯的消息载体。两个码头之间运输货物,Message充当集装箱的功能,里面可以存放任何你想要传递的消息。

  2.4 MessageQueue

  MessageQueue是消息队列,先进先出,它的作用是保存有待线程处理的消息。

  它们四者之间的关系是,在其他线程中调用Handler.sendMsg()方法(参数是Message对象),将需要Main线程处理的事件添加到Main线程的MessageQueue中,Main线程通过MainLooper从消息队列中取出Handler发过来的这个消息时,会回调Handler的handlerMessage()方法。

  除了以上两种常用方法之外,还有几种比较简单的方法

  3) Activity.runOnUiThread(Runnable)

  4) View.post(Runnable)

  View.postDelayed(Runnable, long)

  5) Handler.post

  Handler.postDelayed(Runnable, long)

 

  4. 利用线程池提高性能

 

  这里我们建议使用线程池来管理临时的Thread对象,从而达到提高应用程序性能的目的。

  线程池是资源池在线程应用中的一个实例。了解线程池之前我们首先要了解一下资源池的概念。在JAVA中,创建和销毁对象是比较消耗资源的。我们如果在应用中需要频繁创建销毁某个类型的对象实例,这样会产生很多临时对象,当失去引用的临时对象较多时,虚拟机会进行垃圾回收(GC),CPU在进行GC时会导致应用程序的运行得不到相应,从而导致应用的响应性降低。

  资源池就是用来解决这个问题,当你需要使用对象时,从资源池来获取,资源池负责维护对象的生命周期。

  了解了资源池,就很好理解线程池了,线程池就是存放对象类型都是线程的资源池。

  我增加了如何在其他线程中创建Handler的例子作为选学,前面都掌握好了的同学可以看一下,如果你需要实现一个跟Main线程类似的消息处理机制,需要其他线程可以跟你的线程通讯,可以通过这种方法实现。

android 多线程

阅读数 285

Android多线程问题

阅读数 3769

没有更多推荐了,返回首页