Android 多线程编程

在Android中,我们绘制图形界面的线程即是主线程,也叫UI线程。由于在主线程中进行过于耗时的操作(Activity超过5秒,BroadCast超过10秒)会导致ANR(Application Not Responding,应用程序无响应),而且Android4.0以后也规定,不允许在主线程中进行网络操作(耗时操作),因此对于耗时操作我们并需在新开启的线程中执行,并通过线程间的通信机制在主线程中更新UI。以下总结了几种多线程编程的方法:

1.开启新线程,使用handler通信

这是界面的xml代码:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="horizontal"
    tools:context="com.example.test.MyActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="1+1 = "
        android:textSize="24sp" />

    <TextView
        android:id="@+id/ans"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="\?"
        android:textSize="24sp" />

</LinearLayout>

布局只有一行文字,非常简单。
再来是Activity的代码:
package com.example.test;

import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;


public class MyActivity extends Activity{
	
	// 使用handler实现线程间通信
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			tv.setText(msg.arg1+"");
		};
	};
	private TextView tv;
	
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tv = (TextView) findViewById(R.id.ans);
        // 使用匿名类开启新线程
        new Thread(new Runnable() {
			
			@Override
			public void run() {
				// 模拟耗时操作,等待3秒
				try {
					TimeUnit.SECONDS.sleep(3);
				} catch (Exception e) {};
				// 使用handler实现线程间通信
				// 发送消息
				Message message = Message.obtain();
				message.arg1 = 2;
				handler.sendMessage(message);

				// post Runnable也可以
//				handler.post(new Runnable() {
//					
//					@Override
//					public void run() {
//						tv.setText("2");
//					}
//				});

				// runOnUiThread也行
//				MyActivity.this.runOnUiThread(new Runnable() {
//					
//					@Override
//					public void run() {
//						tv.setText("2");
//					}
//				});
			}
		}).start();
    }

}

在上面我们开启了一个子线程来处理延时事件(让线程休眠模拟延时),并使用了handler发送消息来与主线程通信,在主线程中更新UI界面。我们还可以使用handler.post和Activity的runOnUiThread来传入Runnable参数更新UI界面,但其实两者都是使用handler发送消息并添加回调函数来实现的。
效果图如下:
thread效果图

2.Executor

除了上面手动新建线程来进行多线程编程,我们还可以使用JavaSE5为我们提供的Executor(执行器),他将替我们管理Thread对象,简化了我们的多线程编程。代码如下:
package com.example.test;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

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

public class MyActivity extends Activity {

	// 使用handler实现线程间通信
	private Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			tv.setText(msg.arg1 + "");
		};
	};
	private TextView tv;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tv = (TextView) findViewById(R.id.ans);

		// 使用线程池来开启新线程
		ExecutorService executor = Executors.newCachedThreadPool();
		executor.execute(new Runnable() {

			@Override
			public void run() {
				// 模拟耗时操作,等待3秒
				try {
					TimeUnit.SECONDS.sleep(3);
				} catch (Exception e) {
				}
				;
				// 使用handler实现线程间通信
				// 发送消息
				Message message = Message.obtain();
				message.arg1 = 2;
				handler.sendMessage(message);
				// post Runnable也可以
//				handler.post(new Runnable() {
//
//					@Override
//					public void run() {
//						tv.setText("2");
//					}
//				});
				// runOnUiThread也行
//				MyActivity.this.runOnUiThread(new Runnable() {
//
//					@Override
//					public void run() {
//						tv.setText("2");
//					}
//				});
			}
		});
	}

}
上面使用了CachedThreadPool线程池,他会在执行过程中创建与所需数目相同的线程,然后会回收不使用的旧线程而非创建新线程。除此以外Java还提供了别的多种线程池,在此不一一描述。使用Executor会更便于我们管理线程,而且在线程数较多的时候也能使代码看起来更优雅。
由于效果图一样,在此就不再贴图。

3.AsyncTask

AsyncTask是Android提供的异步任务类,本质是用Java线程池改造的。以下是Java代码:
package com.example.test;

import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

public class MyActivity extends Activity {

	// 使用AsyncTask实现多线程编程
	private AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {
		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			// UI线程预处理
		}
		@Override
		protected Void doInBackground(Void... params) {
			// 线程休眠3秒模拟耗时操作
			try {
				TimeUnit.SECONDS.sleep(3);
			} catch (Exception e) {};
			return null;
		}
		@Override
		protected void onProgressUpdate(Void... values) {
			super.onProgressUpdate(values);
			// doInBackground调用publishProgress(values)时调用该方法;
			// 该方法处于UI线程,可以更新UI!
		}
		@Override
		protected void onPostExecute(Void result) {
			super.onPostExecute(result);
			// 处理结果
			tv.setText("2");
		}
	};
	private TextView tv;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tv = (TextView) findViewById(R.id.ans);
		// 必须在UI线程使用
		task.execute();
	}

}

AsyncTask的三个泛型参数分别代表:处理时传给Task的参数类型,打印过程时所需数据的参数类型,返回结果的类型,如果不需要该参数只要填写Void即可。AsyncTask需要重写几个关键的方法,在代码中已有注释在此不再赘述。值的注意的是,同一个AsyncTask不可execute多次,否则会发生java.lang.IllegalStateException: Cannot execute task: the task is already running.的错误。如果你想开启多个新线程,应该考虑继承AsyncTask类并创建多个实例。同时AsyncTask还存在着些许奇奇怪怪的问题,本文在此也不做深入探究。

4.使用Loader类

Android在3.0以后,SDK提供了Loader技术,使用Loader技术可以很容易进行数据的异步加载。Loader技术为我们提供的核心类有:
  1. LoaderManager:可以通过Activity或者的Fragment的getLoaderManager()方法得到LoaderManager,用来对Loader进行管理,一个Activity或者Fragment只能有一个LoaderManager。
  2. LoaderManager.LoaderCallbacks:用于同LoaderManager进行交互,可以在其中创建Loader对象。
  3. AsyncTaskLoader:抽象类,可以进行异步加载数据的Loader,貌似内部也是通过AsynTask实现的,可以通过继承它构建自己的Loader,也可以使用现有的子类,例如异步查询数据库可以使用CursorLoader。
一般而言还是使用官方实现好的Loader,自己重写Loader比较麻烦。
以下是我自己重写Loader的例子:
package com.example.test;

import java.util.concurrent.TimeUnit;

import android.app.Activity;
import android.app.LoaderManager;
import android.content.AsyncTaskLoader;
import android.content.Context;
import android.content.Loader;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public class MyActivity extends Activity {

	private static String TAG = "MyActivity";
	private TextView tv;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tv = (TextView) findViewById(R.id.ans);
		getLoaderManager().initLoader(0, null, new MyLoaderCallbacks());
	}

	private class MyLoaderCallbacks implements LoaderManager.LoaderCallbacks<Integer> {
		@Override
		public Loader<Integer> onCreateLoader(int id, Bundle args) {
			Log.v(TAG, "onCreateLoader");
			// UI预处理
			return new MyLoader(getApplicationContext());
		}
		@Override
		public void onLoaderReset(Loader<Integer> loader) {
			Log.v(TAG, "onLoaderReset");
			// 处理Loader被reset的情况,在此需要清除掉对上一个data的引用
		}
		@Override
		public void onLoadFinished(Loader<Integer> loader, Integer data) {
			Log.v(TAG, "onLoadFinished");
			// 处理UI结果
			tv.setText(data+"");
		}
	}
	
	// 必须是静态类,否则会有RuntimeException
	private static class MyLoader extends AsyncTaskLoader<Integer> {
		public MyLoader(Context context) {
			super(context);
		}
		@Override
		protected void onStartLoading() {
			Log.v(TAG, "onStartLoading");
			// 调用forceLoad来开启新线程执行任务
			forceLoad();
		}
		@Override
		public Integer loadInBackground() {
			Log.v(TAG, "loadInBackground");
			// 模拟耗时操作
			try {
				TimeUnit.SECONDS.sleep(3);
			} catch (Exception e) {};
			return 2;
		}
	}
	
}

值的注意的有几个地方:
  1. LoaderCallbacks的泛型代表处理结果的返回类型
  2. 自己写的AsyncTaskLoader的子类必须为静态类
  3. 必须在自己写的Loader中调用forceLoad方法,否则Loader不会开启新线程执行操作
与AsyncTask相比,Loader有了更多处理上的优化,比如加载被中断后自动保存现场等,本文在此不做深入探讨。

综上所述,在Android中实现多线程编程的方法有:
  1. 手动新建线程,使用Handler进行通信
  2. 使用线程池新建线程,使用Handler进行通信
  3. 使用Android提供的AsyncTask工具类
  4. 使用Android提供的Loader工具类

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值