天天看點

Android開發之異步詳解(一)Thread+Handler3.   Thread+Handler實作異步操作執行個體

請尊重他人的勞動成果,轉載請注明出處:  Android開發之異步詳解(一)Thread+Handler 

http://blog.csdn.net/fengyuzhengfan/article/details/40211589

        在Android實際開發工程中經常會進行一些諸如:檔案讀寫、通路網絡等耗時的操作,這些耗時的操作是不建議放到UI線程裡的。是以我們會新開一個線程,在子線程中進行這些耗時的操作,耗時操作過程中,UI經常需要更新,但Android是不允許在子線程中修改UI的。是以就出現了Thread+Handler機制,Thread通過handler向主線程發送消息、傳遞資料,來更新UI。下面就介紹如何通過Thread+Handler方式實作異步操作。

1.   什麼是Handler消息傳遞機制?

        當一個程式第一次啟動時,Android會同時啟動一條主線程(MainThread),主線程主要負責處理與UI相關的事件,如使用者的按鍵事件、使用者接觸螢幕的事件及螢幕繪圖事件,并把相關的事件分發到對應的元件進行處理。是以主線程通常又被叫做UI線程。

        Android的消息傳遞機制是另一種形式的“事件處理'這種機制主要是為了解決Android 應用的多線程問題——Android平台隻允許UI線程修改Activity裡的UI元件,這樣就會導緻新啟動的線程無法動态改變界面元件的屬性值。但在實際Android應用開發中,尤其是涉及動畫的遊戲開發中,需要讓新啟動的線程周期性地改變界面元件的屬性值,這就需要借助于 Handler的消息傳遞機制來實作了。

2.   Handler 類簡介

Handler類的主要作用有兩個:

1)  在新啟動的線程中發送消息。

2)  在主線程中擷取、處理消息。

       上面的說法看上去很簡單,似乎隻要分成兩步即可:在新啟動的線程中發送消息;然後在主線程中擷取、并處理消息。但這個過程涉及一個問題:新啟動的線程何時發送消息呢?主線程何時去擷取并處理消息呢?這個時機顯然不好控制。

       為了讓主線程能“适時”地處理新啟動的線程所發送的消息,顯然隻能通過回調的方式來實作——我們者隻要重寫Handler類中處理消息的方法,當新啟動的線程發送消息時,消息會發送到與之關聯的MessageQueue,而Handler會不斷地從MessageQueue中擷取并處理消息——這将導緻Handler類中處理消息的方法被回調。

Handler類包含如下方法用于發送、處理消息:

1)        void  handleMessage(Message  msg):處理消息的方法。該方法通常用于被重寫。

2)        final boolean hasMessages(intwhat):檢査消息隊列中是否包含what屬性為指值的消息。

3)        final boolean hasMessages(intwhat, Object object):檢査消息隊列中是否包含 what屬性為指定值且object屬性為指定對象的消息。

4)        多個重載的Message obtainMessage():擷取消息。sendEmptyMessage(int what):發送空消息。

5)        final booleansendEmptyMessageDelayed(int what, long delayMillis):指定多少毫秒之後發送空消息。

6)        final booleansendMessage(Message msg)立即發送消息。

7)        final booleansendMessageDelayed(Message msg, long delayMillis):指定多少毫秒之後發送消息。

借助于上面這些方法,程式可以友善地利用Handler來進行消息傳遞。

3.   Thread+Handler實作異步操作執行個體

package com.jph.sp;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.SimpleAdapter.ViewBinder;

/**
 * 擷取所有軟體資訊
 * 1.通過異步的方式顯示系統中所有軟體
 * 2.單擊打開指定軟體
 * 3.将所有軟體的包名和activity名儲存的本地SharedPreferences
 * @author jph
 * Date:2014.09.21
 */
public class ScanPackage1 extends Activity {
	/**掃描成功**/
	private final static int FLAG_LOAD_SUCCESS=0x10001;
	private final static int SCANNING=0x10002;	
	private ListView list;
	private List<Map<String, Object>>items=new ArrayList<Map<String,Object>>();
	private SimpleAdapter adapter;
	// 取得所有安裝軟體資訊
	private List<PackageInfo> allPackageInfos;
	// 取得自己安裝的軟體資訊
	private List<PackageInfo> userPackageInfos;
	// 取得系統安裝的軟體資訊
	private List<PackageInfo> sysPackageInfos;	
	Handler mHandler=new Handler(){
		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			switch (msg.what) {
			case FLAG_LOAD_SUCCESS://完成掃描
				
				break;
			case SCANNING://正在掃描					
				items.add((Map<String, Object>) msg.obj);
				//通知擴充卡資料改變
				adapter.notifyDataSetChanged();				
				break;

			default:
				break;
			}			
		}			
	};
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.sp_layout);
		list=(ListView)findViewById(R.id.list);
		new ScanThread().start();
		adapter=new SimpleAdapter(this, items, R.layout.line, new String[]{
				"imgIco","appName","packageName"
		}, new int[]{R.id.imgIco,R.id.tvAppName,R.id.tvAppDesc});
		list.setAdapter(adapter);
		//ViewBinder該類可以幫助SimpleAdapter加載圖檔(如:Bitmap,Drawable)
		adapter.setViewBinder(new ViewBinder() {			
			@Override
			public boolean setViewValue(View view, Object data,
					String textRepresentation) {
				// TODO Auto-generated method stub
				 if(view instanceof ImageView  && data instanceof Drawable){    
		              ImageView iv = (ImageView) view;    
		              iv.setImageDrawable((Drawable) data);		               
		               return true;    
		           }else{
		        	   return false;  
		           }   
			}
		});
		list.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
					long arg3) {
				// TODO Auto-generated method stub
				try {
					PackageInfo pInfo=allPackageInfos.get(arg2);
					Intent intent=new Intent();
					intent.setComponent(new ComponentName(pInfo.packageName, pInfo.activities[0].name));				
					startActivity(intent);
				} catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
				}
				
			}
		});
		
	}
	// ***************--------*建立一個線程加載安裝程式*--------------*******************//
		private class ScanThread extends Thread {
			@Override
			public void run() {
				// 取得系統安裝所有軟體資訊
				allPackageInfos = getPackageManager().getInstalledPackages(
						PackageManager.GET_UNINSTALLED_PACKAGES
								| PackageManager.GET_ACTIVITIES);
				// 定義使用者安裝軟體資訊包
				userPackageInfos = new ArrayList<PackageInfo>();
				// 定義系統安裝軟體資訊包
				sysPackageInfos = new ArrayList<PackageInfo>();				
				// 循環取出所有軟體資訊
				for (int i = 0; i < allPackageInfos.size(); i++) {					
					// 得到每個軟體資訊
					PackageInfo temp = allPackageInfos.get(i);
					ApplicationInfo appInfo = temp.applicationInfo;
					if ((appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0
							|| (appInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
						//系統軟體
						sysPackageInfos.add(temp);						
						
					} else {
						//使用者自己安裝軟體
						userPackageInfos.add(temp);							
					}	
					//擷取程式的圖示
					Drawable ico=ScanPackage1.this.getPackageManager().getApplicationIcon(appInfo);
					//擷取程式的名稱
					String appName=(String) ScanPackage1.this.getPackageManager().getApplicationLabel(appInfo);
					Map<String, Object>item=new HashMap<String, Object>();
					//擷取程式的包名
					String packageName=appInfo.packageName;			
					item.put("imgIco", ico);
					item.put("appName", appName);
					item.put("packageName", packageName);
					Message message = new Message();
	                message.what =SCANNING;
	                message.obj = item;
	                mHandler.sendMessage(message);
										
				}
				saveInfo(sysPackageInfos, userPackageInfos);			
				mHandler.sendEmptyMessage(FLAG_LOAD_SUCCESS);
			}
		};
		
		/**
		 * 将系統中所裝程式的資訊寫入到配置檔案	
		 * @param sysPackageInfos 系統安裝軟體資訊包
		 * @param userPackageInfos 使用者安裝軟體資訊包
		 */
		private void saveInfo(List<PackageInfo> sysPackageInfos,List<PackageInfo> userPackageInfos) {	
		   //将使用者安裝的軟體添加到添加到系統軟體的集合中
			sysPackageInfos.addAll(userPackageInfos);
			SharedPreferences sp = this.getSharedPreferences("appInfs",
					MODE_PRIVATE);
			Editor editor = sp.edit();
			for (int i = 0; i < sysPackageInfos.size(); i++) {
				try {
					//擷取程式的包名
					String packageName = sysPackageInfos.get(i).packageName;
					// 取出activity資訊
					ActivityInfo activityInfo = sysPackageInfos.get(i).activities[0];
					// 取出activity名字
					String activityName=activityInfo.name;					
					//将程式的資訊寫入到配置檔案
					editor.putString(packageName, activityName);					
				} catch (Exception e) {
					// TODO: handle exception
					e.printStackTrace();
				}	
				
			}		
			editor.commit();
		}
}
           

代碼分析:

       上面代碼,中UI 通過代碼new ScanThread().start();啟動一個子線程來掃描所有軟體包,然後子線程将掃描結果通過mHandler.sendMessage(message);時時的發送給UI線程。UI線程收到子線程的消息後擷取消息攜帶的資料,然後更新到ListView上。這樣就實作了異步操作。

 如果你覺得這篇博文對你有幫助的話,請為這篇博文點個贊吧!也可以關注fengyuzhengfan的部落格,收看更多精彩!http://blog.csdn.net/fengyuzhengfan/   

繼續閱讀