《第一行代碼--Android》讀書筆記之多線程與服務

服務(service)是Android中實現程序後臺運行的解決方案。服務並不是運行在一個獨立的進程中,而是依賴於創建服務所在的應用程序的進程。實際上,服務並不會自動開啓線程。

android多線程編程

線程的基本用法

  • 新建一個類繼承自Thread,重寫父類的run()方法,在裏面寫耗時的邏輯。
    class MyThread extends Thread{
    @override
    public void run(){
    //寫耗時的邏輯
    }
    }
    new MyThread().start();
  • 實現Runnable接口,匿名內部類的形式。
        new Thread(new Runnable() {
            @Override
            public void run() {

            }
        }).start();

在子線程中更新UI

想要更新應用程序裏的UI,必須在主線程中進行。android提供了一套異步消息處理機制,完美地解決了在子線程中進行UI操作的問題。

  • 異步消息處理機制
    • 用匿名內部類的形式,實例化一個重寫了handleMessage(Message msg)方法的Handler對象,在handleMessage(Message msg)方法中利用傳過來的Message更新UI。
    • 當子線程有更新的需要時,將數據存在Message對象當中,調用Handler的sendMessage(message)發送到主線程。
private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case UPDATA_TEXT:
                    textView.setText("Hello Android!");
                    break;
                default:
                    break;
            }
        }
    };
new Thread(new Thread() {
                    @Override
                    public void run() {
                        //textView.setText("Hello Android!");
                        Message msg = new Message();
                        msg.what = UPDATA_TEXT;
                        handler.sendMessage(msg);
                        new downtask().execute();
                    }
                }).start();

深入異步消息處理機制
1、組成部分:
何爲handler:異步消息處理者。handler扮演了往MessageQueue上添加消息和處理消息的角色(只處理由自己發出的,由looper分發過來(dispatch)的消息),即通知MQ它要執行一個任務(sendMessage),並在loop到自己的時候執行該任務(handleMessage),整個過程是異步的。Handler的構造方法,會首先得到當前線程中保存的Looper實例,進而與Looper實例中的MessageQueue想關聯。Handler的sendMessage方法,會給msg的target賦值爲handler自身,然後加入MessageQueue中。在構造Handler實例時,我們會重寫handleMessage方法,也就是msg.target.dispatchMessage(msg)最終調用的方法。
何爲message:後臺進程返回的數據,裏面可以存儲bundle等數據格式。
何爲messageQueue:是線程對應looper的一部分,以隊列的形式負責存儲從後臺進程中拋回的和當前handler綁定的message。
何爲looper:looper相當於一個messageQueue的管理人員,同時還是“電報分發員”,它會不停的循環的遍歷隊列,然後將符合條件的message一個個的拿出來分發特定的handler進行處理。首先Looper.prepare()在本線程中保存一個Looper實例,然後該實例中保存一個MessageQueue對象;因爲Looper.prepare()在一個線程中只能調用一次,所以MessageQueue在一個線程中只會存在一個。Looper.loop()會讓當前線程進入一個無限循環,不端從MessageQueue的實例中讀取消息,然後回調msg.target.dispatchMessage(msg)方法。
2、過程:我們通常在UI線程中創建一個handler。這裏要明確Handler、線程、looper的關係,每一個線程都綁定有一個looper,而每個Handler都要關聯一個looper,所以Handler就間接綁定到了一個線程上。一個線程只能有一個looper,但可以有多個Handler。此外,線程中looper負責從其內部的messageQueue中拿出一個個的message,調用dispatchMessage()方法把Message分發給特定的Handler進行處理。handler可以在任意線程中,往它所綁定的looper內部的MQ添加消息,handler在它所綁定的線程中處理消息。因爲我們這裏handler是在UI線程中實現的,所以經過這麼一個handler異步消息處理大師之手,就實現了跨線程間的通信了,那麼UI線程更新UI就不是什麼問題了。
這裏寫圖片描述
另外有幾個值得關注的點:
1、調用handler的post(Runnable r)或者postDelayed(Runnable r,long t)方法裏傳進去一個Runnable的匿名類,其實這樣寫並不能創建一個線程,Runnable內部重寫的run()方法也只是在主線程中執行。因爲handler只負責發送消息和處理消息,並不是負責新建一個線程。而且,所謂的子線程的start()自始至終都沒有被調用。怎樣解決這個問題呢?
上文提到,handler創建時會關聯一個looper,默認的構造方法將關聯當前線程的looper,當然handler也可以關聯到其他線程的looper啦。
可以寫成如下代碼:
//創建一個包含Looper的線程,這裏如果沒有HandlerThread的調用,會直接將後邊的MyThread放到UI線程隊列
HandlerThread myHandlerThread = new HandlerThread("chenzheng_java");
// 啓動新線程
myHandlerThread.start();
// 將handler綁定到新線程,自定義MyHandler類需要定義自己的構造方法
handler = new MyHandler(myHandlerThread.getLooper());
// 在新線程中執行任務,MyRunnableTask是實現了Runnable接口的自定義類
handler.post(new MyRunnableTask());

2、有了handler之後,我們就可以使用 post(Runnable), postAtTime(Runnable, long), postDelayed(Runnable, long), sendEmptyMessage(int), sendMessage(Message), sendMessageAtTime(Message, long)和 sendMessageDelayed(Message, long)這些方法向MQ上發送消息了。光看這些API你可能會覺得handler能發兩種消息,一種是Runnable對象,一種是message對象,這是直觀的理解,但其實post發出的Runnable對象最後都被封裝成message對象了。
最後來張總結性的好圖:
這裏寫圖片描述
要真正理解android的異步消息處理機制,《第一行代碼》並沒有做很詳細的講解,畢竟是入門書,但這個知識點是比較重要的,面試當中也會經常考到。另外推薦兩篇結合android框架源碼分析異步消息處理機制的博文,寫得非常好的。
Android 異步消息處理機制 讓你深入理解 Looper、Handler、Message三者關係:
http://blog.csdn.net/lmj623565791/article/details/38377229
android的消息處理機制(圖+源碼分析)——Looper,Handler,Message:
http://www.cnblogs.com/codingmyworld/archive/2011/09/14/2174255.html

  • 使用AsyncTask

    爲了更加方便我們在子線程中對UI進行操作,android提供了一個工具類AsyncTask,把異步消息處理機制封裝其中。

    • 由於AsyncTask是一個抽象類,我們需要創建一個子類去繼承它。繼承時,我們可以指定三個泛型參數。
      1.Params 在執行AsyncTask時需要傳入的參數,doInBackground(Params,…)。
      2.Progress 在後臺任務執行時,如需要在界面上顯示當前的進度,則使用這裏指定的泛型作爲進度單位
      3.Result 指定返回值類型
    • 重寫onPreExecute(),doInBackground(Params,…),onPrpgressUpdate(Progress,…),onPostExecute(Result,…)方法
    • 啓動自定義任務,new DownloadTask().execute();
class downtask extends AsyncTask<Void,Integer,Boolean>{
        @Override
        protected void onPreExecute() {//後臺任務開始執行前調用,用於進行一些界面上的初始化操作
            super.onPreExecute();
            ProgressDialog.show(MainActivity.this,"Downloading","Loading...");
        }
        @Override
        protected Boolean doInBackground(Void... params) {//在子線程中進行一些耗時的操作
            int downloadProgress=0;
            publishProgress(downloadProgress);//傳入更新ui的數據
            return true;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {//更新ui操作
            super.onProgressUpdate(values);

        }

        @Override
        protected void onPostExecute(Boolean aBoolean) {//任務結束
            super.onPostExecute(aBoolean);
        }
    }

參考博文:詳解Android中AsyncTask的使用

服務的基本用法

  • 當然要自定義一個子類繼承Service類,重寫
    public IBinder onBind(Intent intent),與活動通信的關鍵方法,與活動綁定的時候調用。
    public void onCreate(),當服務被創建的時候調用
    public int onStartCommand(Intent intent, int flags, int startId),當服務被啓動的時候被調用
    public void onDestroy(),當服務被銷燬的時候被調用。
  • 作爲android四大組件之一,要啓動和停止服務當然要藉助intent了,
                Intent startIntent=new Intent(this,MyService.class);
                startService(startIntent);
                Intent stopIntent=new Intent(this,MyService.class);
                stopService(stopIntent);
  • 最後一定要在AndroidManifest文件中進行註冊。

服務與活動通信

  • 在自定義的Service子類中,定義一個內部類,繼承自Binder。在子類內部,定義給關聯組件訪問的接口方法。我把它比喻成電話線。
  • 實例化這個內部類,在onBind()方法中返回Binder子類實例。
    public IBinder onBind(Intent intent) {
    return downloadBinder;
    }
  • 在活動中,建造一條橋樑,創建一個ServiceConnection的匿名類,重寫onServiceDisconnected()和onServiceConnected(),並在onServiceConnected()中得到Binder實例,即當橋樑架好後在onServiceConnected()可獲得來自Service的電話線實例。
    private MyService.DownloadBinder downloadBinder;
    private ServiceConnection serviceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder=(MyService.DownloadBinder)service;
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
  • 橋樑建好了,接下來就可調用bindService()和unbindService()綁定和解綁服務了,把橋樑架在Activity與Service之間。
                Intent bindIntent=new Intent(this,MyService.class);
                bindService(bindIntent,serviceConnection,BIND_AUTO_CREATE);
     //BIND_AUTO_CREATE表示綁定服務後自動啓動服務
  • 接下來在活動中就可以調用返回的在Service內部的Binder子類實例的方法了。任何一個服務在整個應用程序都是通用的,它可以與其他活動綁定。不管服務是如何被創建的,它都允許客戶綁定它。所以一個通過onStartCommand()方法啓動的服務(started service)依然會收到onBind()調用(當客戶調用了bindService())。

服務的生命週期

這裏寫圖片描述
活躍的生命時間,開始於onStartCommand()或onBind()函數被調用。如果是被啓動的服務,他的活躍生命時間會和整個生命時間一同結束(在onStartCommand()函數返回後,服務依然是活躍狀態)。如果是被綁定的服務,活躍生命時間會在onUnbind()函數返回後結束。當調用了startService()又去調用bindService()方法,要調用unbindService()和stopService()方法,onDestroy()方法纔會執行。

服務的更多技巧

前臺服務

如果希望服務一直保持運行狀態,而不會由於系統的內存不足而被回收,可以考慮使用前臺服務。
方法很簡單,在Service的onCreate()方法中,使用通知(Notification),所不同的是調用startForground()啓動通知。

    @Override
    public void onCreate() {
        super.onCreate();
        Intent intent=new Intent(this,MainActivity.class);
        PendingIntent pendingIntent=PendingIntent.getActivity(this,0,intent,0);
        Notification notification=new Notification.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("Front Service")
                .setContentText("this is content")
                .setWhen(System.currentTimeMillis())
                .setContentIntent(pendingIntent)
                .getNotification();
        startForeground(1,notification);
        Log.d("MyService","onCreate");
    }

使用IntentService

不要被後臺服務的後臺所迷惑,其實服務中的代碼都是默認運行渣主線程當中的,如果直接在服務中去處理一些耗時的邏輯就很容易出現ANR(Application Not Response)。
我們應該在服務的每個具體方法中開啓一個子線程,在子線程中處理那些耗時的邏輯。
服務一旦啓動之後,就會一直處於運行狀態,必須調用StopService()或stopSelf()方法才能停止服務。爲了方便創建一個異步的,會自動停止的服務,Android提供了一個IntentService類(方便的服務),它有點像Activity,能夠響應intent,從onHandleIntent(Intent intent)方法可以看出。

public class MyIntentService extends IntentService {
    /**
     * Creates an IntentService.  Invoked by your subclass's constructor.
     *
     * @param name Used to name the worker thread, important only for debugging.
     */
    public MyIntentService(String name) {
        super(name);
    }
    public MyIntentService() {
        super("MyIntentService");
    }
    @Override
    protected void onHandleIntent(Intent intent) {
        //子線程代碼
    }
}

AlarmManager

AlarmManager是可以發送Intent的系統服務,既然是系統服務,當然調用(AlarmManager)getSystemService(ALARM_SERVICE);方法得到Manager實例啦。發送什麼樣的intent呢,我們使用PendingIntent打包Intent。
AlarmManager的set()方法可以設置一個定時任務。(涉及PendingIntent和BroadcastReceiver)

        Intent intent1=new Intent(this,AlarmReceiver.class);
        PendingIntent pendingIntent=PendingIntent.getBroadcast(this,0,intent1,0);
        AlarmManager alarmManager=(AlarmManager)getSystemService(ALARM_SERVICE);
        long triggerAtTime= SystemClock.elapsedRealtime()+30*1000;
       alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,triggerAtTime,pendingIntent);

參考博客和文獻:
Android管理服務(Service)的生命週期(lifecycle)
http://blog.csdn.net/oracleot/article/details/18818575

發佈了32 篇原創文章 · 獲贊 45 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章