標籤:thread+handler android非同步作業 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(){@Overridepublic void handleMessage(Message msg) {// TODO Auto-generated method stubswitch (msg.what) {case FLAG_LOAD_SUCCESS://完成掃描break;case SCANNING://正在掃描items.add((Map<String, Object>) msg.obj);//通知適配器資料改變adapter.notifyDataSetChanged();break;default:break;}}};@Overrideprotected 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() {@Overridepublic 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() {@Overridepublic void onItemClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {// TODO Auto-generated method stubtry {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 exceptione.printStackTrace();}}});}// ***************--------*建立一個線程載入安裝程式*--------------*******************//private class ScanThread extends Thread {@Overridepublic 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 exceptione.printStackTrace();}}editor.commit();}}
程式碼分析:
上面代碼,中UI 通過代碼new ScanThread().start();啟動一個子線程來掃描所有軟體包,然後子線程將掃描結果通過mHandler.sendMessage(message);時時的發送給UI線程。UI線程收到子線程的訊息後擷取訊息攜帶的資料,然後更新到ListView上。這樣就實現了非同步作業。
如果你覺得這篇博文對你有協助的話,請為這篇博文點個贊吧!也可以關注fengyuzhengfan的部落格,收看更多精彩!http://blog.csdn.net/fengyuzhengfan/
Android開發之非同步詳解(一)Thread+Handler