Android 多线程编程

ka7231zfvl 4年前
   <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>