Android多线程的使用
来源:互联网 发布:看网络电视用什么盒子好 编辑:程序博客网 时间:2024/06/09 13:08
在很多编程语言中,线程都是一个重要的组成部分,多线程的支持可以给程序员更加灵活的程序功能实现代码编写方式,线程一般用于处理一些比较耗时的任务(下载文件、复制或者移动文件。。。)。那么Android作为一个最热门的移动操作系统,当然支持多线程编程(严格来说应该是java支持多线程编程,Android使用的是java编程语言)。下面来看一下怎么去使用Android多线程:
Android的线程和java的线程使用的都是相同的语法,如果你熟悉java,那么一定不会感到难,新建一个子线程:
Thread thread = new Thread(new Runnable() { @Override public void run() { // 这里加入我们要做的事情的代码 } });thread.start();
开启一个子线程的标准写法就是这样,在子线程的run方法里面我们可以加入我们想要做的事情的代码逻辑,但是值得注意的是:子线程里面是不可以更新UI的,如果要更新UI必须在UI线程(主线程)中完成。例:
import android.sax.StartElementListener;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity { private TextView textView = null; private Button button = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.textView); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { thread.start(); } }); } Thread thread = new Thread(new Runnable() { @Override public void run() { textView.setText("子线程更新UI测试"); } });}
布局文件就不贴了,比较简单,一个TextView控件和一个Button按钮控件
如果采用以上写法,程序运行单击按钮的时候会崩溃退出,如图:
看看LogCat打印的日志:
大致意思就是只有创建了这个View对象的才能够对这个View的UI进行操作(即只有UI线程才能更新UI)。那么我们怎么才能通过子线程来更新UI呢?直接更新肯定是不行的,Android为我们提供了一个类:Handler,这个类可以对子线程发出的消息进行处理,那么我们就能通过将Handler类对象定义在主线程中然后对子线程发来的消息进行处理(更新UI)来达到子线程更新UI的目的,我们仍然以上面那个例子来看,我们把MainActivity.java改一下:
import android.os.Handler;import android.os.Message;import android.sax.StartElementListener;import android.support.v7.app.AppCompatActivity;import android.os.Bundle;import android.view.View;import android.widget.Button;import android.widget.TextView;public class MainActivity extends AppCompatActivity { private static final int UPDATE_UI = 1; private TextView textView = null; private Button button = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textView = (TextView) findViewById(R.id.textView); button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { thread.start(); } }); } /* * 新建一个Handler 对象,重写handleMessage方法用于处理 子线程发送过来的消息 */ Handler handler = new Handler() { @Override public void handleMessage(Message msg) { /* * 对发送过来的消息进行处理 */ switch (msg.what) { case UPDATE_UI: textView.setText("子线程更新UI测试"); break; } } }; Thread thread = new Thread(new Runnable() { @Override public void run() { /* * 新建一个 Message 对象做为消息发送给Handler对象去处理 */ Message message = new Message(); message.what = UPDATE_UI; handler.sendMessage(message); } });}
可以看到,我们在MainActivity.java里面加了一个新的全局对象:handler,这个对象就是用于处理子线程发送过来的消息的(在handleMessage方法里面)。而在子线程中,我们加了几行代码: 其实就是发送消息的代码,将消息发送到消息队列然后让Handler对象去处理。ok,再运行一下:
成功更新了UI,而且并没有报错。这就是典型的异步通信的例子:子线程执行的结果返回给主线程然后由主线程进行对应的处理。我们来看一下这里面涉及到的一些东西:
Message:其实就是一个普通的类,保存了一些数据,可以储存少量信息数据,在不同的线程中作为“通信者”的角色,可以用于不同的线程交换数据。
Handler:处理者的意思,它本身也用于在子线程中发送消息进入消息队列中,它也对对消息队列中通过它自己发送的Message对象取出并且进行一些处理(通过handleMessage方法),对于Handler对象来说,它不分线程,只要是它发送的消息,它都会接收到
消息队列(MessageQueue):储存通过Handler对象发送的消息,这些消息在被Handler对象取出处理之前会一直存在消息队列中,每个线程只会有一个MessageQueue对象,Android已经帮我们封装起来了,我们并不需要管它
Looper:管理每个线程中的MessageQueue,负责把消息队列中的数据取出,然后交给Handler对象去处理这条消息,每个线程也只会有一个Looper对象
整个过程:
画的不好,理解原理就行了。
其实为了方便我们在子线程更新UI操作,Android提供了一个更加好用的类:AsyncTask,下面来看一下这个类的用法:
首先,它是一个抽象类,我们必须继承它,并且要为它提供3个泛型参数,一般的写法:
public class MyAsyncTask extends AsyncTask<Void, Integer, Boolean> { ....}
之后必须重写里面的一个抽象方法:doInBackground方法,这个方法即为进行我们要执行的耗时操作的方法,我们在这个方法里面进行我们需要的耗时操作,其实,我们一般会重写里面的4个方法:
onPreExecute() : 这个方法会在任务(doInBackground方法)开始之前调用,用于一些初始化的操作
doInBackground(Void…) : 这个方法就是在后台进行的耗时操作的方法,里面的所有代码都会在Android新建的一个子线程中运行,并且这个方法不可以进行UI操作(这个方法是在子线程中执行的),我们可以调用publishProgress(Intger…)方法来调用专门的UI更新方法来进行UI的更新。这个方法的返回值会传递给onPostExecute方法用于收尾
onProgressUpdate(Interge…) : 这个方法里进行UI的更新,当在doInBackground方法中调用了publishProgress方法之后,就会调用这个方法来及时的进行UI更新
onPostExecute(Boolean result) : 这个方法用于收尾,当doInBackground方法执行完成之后就会调用这个方法,主要是对于操作进行判断是否成功…
可以看到,我们在继承AsyncTask传入的三个泛型参数:一个参数类型是doInBackGround方法的参数类型,第二个参数的参数类型是onProgressUpdate方法的参数类型,(这里传入Integer用于进度的UI更新),第三个参数的参数类型为onPostExecute方法的参数类型,并且也是doInBackground方法返回值类型(这里传入Boolean用于判断执行的结果),当然,我们可以根据自己的需要来传入对应的参数类型。那么一个基本的自定义AsyncTask就成了这样:
import android.os.AsyncTask;/** * Created by Administrator on 2017/2/26. */public class MyAsyncTask extends AsyncTask<Void, Integer, Boolean> { @Override public void onPreExecute() { } @Override protected Boolean doInBackground(Void... params) { int percent = 0; try { while (true) { // 执行一些操作, publishProgress(percent); // 调用onProgressUpdate方法更新UI if (percent >= 100) { return true; } } } catch(Exception e) { return false; } } @Override protected void onProgressUpdate(Integer... progress) { // Integer... progress 这种形式的写法相当于一个int类型的数组,其他同理 // 在这里更新UI } @Override protected void onPostExecute(Boolean result) { // 这里做一些操作的结果提示 }}
OK,如果我们要调用这个类的对象,我们只需编写:
MyAsyncTask myAsyncTask = new MyAsyncTask();myAsyncTask.execute();
因为我们定义的MyAsyncTask中第一个参数为Void,所以这里调用execute方法不需要传入参数,但是如果换成别的类型那就需要了,这里根据具体需求来运用。 接下来仍然以上面那个例子,我们用AsyncTask来实现它:
在原来的工程基础上新建一个类MyAsyncTask.java:
import android.content.Context;import android.os.AsyncTask;import android.widget.TextView;import android.widget.Toast;import org.w3c.dom.Text;/** * Created by Administrator on 2017/2/26. */public class MyAsyncTask extends AsyncTask<Void, Integer, Boolean> { private Context myContext = null; private TextView myTextView = null; private String string; public MyAsyncTask(Context context, TextView textView, String str) { myContext = context; myTextView = textView; string = str; } @Override protected Boolean doInBackground(Void... params) { Log.i("ThreadTest", "MyAsyncTask thread: " + Thread.currentThread().getId()); // 打印出当前线程的 id 信息 int percent = 100; try { publishProgress(percent); return true; } catch(Exception e) { return false; } } @Override protected void onProgressUpdate(Integer... progress) { // Integer... progress 这种形式的写法相当于一个int类型的数组,其他同理 // 在这里更新UI if(100 == progress[0]) { myTextView.setText(string); } } @Override protected void onPostExecute(Boolean result) { // 这里做一些操作的结果提示 String str = "操作"; if(result) { str += "完成"; } else { str += "失败"; } Toast.makeText(myContext, str, Toast.LENGTH_SHORT).show(); }}
我们还要对MainActivity.java进行小小的改动:
就是将按钮的点击事件成了执行MyAsyncTask对象中的方法,并且用一个LogCat信息打印出 MainActivity 所在的线程 Id,对于MyAsyncTask来说,基本的和上面的用法差不多,只不过用AsyncTask来进行这么简单的处理实在是有点大材小用,下面看结果:
开始运行的界面和上面没多大区别,点击按钮之后出现操作成功的提示并且TextView的文字也更新了。并且LogCat 中打印的信息中我们可以发现 MyAsyncTask 和 MainActivity 确实不是在同一个线程之中(MyAsyncTask 的 doInBackground 方法在子线程中执行), 那么一个AsyncTask的简单用法就完成了。
如果博客中有什么不正确的地方还请多多指点。
谢谢观看。。。
- Android多线程的使用
- Android多线程的简单使用
- Android 多线程的基本使用
- Android 多线程 AsyncTask的使用
- 【多线程】多线程的使用
- Android-多线程:AsyncTask多线程使用
- android程序中使用多线程的问题
- Android 多线程,线程池的使用
- Android 中多线程的简单使用
- Android Sqlite的使用(多线程优化)
- 多线程/多进程的使用场景-Android
- Android多线程开发之HandlerThread的使用
- Android多线程开发之IntentService的使用
- Android多线程开发之AsyncTask的使用
- Android中使用多线程的各种姿势
- android Java多线程的使用解析
- Android多线程--HandlerThread的使用以及原理
- Android多线程--IntentService的使用及原理
- jQuery选择器课堂随笔
- Java多线程线程池(1)--基本概述
- CSS-水平和垂直居中的总结
- 浅谈Android , 以及Android程序的Hello World
- 后台开发学习(一)对HTML5简单学习
- Android多线程的使用
- Windows10+CentOS7双系统(UEFI+GPT)
- 四方定理 动态规划
- 汴河 2017初。
- 对线性回归,logistic回归和一般回归的认识 及Demo(转载)
- 斗地主背后的思考
- POJ 2104 K-th Number 区间第K大,可持久化线段树
- Insert Delete GetRandom O(1)
- 32、随机文件与定位操作