Android 多线程详解 - 图文

更新时间:2024-03-24 19:23:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

Thread类

常用方法

(1)public static Thread currentThread():返回当前调用的线程 (2)public final String getName():返回当前线程名称 (3)public long getId():返回当前线程标识id

(4)public final void setName(String threadName):设置线程名称 (5)public void start():执行一个新的线程

(6)public final void stop():关闭一个线程,不推荐使用因为该方法可能会产生死锁,推荐使用在Thread中设置标识符结束运行的代码

(7)public static void sleep(long time):等待一段时间后在运行线程,单位是毫秒

进程概念

在同一时间内执行多个程序的操作系统都有进程的概念。一个进程就是一个执行中的程序,而每一个进程都有自己独立的一块内存空间、一组系统资源。在进程的概念中,每一个进程的内部数据和状态都是完全独立的。在Windows操作系统下我们可以通过〈Ctrl+Alt+Del〉组合键查看进程,在UNIX和Linux操作系统下是通过PS命令查看进程的。打开Windows当前运行的进程,如图

在Windows操作系统中一个进程就是一个exe或dll程序,它们相互独立,互相也可以通信,在Android操作系统中进程间的通信应用也是很多

Android空进程

线程概念

多线程指的是在单个程序中可以同时运行多个不同的线程,执行不同的任务。多线程意味着一个程序的多行语句可以看上去几乎在同一时间内同时运行。线程与进程相似,是一段完成某个特定功能的代码,是程序中单个顺序的流控制。但与进程不同的是,同类的多个线程共享一块内存空间和一组系统资源,所以系统在各个线程之间切换时,资源占用要比进程小得多,正因如此,线程也被称为轻量级进程。一个进程中可以包含多个线程。如图所示是计时器程序进程和线程之间的关系,主线程负责管理子线程,即子线程的启动、挂起、停止等操作

Java中的线程

Java的线程类是 java.lang.Thread类。当生成一个Thread类的对象之后,一个新的线程就产生了。Java中每个线程都是通过某个特定Thread对象的方法run()来完成其操作的,方法run( )称为线程体

下面是构建线程类几种常用的方法: public Thread()

public Thread(Runnable target)

public Thread(Runnable target, String name) public Thread(String name)

参数 target 是一个实现Runnable接口的实例,它的作用是实现线程体的run()方法。目标target可为null,表示由本身实例来执行线程。name参数指定线程名字,但没有指定的构造方法,线程的名字是JVM分配的,例如JVM指定为thread-1、thread-2等名字

Handler处理Runnable线程

public class AppLoadingActivity extends BaseActivity { private Handler handler = new Handler();

private Runnable updateThread = new Runnable() {

@Override

public void run() { 线程运行的代码 } }

@Override

protected void onCreate(Bundle savedInstanceState) { //向Handler中添加处理线程 handler.post(updateThread); }

private void checkLogin() { //删除Handler中的线程

handler.removeCallbacks(updateThread); }

}

1、Java中的实现线程体方式

在Java中有两种方法实现线程体:一是继承线程类Thread,二是实现接口Runnable。下面我们先看看继承线程类Thread方式。

(1)继承线程类Thread

该方式继承线程类Thread并重写其中的方法 run(),在初始化这个类实例的时候,目标target可为null,表示由本实例来执行线程体。由于Java只支持单重继承,用这种方法定义的类不能再继承其他父类

import java.io.BufferedReader; import java.io.IOException;

import java.io.InputStreamReader;

public class T extends Thread {

boolean isRunning = true; int timer = 0; /**

* 线程体代码 */

@Override

public void run() { while (isRunning) { try {

Thread.currentThread().sleep(1000); timer++;

System.out.println(\逝去了 \ + timer + \秒\);

Toast.makeText(T.this, \, Toast.LENGTH_SHORT).show(); break; }

super.handleMessage(msg); } }; @Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.tab_host); ...

//可出现在其它位置处

new Thread(new Runnable() { @Override

public void run() { // 进行耗时操作的代码块 ...

//子线程传递数据给主线程

Message msg = new Message(); //obj保存传递的数据 msg.obj = \;

//what传递的标志为int msg.what = 0;

handler.sendMessage(msg); }

}).start(); } }

AsyncTask实现异步任务

public abstract class AsyncTask: Params:“启动任务执行的输入参数” Progress:“后台任务执行的进度” Result:“后台计算结果的类型”

一个异步任务的执行一般包括以下几个步骤:

1.execute(Params... params),执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行。

2.onPreExecute(),在execute(Params... params)被调用后立即执行,一般用来在执行后台任务前对UI做一些标记。

3.doInBackground(Params... params),在onPreExecute()完成后立即执行,用于执行较为费时的操作,此方法将接收输入参数和返回计算结果。在执行过程中可以调用publishProgress(Progress... values)来更新进度信息。

4.onProgressUpdate(Progress... values),在调用publishProgress(Progress... values)时,此方法被执行,直接将进度信息更新到UI组件上。

5.onPostExecute(Result result),当后台操作结束时,此方法将会被调用,计算结果将做为参数传递到此方法中,直接将结果显示到UI组件上。

在使用的时候,有几点需要格外注意: (1)异步任务的实例必须在UI线程中创建

(2)execute(Params... params)方法必须在UI线程中调用

(3)不要手动调用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)这几个方法 (4)不能在doInBackground(Params... params)中更改UI组件的信息 (5)一个任务实例只能执行一次,如果执行第二次将会抛出异常

(6)对于可能随时关闭,或等待时间非常长的的操作,要使用isCancelled()来判断是否

操作被关闭,可使用cancel(true)来关闭异步进程

(7)每个 AsyncTask 只能被执行一次,多次调用将会引发异常

public class MainActivity extends Activity { private MyTask mTask;

@Override

public void onCreate(Bundle savedInstanceState) { mTask = new MyTask();

//调用execute之后,执行AsyncTask的相关方法 mTask.execute(\ //在后台未执行时,调用onCancelled()方法 mTask.cancel(true); }

private class MyTask extends AsyncTask { @Override

protected void onPreExecute() { 最先执行的代码,用于初始化 }

@Override

protected String doInBackground(String... params) { 第二执行代码部分,后台运行时间较长的代码部分(不能省去) }

@Override

protected void onPostExecute(String result) {

接收从doInBackground运行后返回的代码部分,执行完后台任务后更新UI,显示结果 }

@Override

protected void onCancelled() {

取消执行中的任务时更改UI,在UI线程中使用cancle()来调用 }

@Override

protected void onProgressUpdate(Integer... progresses) { 用于更新进度条信息

isCancelled():用于判断后台进程是否关闭 } } }

例如代码:

public class MainActivity extends Activity {

private static final String TAG = \; private Button execute; private Button cancel;

private ProgressBar progressBar; private TextView textView; private MyTask mTask; @Override

public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main);

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

execute.setOnClickListener(new View.OnClickListener() { @Override

public void onClick(View v) {

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

mTask.execute(\ execute.setEnabled(false); cancel.setEnabled(true); } });

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

cancel.setOnClickListener(new View.OnClickListener() { @Override

public void onClick(View v) {

//取消一个正在执行的任务,onCancelled方法将会被调用 mTask.cancel(true); } });

progressBar = (ProgressBar) findViewById(R.id.progress_bar); textView = (TextView) findViewById(R.id.text_view); }

private class MyTask extends AsyncTask {

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

protected void onPreExecute() { Log.i(TAG, \); textView.setText(\); }

//doInBackground方法内部执行后台任务,不可在此方法内修改UI,传递的参数是从UI线程中execute的参数该方法中存放着执行时间长的运行 @Override

protected String doInBackground(String... params) { if(isCancelled())

return null;

// Tak被取消了s,环马上退出循

Log.i(TAG, \); try {

HttpClient client = new DefaultHttpClient(); HttpGet get = new HttpGet(params[0]);

HttpResponse response = client.execute(get);

if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {

HttpEntity entity = response.getEntity(); InputStream is = entity.getContent(); long total = entity.getContentLength();

ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buf = new byte[1024]; int count = 0; int length = -1;

while ((length = is.read(buf)) != -1) { baos.write(buf, 0, length); count += length;

//调用publishProgress公布进度,最后onProgressUpdate方法将被执行 publishProgress((int) ((count / (float) total) * 100)); //为了演示进度,休眠500毫秒 Thread.sleep(500); }

return new String(baos.toByteArray(), \); }

} catch (Exception e) {

Log.e(TAG, e.getMessage()); }

return null; }

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

protected void onProgressUpdate(Integer... progresses) {

Log.i(TAG, \); progressBar.setProgress(progresses[0]);

textView.setText(\ + progresses[0] + \); }

//onPostExecute方法用于在执行完后台任务后更新UI,显示结果 //参数来自于doInBackground()执行完成之后的返回值 @Override

protected void onPostExecute(String result) {

Log.i(TAG, \); textView.setText(result); execute.setEnabled(true); cancel.setEnabled(false); }

//onCancelled方法用于在取消执行中的任务时更改UI,在UI线程中使用cancle()来调用 @Override

protected void onCancelled() {

Log.i(TAG, \); textView.setText(\); progressBar.setProgress(0); execute.setEnabled(true); cancel.setEnabled(false); } }

}

多线程下载

1>MainActivity.java代码 import java.io.File;

import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.ProtocolException; import java.net.URL;

import com.chenshun.util.SDCardInfo;

import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.app.Activity;

import android.text.TextUtils; import android.view.Menu; import android.view.View; import android.widget.EditText; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast;

public class MainActivity extends Activity {

protected static final int DOWN_LOAD_ERROR = 0; protected static final int SERVER_ERROR = 1; private static final int DOWNLOAD_ALL = 2; private static final int UPDATE_TEXT = 3; private EditText et_path; private ProgressBar pb; private TextView tv_process;

private int currentProcess = 0;//当前进度

public static int threadCount = 3;

public static int runningThread = 3;

private Handler handler = new Handler() { @Override

public void handleMessage(Message msg) { switch(msg.what) { case DOWN_LOAD_ERROR:

Toast.makeText(MainActivity.this, \下载失败\ break; case SERVER_ERROR:

Toast.makeText(MainActivity.this, \服务器请求失败!\ break;

case DOWNLOAD_ALL:

Toast.makeText(MainActivity.this, \所有文件都已下载!\ break; case UPDATE_TEXT:

tv_process.setText(\当前进度:\ break; }

super.handleMessage(msg); } }; @Override

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);

et_path = (EditText) findViewById(R.id.edit_path); pb = (ProgressBar) findViewById(R.id.pb);

tv_process = (TextView) findViewById(R.id.tv_process); }

public void downLoad(View view) {

final String path = et_path.getText().toString().trim();

Toast.makeText(MainActivity.this, \点击下载!

path=\ORT).show();

if(TextUtils.isEmpty(path)) {

Toast.makeText(MainActivity.this, \路径输入有错误!\ return; } /**

* 主线程中主要是用来修改UI因此将耗时长的操作放在子线程中 * 比如说:访问网络、修改UI(除对于精度条为特例) */

new Thread() { public void run() { try {

//1:连接服务器,获取一个文件,获取文件长度,在本地创建一个和服务器打下一样的文件

URL url = new URL(path);

System.out.println(\ //创建URL连接

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

//设置连接时间5秒

conn.setConnectTimeout(5000); // conn.setRequestMethod(\ // int code = conn.getResponseCode(); System.out.println(\ if(200 == code ) { //f int length= conn.getContentLength(); System.out.println(\ // pb.setMax(length); //大小的零时文件

//RandomAccessFile.RandomAccessFile(String arg0, String arg1)

//arg0设置连接方式 注:此处是大小写敏感 获取返回码 200:请求成功

返回码:\

服务器返回文件的长度

文件总长度:\ 设置进度条最大值

在客户端本地创建出来一个大小跟服务器文件一样:文件存储名 arg1:文件存储访问方式

RandomAccessFile raf = new RandomAccessFile(\

//指定文件长度 raf.setLength(length); raf.close(); //假设是3个线程

//平均每一个线程的文件大小

int blockSize = length / threadCount; for(int threadId = 1;threadId <= threadCount; threadId ++) {

//线程开始位置

int startIndex = (threadId - 1) * blockSize; int endIndex = threadId * blockSize - 1; if(threadCount == threadId ) { endIndex = length; }

System.out.println(\线程:

\下载 startIndex --> \ new DownloadThread(threadId, startIndex, endIndex, path).start();

} } else {

Message msg = new Message(); msg.what = SERVER_ERROR; handler.sendMessage(msg); }

} catch (Exception e) {

Message msg = new Message(); msg.what = DOWN_LOAD_ERROR; handler.sendMessage(msg); e.printStackTrace(); } } }.start(); } /**

* 下载文件的子线程 每一个线程下载位置的文件 * @author Administrator

* */

public class DownloadThread extends Thread { private int threadId; private int startIndex; private int endIndex; private String path; /** *

* @param threadId 线程id

* @param startIndex 线程下载开始位置 * @param endIndex 线程下载结束位置 * @param path 文件下载的路径 */

public DownloadThread(int threadId, int startIndex, int endIndex, String path) { super();

this.threadId = threadId; this.startIndex = startIndex;

this.endIndex = endIndex; this.path = path; }

/* (non-Javadoc)

* @see java.lang.Thread#run() */ @Override public void run() { try {

//检查是否存在记录下载长度的文件,如果存在读取这个文件读数据

File tempFile = new File(\ if(tempFile.exists() && tempFile.length() > 0) {

FileInputStream fis = new FileInputStream(tempFile); byte[] temp = new byte[1024]; int leng = fis.read(temp);

String downloadLen = new String(temp, 0, leng); //获取上次下载的存储位置

int downloadlenInt = Integer.parseInt(downloadLen);

int alreadyDownlodint = downloadlenInt - startIndex ;

currentProcess+=alreadyDownlodint; //计算上次断点 已经下载的文件的长度.

startIndex = downloadlenInt;//修改下载的真实的开始位置.

fis.close(); }

URL url = new URL(path);

HttpURLConnection conn = (HttpURLConnection) url.openConnection();

conn.setConnectTimeout(5000); conn.setRequestMethod(\ //重要:请求服务器下载部分文件的位置

//void java.net.URLConnection.setRequestProperty(String arg0, String arg1)

//arg0:请求范围 arg1:请求范围

conn.setRequestProperty(\+endIndex);

//从服务器请求全部资源 200 ok //从服务器请求部分资源 206 ok // 20 // 30 // 40 // 50 int code = conn.getResponseCode(); System.out.println(\ // InputStream is = conn.getInputStream(); RandomAccessFile raf = new RandomAccessFile(\etup.chm\ // raf.seek(startIndex); int len = 0; byte[] buffer = new byte[1024]; // int total = startIndex;开头的表示:请求成功

开头的表示:资源不存在或者是重定向 开头的表示:服务器资源找不到 开头的表示:服务期内部出现错误 返回码 : \ 返回当前文件位置输入流

随机从哪个位置开始写

已下载的数据长度

//用于记录当前线程数据长度

File file = new File(\ while(-1 != (len = is.read(buffer))) { raf.write(buffer, 0, len); total += len;

FileOutputStream fos = new FileOutputStream(file); fos.write(String.valueOf(total).getBytes()); fos.flush(); fos.close();

//更新进度条 synchronized (MainActivity.this) {}:对于多个进程代码可以同时被访问但是资源不可以被同时访问

synchronized (MainActivity.this) { //获取当前的总进度 currentProcess += len;

//设置当前进度,并修改显示进度(这是一个特殊情况可在子线程中修改界面)

pb.setProgress(currentProcess);

//Message.obtain() :返回消息的实例要是该消息已存在就不会再new,就可以复用旧的消息提高效率

Message msg = Message.obtain(); msg.what = UPDATE_TEXT;

handler.sendMessage(msg); } } is.close(); raf.close();

System.out.println(\线程:\下载完毕...\ } catch (Exception e) { e.printStackTrace(); } finally {

runningThread--; if(0 == runningThread) {

for(int i = 1;i <= threadCount;i ++) {

File deleteFile = new File(\ deleteFile.delete(); }

Message msg = new Message(); msg.what = DOWNLOAD_ALL; handler.sendMessage(msg); } }

} } }

2>activity_main.XML

android:id=\ android:layout_width=\ android:layout_height=\

android:text=\ android:hint=\请输入下载的路径\

android:id=\

style=\ android:layout_width=\ android:layout_height=\

Top