Android 多线程编程
ka7231zfvl
9年前
<p>在Android中,我们绘制图形界面的线程即是主线程,也叫UI线程。由于在主线程中进行过于耗时的操作(Activity超过5秒,BroadCast超过10秒)会导致ANR(Application Not Responding,应用程序无响应),而且Android4.0以后也规定,不允许在主线程中进行网络操作(耗时操作),因此对于耗时操作我们并需在新开启的线程中执行,并通过线程间的通信机制在主线程中更新UI。以下总结了几种多线程编程的方法:</p> <p> </p> <h2>1.开启新线程,使用handler通信</h2> <p>这是界面的xml代码:</p> <pre> <code class="language-java"><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></code></pre> <p><br> 布局只有一行文字,非常简单。</p> <p>再来是Activity的代码:</p> <pre> <code class="language-java">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(); } }</code></pre> <p><br> 在上面我们开启了一个子线程来处理延时事件(让线程休眠模拟延时),并使用了handler发送消息来与主线程通信,在主线程中更新UI界面。我们还可以使用handler.post和Activity的runOnUiThread来传入Runnable参数更新UI界面,但其实两者都是使用handler发送消息并添加回调函数来实现的。</p> <p>效果图如下:</p> <p><img alt="thread效果图" src="https://simg.open-open.com/show/6b9dfd734034541ab0552dd1409f1a1c.gif"></p> <h2>2.Executor</h2> <p>除了上面手动新建线程来进行多线程编程,我们还可以使用JavaSE5为我们提供的Executor(执行器),他将替我们管理Thread对象,简化了我们的多线程编程。代码如下:</p> <pre> <code class="language-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.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"); // } // }); } }); } }</code></pre> <p>上面使用了CachedThreadPool线程池,他会在执行过程中创建与所需数目相同的线程,然后会回收不使用的旧线程而非创建新线程。除此以外Java还提供了别的多种线程池,在此不一一描述。使用Executor会更便于我们管理线程,而且在线程数较多的时候也能使代码看起来更优雅。</p> <p>由于效果图一样,在此就不再贴图。</p> <h2>3.AsyncTask</h2> <p>AsyncTask是Android提供的异步任务类,本质是用Java线程池改造的。以下是Java代码:</p> <pre> <code class="language-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(); } }</code></pre> <p><br> AsyncTask的三个泛型参数分别代表:处理时传给Task的参数类型,打印过程时所需数据的参数类型,返回结果的类型,如果不需要该参数只要填写Void即可。AsyncTask需要重写几个关键的方法,在代码中已有注释在此不再赘述。值的注意的是,同一个AsyncTask不可execute多次,否则会发生java.lang.IllegalStateException: Cannot execute task: the task is already running.的错误。如果你想开启多个新线程,应该考虑继承AsyncTask类并创建多个实例。同时AsyncTask还存在着些许奇奇怪怪的问题,本文在此也不做深入探究。</p> <p> </p> <h2>4.使用Loader类</h2> <p>Android在3.0以后,SDK提供了Loader技术,使用Loader技术可以很容易进行数据的异步加载。Loader技术为我们提供的核心类有:</p> <ol> <li>LoaderManager:可以通过Activity或者的Fragment的getLoaderManager()方法得到LoaderManager,用来对Loader进行管理,一个Activity或者Fragment只能有一个LoaderManager。</li> <li>LoaderManager.LoaderCallbacks:用于同LoaderManager进行交互,可以在其中创建Loader对象。</li> <li>AsyncTaskLoader:抽象类,可以进行异步加载数据的Loader,貌似内部也是通过AsynTask实现的,可以通过继承它构建自己的Loader,也可以使用现有的子类,例如异步查询数据库可以使用CursorLoader。</li> </ol> <p>一般而言还是使用官方实现好的Loader,自己重写Loader比较麻烦。</p> <p>以下是我自己重写Loader的例子:</p> <pre> <code class="language-java">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; } } }</code></pre> <p><br> 值的注意的有几个地方:</p> <ol> <li>LoaderCallbacks的泛型代表处理结果的返回类型</li> <li>自己写的AsyncTaskLoader的子类必须为静态类</li> <li>必须在自己写的Loader中调用forceLoad方法,否则Loader不会开启新线程执行操作</li> </ol> <p>与AsyncTask相比,Loader有了更多处理上的优化,比如加载被中断后自动保存现场等,本文在此不做深入探讨。</p> <p> </p> <p>综上所述,在Android中实现多线程编程的方法有:</p> <ol> <li>手动新建线程,使用Handler进行通信</li> <li>使用线程池新建线程,使用Handler进行通信</li> <li>使用Android提供的AsyncTask工具类</li> <li>使用Android提供的Loader工具类</li> </ol> <p>来自: <a href="/misc/goto?guid=4959672198112287714" rel="nofollow">http://blog.csdn.net/superxlcr/article/details/50890769</a></p>