您的位置:首页 > 移动开发 > Android开发

Android线程(一)

2015-10-15 20:56 501 查看

当我们第一次启动一个Android程序的时候,一个被称为“main”的线程就被自动的创建了。它被称为主线程或者UI线程,它是非常重要的,因为它负责分发时间给响应的控件,其中包含屏幕绘图事件。主线程贯穿用户和Android
widget(Android控件)的交互的整个过程
。例如,我们触摸了屏幕上的按钮(Button),UI线程派发触摸(touch)事件给这个widget,紧接着这个widget设置它自身为按下状态并向事件队列发送一个无效(invalidate)的请求。UI线程会把这个请求移出事件队列并通知按钮在屏幕上重新绘制自身。

单线程模型会在没有考虑到它的影响的情况下引起Android应用程序性能低下,因为所有的任务都在同一个线程中执行,如果执行一些耗时的操作,如访问网络或查询数据库,会阻塞整个用户界面。当在执行一些耗时的操作的时候,不能及时的分发事件,包括用户界面重绘事件。从用户的角度来看,应用程序看上去像卡的很厉害。更糟糕的是,如果阻塞应用程序的时间过长(现在大概是5秒钟)Android会向用户提示一些信息,即弹出一个“application
not responding(应用程序没有响应)”的对话框。
于是,我验证了一下,写了一个简单的含有一个按钮的程序,并为按钮注册一个点击事件,并在事件处理器汇总调用了Thread.sleep(60000)。在按下这个按钮之后恢复按钮的正常状态之前,它会保持按下一段时间(上面说的是5秒,我检验的的时间远大于5秒),之后弹出提示框。代码如下:
package com.example.study;

import android.os.Bundle;

import android.app.Activity;

import android.graphics.Color;

import android.view.View;

import android.widget.Button;

/**

*

* ClassName:MainActivity

* Function: 测试线程(实现:阻塞应用程序的时间过长,弹出对话框)

* Reason: TODO ADD REASON

*

* @author perArhter

* @version

* @since Ver 1.1

* @Date 2015
2015-10-15 下午9:31:33

*

* @see

*/

public class MainActivity extends Activity {

//定义一个按钮

private Button button1;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//按钮初始化

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

//按钮初始状态为为按下状态( pressed 加压的;紧迫的)

button1.setPressed(false);

//按钮初始化颜色为粉色

//button1.setBackgroundColor(Color.parseColor("#FFC0CB"));

//按钮点击事件

button1.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

//按下的一瞬间,按钮的背景颜色变为番茄红

//button1.setBackgroundColor(Color.parseColor("#FF6347"));

//设置按钮为按下状态

button1.setPressed(true);

//然后判断按钮是否是处于被按下的状态,是则过60秒后弹起

if(button1.isPressed()){

try {

//睡眠六秒

Thread.sleep(60000);

//按钮颜色恢复初始色

//button1.setBackgroundColor(Color.parseColor("#FFC0CB"));

//设置按钮为抬起状态

button1.setPressed(false);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

});

}



现在知道一定要避免在主线程中执行过长时间的操作,那我们可能会使用额外的线程(后台线程或者工作线程)去执行操作。让我们来看看例子:

package com.example.study;

import android.os.Bundle;

import android.app.Activity;

import android.view.View;

import android.widget.Button;

import android.widget.ImageView;

/**

*

* ClassName:MainActivity

* Function: 测试线程(使用额外的线程,延时6秒后给ImageView设置图片)

* Reason: TODO ADD REASON

*

* @author perArther

* @version

* @since Ver 1.1

* @Date 2015
2015-10-15 下午10:30:13

*

* @see

*/

public class MainActivity extends Activity {

//定义一个按钮

private Button button1;

//定义一个ImageView

private ImageView imageView1;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//按钮初始化

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

//ImageView初始化

imageView1 = (ImageView) findViewById(R.id.imageView1);

button1.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

//另外创建一个线程

new Thread(new Runnable() {

@Override

public void run() {

try {

//延时6秒

Thread.sleep(6000);

//延时完毕后让ImageView放上图片

imageView1.setImageResource(R.drawable.ic_launcher);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}).start();

}

});

}

}
首先,代码看上去是解决的之前的问题,因为它不会阻塞UI线程,可是,它违背了单一线程模式:Android UI操作并不是线程安全的,而且这些直接控制试图控件的操作必须在UI线程中执行。所以它在运行是会报错:Only the original thread that created
a view hierarchy can touch its views.意思是只有UI线程才能创建一个试图层次来触摸它的试图。
为了解决这个问题,Android提供了几种在其他线程中访问UI线程的方法,如下所示:
Activity.runOnUiThread(Runnable)
View.post(Runnable)
View.postDelayed(Runable,long)
Handler
检验四种方法是否可行:
package com.example.study;

import android.os.Bundle;

import android.os.Handler;

import android.os.Message;

import android.app.Activity;

import android.view.View;

import android.widget.Button;

import android.widget.ImageView;

/**

*

* ClassName:MainActivity

* Function: 测试线程(利用Android提供的四种方法,使用额外的线程,延时6秒后给ImageView设置图片)

* Reason: TODO ADD REASON

*

* @author perArther

* @version

* @since Ver 1.1

* @Date 2015
2015-10-15 下午11:05:15

*

* @see

*/

public class MainActivity extends Activity {

//定义一个按钮

private Button button1;

//定义一个ImageView

private ImageView imageView1;

//定义一个Handler

private Handler handler;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//按钮初始化

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

//ImageView初始化

imageView1 = (ImageView) findViewById(R.id.imageView1);

//初始化一个Handler对象

handler = new Handler(){

@Override

public void handleMessage(Message msg) {

if(msg.what==0x000) imageView1.setImageResource(R.drawable.ic_launcher);

super.handleMessage(msg);

}

};

//按钮的点击事件

button1.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

//另外创建一个线程

new Thread(new Runnable() {

@Override

public void run() {

try {

//延时60秒

Thread.sleep(6000);

//延时完毕后让ImageView放上图片

//=======================================================================================

/**

* 第一种Activity.runOnUiThread(Runnable)

*

* MainActivity.this.runOnUiThread(new Runnable() {

* @Override

* public void run() {

* imageView1.setImageResource(R.drawable.ic_launcher);

* }

* });

*

*/

//=======================================================================================

/**

* 第二中View.post(Runnable)

*

* imageView1.post(new Runnable() {

* @Override

* public void run() {

* imageView1.setImageResource(R.drawable.ic_launcher);

* }

* )};

*/

//=======================================================================================

/**

* 第三种View.postDelayed(Runnable,long)

*

* imageView1.postDelayed(new Runnable() {

* @Override

* public void run() {

* imageView1.setImageResource(R.drawable.ic_launcher);

* }

* },6000); // 这个就要延时前面的6秒加上这个6秒的时间,即延时12秒

*/

//=======================================================================================

/**

* 第四种Handler,先初始化一个Handler对象

*/

//延时完毕后,利用handler发送一个信息

handler.sendEmptyMessage(0x000);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}).start();

}

});

}

}



虽然,这四种方法解决了任务线程直接操作UI线程的View的问题,但是这些类或者方法同样会是我们的代码很复杂很难理解。然而当我们需要实现一些很复杂的操作并需要频繁的更新UI时者会变得很复杂。为了解决这个问题,Android1.5提供了一个工具类:AsyncTask(async [əˈsɪŋk] abbr. 异步,非同步),它简化了任务线程和主线程之间的通信。
AsyncTask的目的就是帮助你管理线程。
AsyncTask是抽象类。AsyncTask定义了三种泛型类型Params,Progress和Result。 AsyncTask<Params,Progress,Result>
其中Params指的是启动任务执行的输入参数,Progress指的是后台任务执行的百分比,Result指的是后台执行任务最终返回的结果。
AsyncTask的执行分为四个步骤,每一步都对应一个回调方法,这些方法不应该由程序调用,开发者需要做的就是实现这些方法。
1)继承AsyncTask
2)实现AsyncTask中定义的下面一个或几个方法
onPreExecute(),(execute ['eksɪkjuːt] vt. 实行;执行),该方法将在执行实际的后台操作前被UI线程调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条,或者一些空间的实例化,这个方法可以不用实现。
doInBackground(Params...),将在onPreExecute方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台处理工作。可以调用publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
onProgressUpdate(Result),在PublishProgress方法被调用后,UI线程将调用这个方法从而在界面上展示任务的进展情况。
onPostExecute(Result),在DoInBackground执行之后,onPostExecute方法将被UI线程调用,后台的计算结果将通过该方法传递到UI线程,并在界面上展示给用户。
onCancelled(),在用户取消线程操作的时候调用。在主线程中调用onCancelled()的时候调用。
为了正确的使用AsyncTask类,以下是几条必须遵守的准则:

 1) Task的实例必须在UI 线程中创建

 2) execute方法必须在UI 线程中调用

 3) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params...), onProgressUpdate(Progress...)这几个方法,需要在UI线程中实例化这个task来调用。

 4) 该task只能被执行一次,否则多次调用时将会出现异常

doInBackground方法和onPostExecute的参数必须对应,这两个参数在AsyncTask声明的泛型参数列表中指定,第一个为doInBackground接受的参数,第二个为显示进度的参数,第第三个为doInBackground返回和onPostExecute传入的参数。

了解了AsyncTask之后,利用它来实现之前的延时显示图片,代码如下:
package com.example.study;

import android.os.AsyncTask;

import android.os.Bundle;

import android.app.Activity;

import android.view.View;

import android.widget.Button;

import android.widget.ImageView;

/**

*

* ClassName:MainActivity

* Function: 测试线程(利用AsyncTask实现延时6秒后给ImageView设置图片)

* Reason: TODO ADD REASON

*

* @author perArther

* @version

* @since Ver 1.1

* @Date 2015
2015-10-15 下午11:57:20

*

* @see

*/

public class MainActivity extends Activity {

//定义一个按钮

private Button button1;

//定义一个ImageView

private ImageView imageView1;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

//按钮初始化

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

//ImageView初始化

imageView1 = (ImageView) findViewById(R.id.imageView1);

//按钮点击事件

button1.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

//给定执行参数,为Integer类型

new ImageTask().execute(R.drawable.ic_launcher);

}

});

}

/**

* AsyncTask是抽象类.AsyncTask定义了三种泛型类型 Params,Progress和Result。

   * Params 启动任务执行的输入参数,比如HTTP请求的URL。

   * Progress 后台任务执行的百分比。

   * Result 后台执行任务最终返回的结果

*/

private class ImageTask extends AsyncTask<Integer, Integer, Integer>{

//AsyncTask定义了三种泛型类型,输入参数为Integer型,后台任务执行的百分比为Integer型,最终返回的结果为Integer型

@Override

protected Integer doInBackground(Integer... params) {

try {

//延时60秒

Thread.sleep(6000);

} catch (InterruptedException e) {

e.printStackTrace();

}

return params[0];

}

@Override

protected void onPostExecute(Integer result) {

//设置ImageView的前景图片

imageView1.setImageResource(result);

super.onPostExecute(result);

}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息