編輯:關於Android編程
總結下來就是:
- IntentService是Service類的子類,用來處理異步請求;
- 客戶端可以通過startService(Intent)方法傳遞請求給IntentService;
- IntentService單獨開啟了一個線程來處理所有的Intent請求所對應的任務,以免事務處理阻塞主線程,而且任務是按先後順序逐個進行處理的;
- 當IntentService處理完所有的任務後,它會在適當的時候自動結束服務。
下面是一個模擬圖片上傳的demo。
ImgUploadService.java類:
public class ImgUploadService extends IntentService { public static final String TAG = "ImgUploadService"; private static final String ACTION_UPLOAD_IMG = "com.demo.service.action.UPLOAD_IMAGE"; public static final String EXTRA_IMG_PATH = "com.demo.service.extra.IMG_PATH"; public ImgUploadService(String name) { super(name); Log.d(TAG, "ImgUploadService[" + " ThreadName: " + name + " ]"); } @Override protected void onHandleIntent(Intent intent) { Log.d(TAG, "onHandleIntent"); if (intent != null) { final String action = intent.getAction(); if (ACTION_UPLOAD_IMG.equals(action)) { final String path = intent.getStringExtra(EXTRA_IMG_PATH); handleUploadImg(path); } } } private void handleUploadImg(String path) { try { Thread.sleep(3000); //模擬上傳耗時 Intent intent = new Intent(IntentServiceActivity.UPLOAD_RESULT); intent.putExtra(EXTRA_IMG_PATH, path); LocalBroadcastManager.getInstance(getApplicationContext()).sendBroadcast(intent); } catch (InterruptedException e) { e.printStackTrace(); } } public static void startUploadImg(Context context, String path) { Intent intent = new Intent(context, ImgUploadService.class); intent.setAction(ACTION_UPLOAD_IMG); intent.putExtra(EXTRA_IMG_PATH, path); context.startService(intent); } @Override public void onDestroy() { super.onDestroy(); Log.d(TAG, "onDestroy"); } }
IntentServiceActivity.java類:
public class IntentServiceActivity extends Activity { public static final String UPLOAD_RESULT = "com.demo.service.UPLOAD_RESULT"; private LinearLayout mLlContainer; private TextView mBtnUpload; int i = 0; @Override public void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) { super.onCreate(savedInstanceState, persistentState); setContentView(R.layout.activity_main_handlerthread); mLlContainer = (LinearLayout) findViewById(R.id.ll_container); mBtnUpload = (TextView) findViewById(R.id.btn_upload); registerReceiver(); mBtnUpload.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { addTask(); //模擬上傳 } }); } private void registerReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(UPLOAD_RESULT); LocalBroadcastManager.getInstance(this).registerReceiver(mUploadImgReceiver, filter); } public void addTask() { String path = "圖片" + i++ + ".png"; ImgUploadService.startUploadImg(this, path); TextView tv = new TextView(this); mLlContainer.addView(tv); tv.setText(path + " ....正在上傳中...."); tv.setTag(path); } private void handleResult(String path) { TextView tv = (TextView) mLlContainer.findViewWithTag(path); tv.setText(path + " ----上傳成功---- "); } private BroadcastReceiver mUploadImgReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(UPLOAD_RESULT)) { String path = intent.getStringExtra(ImgUploadService.EXTRA_IMG_PATH); handleResult(path); } } }; @Override protected void onDestroy() { super.onDestroy(); LocalBroadcastManager.getInstance(this).unregisterReceiver(mUploadImgReceiver); } }
activity_main.xml文件:
IntentService本身就是一個Service,它擁有Service的所有生命周期方法,但內部是通過HandlerThread實現異步執行任務的,HandlerThread是一個內部維護了一個消息隊列的線程。
既然IntentService有生命周期,那麼就從它的構造函數看起:
/** * Creates an IntentService. Invoked by your subclass's constructor. * * @param name Used to name the worker thread, important only for debugging. */ public IntentService(String name) { super(); mName = name; }
構造方法很簡單,用於創建一個IntentService對象,參數namne用於定義工作線程的名字,僅用於調試作用。接下來看下IntentService的onCreate()方法:
@Override public void onCreate() { // TODO: It would be nice to have an option to hold a partial wakelock // during processing, and to have a static startService(Context, Intent) // method that would launch the service & hand off a wakelock. super.onCreate(); // 創建一個HandlerThread對象,並傳入工作線程的名字 HandlerThread thread = new HandlerThread("IntentService[" + mName + "]"); // 開啟後台工作線程 thread.start(); // 獲取後台工作線程的Looper對象 mServiceLooper = thread.getLooper(); // 創建一個ServiceHandler對象,用來處理異步消息。 mServiceHandler = new ServiceHandler(mServiceLooper); }
在onCreate()方法裡,首先利用HandlerThread類創建了一個循環的工作線程,接著獲取工作線程中的Looper對象並將其作為參數創建了一個叫ServiceHandler的類,該類是IntentService的內部類,該類繼承了Handler,我們看它的定義:
public abstract class IntentService extends Service { //volatile關鍵字保證變量每次在使用的時候,都從主存中取。而不是從各個線程的“工作內存”中讀取 private volatile Looper mServiceLooper;// private volatile ServiceHandler mServiceHandler; private String mName; private boolean mRedelivery; private final class ServiceHandler extends Handler { public ServiceHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { //一收到消息就回調到onHandleIntent()中進行處理 onHandleIntent((Intent)msg.obj); //處理完消息就調用stopSelf()方法,並傳入消息的索引值 stopSelf(msg.arg1); } } }
從源碼中可以看到,mServiceLooper和mServiceHandler都加了volatile關鍵字修飾,這是為了保證變量在每次使用的時候都從主內存中讀取,而不是從各個線程的“工作內存”中讀取,也就是說保證內存的可見性。
接著看onHandleIntent()方法,該方法是在ServiceHandler收到消息後回調的,也即onHandleIntent()方法是在HandlerThread工作線程中執行的,它是一個抽象方法,留給調用者去實現耗時任務的。
/** * This method is invoked on the worker thread with a request to process. * Only one Intent is processed at a time, but the processing happens on a * worker thread that runs independently from other application logic. * So, if this code takes a long time, it will hold up other requests to * the same IntentService, but it will not hold up anything else. * When all requests have been handled, the IntentService stops itself, * so you should not call {@link #stopSelf}. * * @param intent The value passed to {@link * android.content.Context#startService(Intent)}. */ protected abstract void onHandleIntent(Intent intent);
此外,因為任務是通過ServiceHandler發送給異步線程的消息隊列來處理的,而消息隊列裡的消息是依次取出並執行的,所以從這裡可以了解到任務必定是串行執行的。而執行完一個消息後,調用了一個帶有startId參數的stopSelf()方法:
** * Old version of {@link #stopSelfResult} that doesn't return a result. * * @see #stopSelfResult */ public final void stopSelf(int startId) { if (mActivityManager == null) { return; } try { mActivityManager.stopServiceToken( new ComponentName(this, mClassName), mToken, startId); } catch (RemoteException ex) { } }
該方法實際上是通過調用ActivityManager的stopServiceToken()方法來停止當前服務的,不過服務不會馬上停止,而是等待繼續完成剩下的任務後才自動結束,因為方法參數裡攜帶了一個startId,它可以看做是一個請求的唯一標識,只有當所有的請求都結束,我們的Service才自行銷毀,而startId是從onStartCommand()裡被調用的,我們看下它的源碼:
/** * You should not override this method for your IntentService. Instead, * override {@link #onHandleIntent}, which the system calls when the IntentService * receives a start request. * @see android.app.Service#onStartCommand */ @Override public int onStartCommand(Intent intent, int flags, int startId) { onStart(intent, startId); return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY; } @Override public void onStart(Intent intent, int startId) { Message msg = mServiceHandler.obtainMessage(); msg.arg1 = startId; msg.obj = intent; mServiceHandler.sendMessage(msg); }
對於Service,每次調用startServcie方法啟動一個服務,都會調用一次onStartCommand()方法,每次也會帶一個startId過去,如果執行多個任務,那麼就會存在多個startId,然後在onStart()方法中通過mServiceHandler獲得一個消息對象msg,然後將startId作為該消息的消息碼,將異步任務請求intent作為消息內容封裝成一個消息msg發送到mServiceHandler中進行異步處理,最後回調到onHandleIntent()中,子類通過實現onHandleIntent()即可進行異步耗時任務的執行。
這裡注意,我們不需要自己去重寫onStartCommand()和onStart()方法,但onHandleIntent()必須重寫。
當所有的startId都處理完後,IntentService會調用onDestroy()自行銷毀,消息隊列也會自行退出。
意圖是指兩個UI主界面的轉換,要想了解意圖就必須學習ACtivity的生命周期 默認在UI界面顯示的為運行為運行狀態,而在後台的為onPause方法 主線:
““XXX(機主姓名)看這個,ht://********XXshenqi.apk”最近一種手機病毒爆發,機主收到這樣的短信,開頭是以發
上一篇文章中我們講解了android UI優化方面的知識。我們講解了android中的include、marge、ViewStub標簽,在使用這些標簽時可以簡化我們的布局
1.寫在前面本篇的主要內容是關於在Dialog中軟鍵盤的顯示與隱藏問題,需求是在Dialog中有一個密碼輸入框,彈出Dialog顯示軟鍵盤,關閉Dialog隱藏軟鍵盤。