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内部应该是用线程池来实现的,但是在保证消息处理不被堵住的情况下,异步的方式可以省很多的线程,进而整体上效率会高一点。
这个和网游服务端的处理方式是一样的,实际上协程只是透明化的异步处理方式而已。
相关推荐
Android中多线程的Handler的工作原理,其中涉及到MessageQueue和Looper。详情可以参见博客:http://www.cnblogs.com/plokmju/p/android_Looper.html
总的来说,实现Android多线程下载涉及网络请求、线程管理、文件操作、数据同步等多个方面。开发者需要熟悉Android的并发编程模型,了解网络编程的基础知识,并掌握文件I/O的操作。通过这样的实践,不仅可以提高应用...
在Android应用开发中,多线程机制扮演着至关重要的角色,因为它们确保了应用程序的流畅性和响应性。Android系统默认情况下会让所有应用组件在同一个进程和线程中运行,即所谓的主线程或UI线程。这为主线程设计了一个...
本主题聚焦于"Android多线程文件上传",我们将探讨如何利用多线程技术来优化文件上传过程,提高效率,并确保应用的响应性。 一、多线程基础 多线程是并发执行多个任务的能力,它允许应用程序同时处理多个任务,提高...
代码如下: public class MainActivity extends Activity { // 声明控件 // 路径与线程数量 private EditText et_url, et_num; // 进度条 public static ProgressBar pb_thread; // 显示进度的操作 private TextView ...
浅谈Android中多线程切换的几种方法 本篇文章主要介绍了Android中多线程切换的几种方法,包括Thread、ThreadPool、Runnable、Callable、Future、Condition、Handler等方法。多线程切换是Android开发中必现的场景,...
### Android开发中的多线程编程技术详解 #### 一、多线程编程的重要性与挑战 在Android开发过程中,多线程编程技术对于提高应用程序的性能和用户体验至关重要。...希望本文对您理解和应用Android多线程编程有所帮助。
在Android平台上,多线程下载是一项重要的技术,它允许应用程序同时从服务器获取数据,从而显著提高了文件下载的速度和效率。本教程将深入讲解如何在Android环境中实现多线程下载功能,以及涉及的相关知识点。 首先...
标题"android——多线程"和描述"android——Handler与多线程应用范例"暗示我们将深入探讨如何在Android中使用Handler来管理多线程。 Android系统默认运行在一个单线程环境中,即主线程,也被称为UI线程。主线程主要...
这个"Android多线程分段下载源码"实例是一个很好的学习资源,它实现了文件的分块下载,并允许用户自定义线程数来控制下载速度和效率。下面我们将详细探讨这一技术。 首先,我们要理解什么是分段下载。传统的单线程...
本话题将深入探讨如何在Android中使用JNI进行多线程编程,并实现native方法对Java函数的回调。 1. **JNI基础知识**: - JNI是Java平台的一部分,为Java应用程序提供了与本地代码交互的能力。开发者可以通过JNI在...
标题"Android多线程操作"和描述"Android多线程开发实例,对使用多线程的用户有一定的参考价值!"暗示我们将深入探讨Android中的线程管理以及如何在实践中有效利用。 Android系统默认运行在主线程,也被称为UI线程,...
多线程下载是将一个大文件分成多个部分,每个部分在一个单独的线程中下载。这样可以同时利用多个网络连接,提高下载速度。在`downloadDemo`项目中,可能包含以下关键点: 1. **任务分片**:根据文件大小,计算出每...
android多线程后台下载示例程序,android多线程后台下载示例程序,android多线程后台下载示例程序,android多线程后台下载示例程序,android多线程后台下载示例程序,android多线程后台下载示例程序
通过以上步骤,我们可以实现一个功能完善的Android多线程下载器,它支持暂停、断点续传,并能够根据用户需求进行定制。在项目实践中,务必注意线程同步和数据一致性,以保证下载的正确性。 最后,提供的...
Android中多线程的使用,实现多线程对控件的控制,t1,t2测试
### Android多线程机制详解 #### 一、引言 Android多线程机制是Android开发中非常重要的一部分,尤其是在处理耗时任务(如网络请求、大数据处理等)时,避免阻塞UI线程,保证应用程序的流畅性和响应性。本文将详细...