Android移动开发之【Android实战项目】DAY6-安卓多线程

Android只会存在两种线程:UI主线程(UI thread)和工作线程(work thread).

我们知道Android的UI主线程主要负责处理用户的按键事件、用户的触屏事件以及屏幕绘图事件等,在子线程中处理耗时的任务,任务完成后通过Handler通知UI主线程更新UI界面

注意:

不能阻塞UI主线程,也就是不能在UI主线程中做耗时的操作,如网络连接,文件的IO;
只能在UI主线程中做更新UI的操作;

ANR:

anr:application not reponse:应用程序无响应
  主线程:UI线程
  anr产生的原因:主线程需要做很多重要的事情,响应点击事件,更新ui,如果在主线程里面阻塞时间过久,应用程序就会无响应,为了避免应用程序出现anr,所有的耗时的操作,都应该放在子线程中执行。

1、使用线程处理 耗时比较长的“业务”

有以下几种方式:

1)Activity.runOnUiThread(Runnable)

2)View.post(Runnable) ;View.postDelay(Runnable , long)

3)Handler

4)AsyncTask

2、使用方法举例

1)Activity.runOnUiThread(Runnable)

采用runOnUiThread(new Runnable()),这要实现Runnable借口,我们可以直接在这个线程中进行UI的更新。是api提供的方法,较为便捷。

new Thread(){
        @Override
        public void run() {
            final String result = LoginServices.loginByGet(username, password);
            if(result != null){
                //成功
                runOnUiThread(new Runnable() {
                     
                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        Toast.makeText(MainActivity.this, result, 0).show();
                    }
                });
            }else{
                //请求失败
                runOnUiThread(new Runnable() {
                     
                    @Override
                    public void run() {
                        // TODO Auto-generated method stub
                        Toast.makeText(MainActivity.this, "请求失败", 0).show();
                    }
                });
            }
             
        };
    }.start();

2)View.post(Runnable) ;
handler.post®其实这样并不会新起线程,只是执行的runnable里的run()方法,却没有执行start()方法,所以runnable走的还是UI线程。

如果像这样,是可以操作ui,但是run还是走在主线程,见打印出来的Log线程名字是main,说明是主线程。
这就是为什么可以直接在run方法里操作ui,因为它本质还是ui线程

handler.post(new Runnable(){
 
  public void run(){
 
  Log.e("当前线程:",Thread.currrentThread.getName());//这里打印de结果会是main
 
  setTitle("哈哈");
 
      }
 
});

3)Handler
Thread与Handler组合,比较常见

这里在加载100张图片,然后没加载完成一个用handler 返回给UI线程一张图片并显示,最后加载完成返回一个List给UI线程 ,Handler就是一个后台线程与UI线程中间的桥梁

package com.android.wei.thread;
 
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
 
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
 
public class Activity01 extends Activity {
    /** Called when the activity is first created. */
  
    /**读取进度**/
    public final static int LOAD_PROGRESS =0;
    
    /**标志读取进度结束**/
    public final static int LOAD_COMPLETE = 1;
    /**开始加载100张图片按钮**/
    Button mButton = null;
    
    /**显示内容**/
    TextView mTextView = null;
    
    /**加载图片前的时间**/
    Long mLoadStart = 0L;
    /**加载图片完成的时间**/
    Long mLoadEndt = 0L;
    
    Context mContext = null;
    /**图片列表**/
    private List<Bitmap> list;
    /**图片容器**/
    private ImageView mImageView;
    //接受传过来得消息
    Handler handler = new Handler(){
    	public void handleMessage(Message msg){
    		switch(msg.what){
    		case LOAD_PROGRESS:
    			Bitmap bitmap = (Bitmap)msg.obj;
    			mTextView.setText("当前读取到第"+msg.arg1+"张图片");
    			mImageView.setImageBitmap(bitmap);
    			break;
    		case LOAD_COMPLETE:	
    			list = (List<Bitmap>) msg.obj;
    			mTextView.setText("读取结束一共加载"+list.size()+"图片");
    			break;
    		}
    		super.handleMessage(msg);
    	}
    };
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mContext = this;
        setContentView(R.layout.main);
        mButton =(Button) findViewById(R.id.button1);
        mTextView=(TextView) findViewById(R.id.textView1);
        mImageView =(ImageView) this.findViewById(R.id.imageview);
        mTextView.setText("点击按钮加载图片");
        mButton.setOnClickListener(new OnClickListener(){
        	public void onClick(View v){
        		//调用方法
        		LoadImage();
        	}
        });
        	
        
    }
    public void LoadImage(){
    	new Thread(){
    		public void run(){
    			mLoadStart = System.currentTimeMillis();
    			List<Bitmap> list = new ArrayList<Bitmap>();
    			for(int i =0;i<100;i++){
    				Bitmap bitmap=ReadBitmap(mContext,R.drawable.icon);
    				Message msg = new Message();
    				msg.what = LOAD_PROGRESS;
    				msg.arg1 = i+1;
    				list.add(bitmap);
    				msg.obj = bitmap;
    				handler.sendMessage(msg);
    			}
    			mLoadEndt = System.currentTimeMillis();
    			Message msg = new Message();
    			msg.what = LOAD_COMPLETE;
    			msg.obj=list;
    			msg.arg1 = (int) (mLoadEndt - mLoadStart);
    			handler.sendMessage(msg);
    			
    		}
    	}.start();
    }
    public Bitmap ReadBitmap(Context context,int resId){
    	BitmapFactory.Options opt = new BitmapFactory.Options();
    	opt.inPreferredConfig = Bitmap.Config.RGB_565;
    	opt.inPurgeable = true;
    	opt.inInputShareable = true;
    	InputStream is = context.getResources().openRawResource(resId);
    	return BitmapFactory.decodeStream(is, null, opt);
    }
}

4)AsyncTask 异步任务 规范型很强,能够时时反映更新的情况
(1)基本概念

我们如果要定义一个AsyncTask,就需要定义一个类来继承AsyncTask这个抽象类,并实现其唯一的一个 doInBackgroud 抽象方法。要掌握AsyncTask,我们就必须要一个概念,总结起来就是:3个泛型,4个步骤。

AsyncTask <Params, Progress, Result>

3个泛型:

Params: 这个泛型指定的是我们传递给异步任务执行时的参数的类型
Progress: 这个泛型指定的是我们的异步任务在执行的时候将执行的进度返回给UI线程的参数的类型
Result: 这个泛型指定的异步任务执行完后返回给UI线程的结果的类型

我们在定义一个类继承AsyncTask类的时候,必须要指定好这三个泛型的类型,如果都不指定的话,则都将其写成Void,例如:

AsyncTask <Void, Void, Void>

4个步骤:当我们执行一个异步任务的时候,其需要按照下面的4个步骤分别执行

onPreExecute(): 这个方法是在执行异步任务之前的时候执行,并且是在UI Thread当中执行的,通常我们在这个方法里做一些UI控件的初始化的操作,例如弹出要给ProgressDialog
doInBackground(Params… params): 在onPreExecute()方法执行完之后,会马上执行这个方法,这个方法就是来处理异步任务的方法,Android操作系统会在后台的线程池当中开启一个worker thread来执行我们的这个方法,所以这个方法是在worker thread当中执行的,这个方法执行完之后就可以将我们的执行结果发送给我们的最后一个onPostExecute 方法,在这个方法里,我们可以从网络当中获取数据等一些耗时的操作
onProgressUpdate(Progess… values): 这个方法也是在UI Thread当中执行的,我们在异步任务执行的时候,有时候需要将执行的进度返回给我们的UI界面,
例如下载一张网络图片,我们需要时刻显示其下载的进度,就可以使用这个方法来更新我们的进度。这个方法在调用之前,我们需要在 doInBackground 方法中调用一个 publishProgress(Progress) 的方法来将我们的进度时时刻刻传递给 onProgressUpdate 方法来更新
onPostExecute(Result… result): 当我们的异步任务执行完之后,就会将结果返回给这个方法,这个方法也是在UI Thread当中调用的,我们可以将返回的结果显示在UI控件上

(2)举例

同样加载100张图片

package com.android.wei.thread;
 
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
 
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
 
public class Activity02 extends Activity{
	
	/**开始StartAsync按钮**/
	Button mButton = null;
	
	Context mContext = null;
	
	//内容显示出来
	TextView mTextView = null;
	
	//Timer 对象
	Timer mTimer = null;
	
	/** timerTask 对象**/
	TimerTask mTimerTask = null;
	
	/**记录TimerId**/
	int mTimerId =0;
	/**图片列表**/
	private List<Bitmap> list;
	/**图片容器**/
	private ImageView mImageView;
	public void onCreate(Bundle s){
		super.onCreate(s);
		setContentView(R.layout.main);
		mContext = this;
		mButton =(Button) this.findViewById(R.id.button1);
		mImageView =(ImageView)this.findViewById(R.id.imageview);
		mTextView =(TextView) this.findViewById(R.id.textView1);
		mButton.setOnClickListener(new OnClickListener(){
			public void onClick(View v){
				StartAsync();
			}
		});
		
		
	}
	public void StartAsync(){
		new AsyncTask<Object,Object,Object>(){
            protected void onPreExecute(){
            	//首先执行这个方法,它在UI线程中,可以执行一些异步操作
            	mTextView.setText("开始加载进度");
            	super.onPreExecute();
            }
			@Override
			protected Object doInBackground(Object... params) {
				// TODO Auto-generated method stub
				//异步后台执行,执行完毕可以返回出去一个结果 Object 对象
				//得到开始加载得时间
				list = new ArrayList<Bitmap>();
				for(int i =0;i<100;i++){
					Bitmap bitmap =ReadBitmap(mContext,R.drawable.icon);					
					final ByteArrayOutputStream os = new ByteArrayOutputStream();
					bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
					byte[] image = os.toByteArray();
					Bundle bundle = new Bundle();
					bundle.putInt("index", i);
					bundle.putByteArray("image", image);
					publishProgress(bundle);
					list.add(bitmap);
					
				}
				
				return list;
			}
			protected void onPostExecute(Object result){
				//doInBackground 执行之后在这里可以接受到返回结果的对象
				List<Bitmap> list = (List<Bitmap>) result;
				mTextView.setText("一共加载了"+list.size()+"张图片");
			   super.onPostExecute(result);
			}
			protected void onProgressUpdate(Object... values){
				//时时拿到当前的进度更新UI
				Bundle bundle = (Bundle)values[0];
				byte[] image = bundle.getByteArray("image");
				Bitmap bitmap = BitmapFactory.decodeByteArray(image, 0, image.length);
				int index = bundle.getInt("index");
				mTextView.setText("当前加载进度"+index);
				mImageView.setImageBitmap(bitmap);
				super.onProgressUpdate(values);
			}
			
		}.execute();
	}
	public Bitmap ReadBitmap(Context context,int resId){
		BitmapFactory.Options opt = new BitmapFactory.Options();
		opt.inPreferredConfig = Bitmap.Config.RGB_565;
		opt.inPurgeable = true;
		opt.inInputShareable = true;
		InputStream is = context.getResources().openRawResource(resId);
		return BitmapFactory.decodeStream(is, null, opt);
	}
	
}

(3)举一反三

通过AsyncTask来从网络上下载一张图片

下面我们就通过两个代码示例,来看看如何通过AsyncTask来从网络上下载一张图片,并更新到我们的ImageView控件上。

①下载图片时,弹出一个ProgressDialog,但是不显示实时进度

我们来看看布局文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 
    <ImageView
        android:id="@+id/imageView"
        android:layout_width="wrap_content"
        android:layout_height="200dp"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:scaleType="fitCenter"/>
 
    <Button
        android:id="@+id/button"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/imageView"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="41dp"
        android:text="从网络上下载一张图片" />
 
</RelativeLayout>

就是很简单的一个ImageView控件和一个Button控件,当点击Button控件时,弹出一个ProgressDialog,然后开启一个异步任务,从网络中下载一张图片,并更新到我们的ImageView上。这里还要注意一点,如果我们要使用手机访问网络,必须还要给其授权才行,在后续的学习当中,将会详细讲解Android当中的授权的知识。我们来看看

AndroidManifest.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.xiaoluo.android_asynctast"
    android:versionCode="1"
    android:versionName="1.0" >
 
    <uses-sdk
        android:minSdkVersion="8"
        android:targetSdkVersion="18" />
    
    <!-- 授权手机能够访问网络 -->
    <uses-permission android:name="android.permission.INTERNET"/>
    
    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.xiaoluo.android_asynctast.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
 
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
 
</manifest>

接下来我们来看看我们的Activity代码:

public class MainActivity extends Activity
{
    private Button button;
    private ImageView imageView;
    private ProgressDialog progressDialog;
    private final String IMAGE_PATH = "http://developer.android.com/images/home/kk-hero.jpg";
//    private final String IMAGE_PATH2 = "http://ww2.sinaimg.cn/mw690/69c7e018jw1e6hd0vm3pej20fa0a674c.jpg";
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        button = (Button)findViewById(R.id.button);
        imageView = (ImageView)findViewById(R.id.imageView);
        //    弹出要给ProgressDialog
        progressDialog = new ProgressDialog(MainActivity.this);
        progressDialog.setTitle("提示信息");
        progressDialog.setMessage("正在下载中,请稍后......");
        //    设置setCancelable(false); 表示我们不能取消这个弹出框,等下载完成之后再让弹出框消失
        progressDialog.setCancelable(false);
        //    设置ProgressDialog样式为圆圈的形式
        progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
        
        button.setOnClickListener(new View.OnClickListener()
        {
            @Override
            public void onClick(View v)
            {
         // 在UI Thread当中实例化AsyncTask对象,并调用execute方法
                new MyAsyncTask().execute(IMAGE_PATH);
            }
        });
    }
    
    /**
     * 定义一个类,让其继承AsyncTask这个类
     * Params: String类型,表示传递给异步任务的参数类型是String,通常指定的是URL路径
     * Progress: Integer类型,进度条的单位通常都是Integer类型
     * Result:byte[]类型,表示我们下载好的图片以字节数组返回
     * @author xiaoluo
     *
     */
    public class MyAsyncTask extends AsyncTask<String, Integer, byte[]>
    {
        @Override
        protected void onPreExecute()
        {
            super.onPreExecute();
            //    在onPreExecute()中我们让ProgressDialog显示出来
            progressDialog.show();
        }
        @Override
        protected byte[] doInBackground(String... params)
        {
            //    通过Apache的HttpClient来访问请求网络中的一张图片
            HttpClient httpClient = new DefaultHttpClient();
            HttpGet httpGet = new HttpGet(params[0]);
            byte[] image = new byte[]{};
            try
            {
                HttpResponse httpResponse = httpClient.execute(httpGet);
                HttpEntity httpEntity = httpResponse.getEntity();
                if(httpEntity != null && httpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_OK)
                {
                    image = EntityUtils.toByteArray(httpEntity);
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
            finally
            {
                httpClient.getConnectionManager().shutdown();
            }
            return image;
        }
        @Override
        protected void onProgressUpdate(Integer... values)
        {
            super.onProgressUpdate(values);
        }
        @Override
        protected void onPostExecute(byte[] result)
        {
            super.onPostExecute(result);
            //    将doInBackground方法返回的byte[]解码成要给Bitmap
            Bitmap bitmap = BitmapFactory.decodeByteArray(result, 0, result.length);
            //    更新我们的ImageView控件
            imageView.setImageBitmap(bitmap);
            //    使ProgressDialog框消失
            progressDialog.dismiss();
        }
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
}

我们来看看效果图:
在这里插入图片描述

发布了552 篇原创文章 · 获赞 129 · 访问量 4万+

猜你喜欢

转载自blog.csdn.net/weixin_43838785/article/details/104246338