android中的多线程

android中的多线程

一、基于消息队列的handler机制,别的线程与自己进行交互,需要使用消息队列,这样可以实现功能的分离和处理的异步,

Android中对消息队列进行了封装,具体的:

1、Looper 负责维护并发安全的消息队列

2、handler 负责进行消息的入队、出队,以及消息的处理函数 void handleMessage(Message msg)

3、Message 是OS系统内抽象的数据结构

4、相互关系:handler操作的是具体Looper代表的消息队列(不指定时默认是当前线程的looper),并且每个线程有自己的消息队列,因此别的线程可以通过handler来操作主线程

  5、Loop.myLooper()得到当前线程的Looper对象,通过Loop.getMainLooper()可以获得当前进程的主线程的 Looper对象

6、注意:Looper对message的处理是同步的,即一个handler会阻塞别的looper,且会阻塞后面所有消息的处理,所以注意 handleMessage中不能有耗时的操作(可以做完后发送消息来实现)

7、post过去的Runnable是接口,不会再创建新的线程

8、Looper.Callback 以Runnable为接口

1、handler机制是常见的消息队列的方式,但是每个回调都是同步的,例子如下(代码来自网络,但不知道是谁写的):

package com.weitm.testhandler;

import android.os.Bundle;

import android.os.Handler;

import android.os.Looper;

import android.os.Message;

import android.app.Activity;

import android.graphics.Color;

import android.util.Log;

import android.view.Menu;

import android.widget.*;

import android.view.*;

import android.view.View.OnClickListener;

import android.view.ViewGroup.LayoutParams;

public class MainActivity extends Activity implements OnClickListener

{

  private String               TAG                  = "HandlerTest";

  private boolean              bpostRunnable        = false;

  private NoLooperThread       noLooperThread       = null;         // 普通线程

  private OwnLooperThread      ownLooperThread      = null;         // 有自己消息队列的线程

  private ReceiveMessageThread receiveMessageThread = null;         // 主线程内的子线程(拥有自己的消息队列)

  private Handler              mOtherThreadHandler  = null;         // 主线程内的子线程对应的handler

  private EventHandler         mHandler             = null;

  private Button               btn1                 = null;

  private Button               btn2                 = null;

  private Button               btn3                 = null;

  private Button               btn4                 = null;

  private Button               btn5                 = null;

  private Button               btn6                 = null;

  private TextView             tv                   = null;

  @Override

  protected void onCreate(Bundle savedInstanceState)

  {

super.onCreate(savedInstanceState);

LinearLayout layout = new LinearLayout(this);

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(250, 50);

layout.setOrientation(LinearLayout.VERTICAL);

btn1 = new Button(this);

btn1.setId(101);

btn1.setText("UI thread 发给自己");

btn1.setOnClickListener(this);

layout.addView(btn1, params);

btn2 = new Button(this);

btn2.setId(102);

btn2.setText("別人发给UI thread");

btn2.setOnClickListener(this);

layout.addView(btn2, params);

btn3 = new Button(this);

btn3.setId(103);

btn3.setText("别人发给自己msg");

btn3.setOnClickListener(this);

layout.addView(btn3, params);

btn4 = new Button(this);

btn4.setId(104);

btn4.setText("别人发给UI runnable");

btn4.setOnClickListener(this);

layout.addView(btn4, params);

btn5 = new Button(this);

btn5.setId(105);

btn5.setText("主线程发给自己的子线程");

btn5.setOnClickListener(this);

layout.addView(btn5, params);

btn6 = new Button(this);

btn6.setId(106);

btn6.setText("exit");

btn6.setOnClickListener(this);

layout.addView(btn6, params);

tv = new TextView(this);

tv.setTextColor(Color.RED);

tv.setText("");

params = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT);

params.topMargin = 10;

layout.addView(tv, params);

setContentView(layout);

    // 不使用默认的activity内的Looper和handler,而是使用自定义的,以便与主线程相区分

receiveMessageThread = new ReceiveMessageThread();

receiveMessageThread.start();

  }

  class ReceiveMessageThread extends Thread

  {

@Override

public void run()

{

 Looper.prepare(); // 有了自己的消息队列

 // 最直接的处理自己的消息队列,但是这个不同于主线程

 mOtherThreadHandler = new Handler() {

@Override

public void handleMessage(Message msg)

{

 // TODO Auto-generated method stub

 super.handleMessage(msg);

 Log.e(TAG, "-------+>" + (String) msg.obj);

 Log.e(TAG, "CurrentThread id:----------+>"+ Thread.currentThread().getId());

}

 };

 Log.e(TAG, "ReceiveMessageThread id:--------+>" + this.getId());

 Looper.loop(); // 开始事件循环

}

  }

  class NoLooperThread extends Thread

  {

private EventHandler mNoLooperThreadHandler;

@Override

public void run()

{

 Looper myLooper = Looper.myLooper();

 Looper mainLooper = Looper.getMainLooper();

 String msgobj;

 if (null == myLooper)

 {

// 这里获得的是主线程的Looper,由于NoLooperThread没有自己的looper所以这里肯定会被执行

mNoLooperThreadHandler = new EventHandler(mainLooper);

msgobj = "NoLooperThread has no looper and handleMessage function executed in main thread!";

 } else

 {

mNoLooperThreadHandler = new EventHandler(myLooper);

msgobj = "This is from NoLooperThread self and handleMessage function executed in NoLooperThread!";

 }

 mNoLooperThreadHandler.removeMessages(0);

 if (bpostRunnable == false)

 {

// send message to main thread

Message msg = mNoLooperThreadHandler.obtainMessage(2, 1, 1, msgobj);

mNoLooperThreadHandler.sendMessage(msg);

Log.e(TAG, "NoLooperThread id:--------+>" + this.getId());

 } else

 {

// 下面new出来的实现了Runnable接口的对象中run函数是在Main

// Thread中执行,不是在NoLooperThread中执行 记得 null == myLooper么

// 注意Runnable是一个接口,它里面的run函数被执行时不会再新建一个线程

// 您可以在run上加断点然后在eclipse调试中看它在哪个线程中执行

mNoLooperThreadHandler.post(new Runnable() {

 public void run()

 {

// TODO Auto-generated method stub

tv.setText("update UI through handler post runnalbe mechanism!");

// 这里打印出来的是main thread的线程id,也就是说Runnable在handler机制中只是作为回调接口

// 并没有创建新的线程

Log.e(TAG, "update UI id:--------+>"+ Thread.currentThread().getId());

noLooperThread.stop();

 }

}); // end of post

 } // end of else

}

  }

  class EventHandler extends Handler

  {

public EventHandler(Looper looper)

{

 super(looper);

}

public EventHandler()

{

 super();

}

@Override

public void handleMessage(Message msg)

{

 // TODO Auto-generated method stub

 super.handleMessage(msg);

 Log.e(TAG, "CurrentThread id:----------+>" + Thread.currentThread().getId());

 switch (msg.what)

 {

 case 1:

tv.setText((String) msg.obj);

break;

 case 2:

tv.setText((String) msg.obj);

noLooperThread.stop();

break;

 case 3:

// 不能在非主线程的线程里面更新UI,所以这里通过log打印信息

Log.e(TAG, (String) msg.obj);

ownLooperThread.stop();

break;

 default:

Log.e(TAG, (String) msg.obj);

break;

 }

}

  }

  class OwnLooperThread extends Thread

  {

private EventHandler mOwnLooperThreadHandler = null;

@Override

public void run()

{

 Looper.prepare();

 Looper myLooper = Looper.myLooper();

 Looper mainLooper = Looper.getMainLooper();

 String msgobj;

 if (null == myLooper)

 {

mOwnLooperThreadHandler = new EventHandler(mainLooper);

msgobj = "OwnLooperThread has no looper and handleMessage function executed in main thread!";

 } else

 {

mOwnLooperThreadHandler = new EventHandler(myLooper);

msgobj = "This is from OwnLooperThread self and handleMessage function executed in !";

 }

 mOwnLooperThreadHandler.removeMessages(0);

 // 给自己发送消息

 Message msg = mOwnLooperThreadHandler.obtainMessage(3, 1, 1, msgobj);

 mOwnLooperThreadHandler.sendMessage(msg);

 // 开始循环

 Looper.loop();

}

  }

  public void onClick(View v)

  {

switch (v.getId())

{

case 101:

 // 主线程发送消息给自己

 Looper looper = Looper.myLooper();// get the Main looper related with the

// main thread

 // 如果不给任何参数的话会用当前线程对应的Looper(这里就是Main Looper)为Handler里面的成员mLooper赋值

 mHandler = new EventHandler(looper);

 // 清除整个MessageQueue里的消息

 mHandler.removeMessages(0);

 String obj = "This main thread's message and received by itself!";

 // 消息号是1

 Message msg = mHandler.obtainMessage(1, 1, 1, obj);

 // 将Message对象送入到main thread的MessageQueue里面

 mHandler.sendMessage(msg);

 break;

case 102:

 bpostRunnable = false;

 noLooperThread = new NoLooperThread();

 noLooperThread.start();

 break;

case 103:

 tv.setText("please look at the error level log for other thread received message");

 ownLooperThread = new OwnLooperThread();

 ownLooperThread.start();

 break;

case 104:

 bpostRunnable = true;

 noLooperThread = new NoLooperThread();

 noLooperThread.start();

 break;

case 105:

 if (null != mOtherThreadHandler)

 {

tv.setText("这是主线程向自己的子线程发送消息");

String msgObj = "message from mainThread";

Message mainThreadMsg = mOtherThreadHandler.obtainMessage(1, 1, 1, msgObj);

mOtherThreadHandler.sendMessage(mainThreadMsg);

 }

 break;

case 106:

 finish();

 break;

}

  }

  @Override

  public boolean onCreateOptionsMenu(Menu menu)

  {

// Inflate the menu; this adds items to the action bar if it is present.

// getMenuInflater().inflate(R.menu.main, menu);

return true;

  }

}

2、例子说明

   按钮依次演示了 主线程内的消息处理、子线程内的消息和runnable处理,以及往别的线程中发消息等操作,

并且重点区分了looper所对应的执行线程,以及handler所操作的具体线程等信息。

3、handler机制的问题

  一个looper上可以挂多个handler,且looper内的message是依次进行处理的,这样 同一个message上的handler之间会相互阻塞,而且前一个消息会阻塞后面消息的处理,

这里的阻塞的意思是说只能等着前一个结束了才能进行后一个,如果要实现不阻塞,则只能每个handler都在不同的线程中,这样的话很快就会出现线程太多拖慢系统的问题。

举个应用中的例子:假如我们的app可以允许从网上下载内容,则如果要同时支持多个下载,则必须同时开多个线程,如果同时app支持从远端数据库中查询数据,从淘宝中捞出数据,和别人实时交互... 这里的每一样都需要

开一个新的线程,所以如果app复杂一些,则会有性能问题。解决方案是AsyncTask机制。

二、AsyncTask机制

前面说了需要每个handler的回调是异步的,即每次调到这里时可以直接返回,而不是一直堵在那里,那么只能使用异步的消息了(底层是使用线程池来进行调度,所以不会有一个handler占一个线程的问题)。

1、基本的实现思路

异步的基本实现思路是 将每个需要执行的task排队,之后依次取出进行执行,在执行时检查是否完成,没有的话直接返回给监听线程,这样监听线程可以查看别的task执行的情况,这样从总体上来看

好像所有的消息都得到了照顾,但是实际上这个只是个假象,因为实际在干活的只能是有限的worker thread,而且道理上讲 线程越多,系统越慢。

2、AsyncTask的抽象数据结构

  抽象上讲,AsyncTask需要提供 输入的参数、更新时的参数、输出的参数等3个泛型的参数,为了实现异步需要提供 

 1、doInBackground(输入的参数) --- 实际干活的逻辑

 2、onProgressUpdate(更新时的参数类型) --- 主线程要使用的回调

 3、onPreExecute()                      ----- 初始化的工作

 4、onPostExecute(输出时的数据类型)  ------ 返回结果

3、可以使用的有用的函数 publishProgress(更新时的参数) --- 更新返回给调用线程的进度值

4、例子

package com.example.testasync;

import java.util.Random;

import android.R;

import android.app.Activity;

import android.os.AsyncTask;

import android.os.Bundle;

import android.util.Log;

import android.view.Menu;

import android.view.View;

import android.widget.Button;

import android.widget.LinearLayout;

import android.widget.ProgressBar;

import android.widget.TextView;

public class MainActivity1 extends Activity 

{

  private static final String TAG = "异步任务";

  private static final String URL = "http://www.google.com.hk/";

  private static final String STEP1 = "步骤1";

  private static final String STEP2 = "步骤2";

  private static final String STEP3 = "步骤3";

  private static final String STEP4 = "步骤4";

  

  private Button execute;

  private Button cancel;

  private ProgressBar progressBar;

  private TextView textView;

  

  private MyTask mTask;

  @Override

  protected void onCreate(Bundle savedInstanceState)

  {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_list_item);

LinearLayout layout = new LinearLayout(this);

LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(250, 50);

layout.setOrientation(LinearLayout.VERTICAL);

execute = new Button(this);

execute.setId(101);

execute.setText("execute");

execute.setOnClickListener(new View.OnClickListener() {

      public void onClick(View v) {

              //注意每次需new一个实例,新建的任务只能执行一次,否则会出现异常

              mTask = new MyTask();

              mTask.execute(URL);

              

              execute.setEnabled(false);

              cancel.setEnabled(true);

      }

});

layout.addView(execute, params);

cancel = new Button(this);

cancel.setId(102);

cancel.setText("cancel");

layout.addView(cancel, params);

cancel.setOnClickListener(new View.OnClickListener() {

       public void onClick(View v) {

               //取消一个正在执行的任务,onCancelled方法将会被调用

               mTask.cancel(true);

       }

    });

    progressBar = new ProgressBar(this);

    layout.addView(progressBar, params);

    

    textView    = new TextView(this);

    layout.addView(textView, params);

    setContentView(layout);

  }

  @Override

  public boolean onCreateOptionsMenu(Menu menu)

  {

// Inflate the menu; this adds items to the action bar if it is present.

//getMenuInflater().inflate(R.menu.main, menu);

return true;

  }

  

  // 参数依次是输入、更新、输出的参数类型,为了说明问题故意将更新的参数类型设置为string 而不是integer

  class MyTask extends AsyncTask<String, String, String> {

int times;

// onPreExecute方法用于在执行后台任务前做一些UI操作

protected void onPreExecute()

{

 Log.i(TAG, "onPreExecute() called");

 textView.setText("开始执行...");

}

// doInBackground方法内部执行后台任务,不可在此方法内修改UI

protected String doInBackground(String... params)

{

 Log.i(TAG, "doInBackground(Params... params) called");

 String result = "";

 try{

// 模拟消耗时间的操作

for (int j = 0; j < 15;j++)

{

Random random = new Random();

int i = Math.abs(random.nextInt())%4;

result = "步骤" + i;

Thread.sleep(1000);

// 本来发布出去的一般是进度,这里故意用string

publishProgress(result);

}

// 随机返回,如果不是 STEP1则显示75%,否则是100%

// 这里是用这个来模拟task内部状态的变化,类似于游戏内的状态机

 return result;

 }

 catch(Exception e)

 {

return null;

 }

}

// onProgressUpdate方法用于更新进度信息

protected void onProgressUpdate(String... progresses)

{

 Log.i(TAG, "onProgressUpdate(Progress... progresses) called");

 if (progresses[0].equals(STEP1) )

progressBar.setProgress(100);

 else

progressBar.setProgress(20);

 textView.setText("loading..." + progresses[0]);

}

// onPostExecute方法用于在执行完后台任务后更新UI,显示结果

// 这个result具体的值是由doInBackground来设置的

protected void onPostExecute(String result)

{

 Log.i(TAG, "onPostExecute(Result result) called");

 textView.setText(result);

 execute.setEnabled(true);

 cancel.setEnabled(false);

}

// onCancelled方法用于在取消执行中的任务时更改UI

protected void onCancelled()

{

 Log.i(TAG, "onCancelled() called");

 textView.setText("cancelled");

 progressBar.setProgress(0);

 execute.setEnabled(true);

 cancel.setEnabled(false);

}

}

}

5、说明 

  例子中演示了AsyncTask中 3个泛型参数的具体化,利用publishProgress实时的返回执行的情况,以及onProgressUpdate中实现的 操作异步化

  

三、内部实现猜测

  尽管AsyncTask内部应该是用线程池来实现的,但是在保证消息处理不被堵住的情况下,异步的方式可以省很多的线程,进而整体上效率会高一点。

这个和网游服务端的处理方式是一样的,实际上协程只是透明化的异步处理方式而已。

猜你喜欢

转载自eric-weitm.iteye.com/blog/1892414