安卓中的服務

1.服務是什麼

服務是Android中執行程序後臺運行的解決方案,不依賴與任何用戶界面,但是依賴於進程,所以應用進程被殺掉,服務也會停止運行.
因爲所有代碼都是默認運行在主線程中,所以我們需要在線程的內部手動創建,否則可能會出現主線程被堵塞的情況.

2.多線程的知識

以下寫法runOnUiThread(new Runnable() {}中的代碼爲切換到主線程的更新UI操作

寫法1:

class MyThread implements Runnable{
        @Override
        public void run() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(NormalActivity.this, "123", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
MyThread myThread = new MyThread();
        new Thread(myThread).start();

寫法2:

class MyThread extends Thread{
        @Override
        public void run() {
            runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(NormalActivity.this, "123", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }
MyThread myThread = new MyThread();
        myThread.start();

寫法3

new Thread(new Runnable() {
            @Override
            public void run() {
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        Toast.makeText(NormalActivity.this, "123", Toast.LENGTH_SHORT).show();
                    }
                });
            }
        }).start();
以上代碼中runOnUiThread是用來寫UI的,因爲UI必須在主線程中修改,否則會崩潰,除此之外還有兩種異步消息處理機制

1.handler

Android中的異步消息處理主要是4個部分組成,Message,Handler,MessageQueue,Looper
1.Message是線程之間傳遞的消息,
Message.what 傳遞消息
Message.arg1,Message.arg2,傳遞整形數據
Message.obj 傳遞Object對象

2.Handler
發送消息一般都是handler.sendMessage();
處理消息handleMessage()方法中

3.MessageQueue
存放所有通過HandleMessage發送的消息,每個線程中只會有一個MessageQueue對象

4.Looper
每個線程中MessageQueue的管家,Looper的loop()方法發現MessageQueue有消息就會將它取出,並放入Handle的handleMessage()方法,每個線程只有一個Looper對象

異步消息處理流程

首先在主線程中創建一個Handler對象,並重寫handleMessage()方法,然後當子線程需要進行UI操作時,就創建一個Message對象,通過handle.sendMessage()發送出去,這條消息被送到MessageQueue中被等待處理,然後Looper從中取出,發送到handleMessage的方法中到主線程處理

			 public static final int UPDATE_TEXT = 1;
@SuppressLint("HandlerLeak")
    public Handler handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case UPDATE_TEXT:
                    text.setText("123");
                    break;
            }
        }
    };
text.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Message message = new Message();
                        message.what = UPDATE_TEXT;
                        handler.sendMessage(message);
                    }
                }).start();
            }
        });

2.AsyncTask

AsyncTask是一個抽象類,在繼承時我們爲其指定三個泛型參數,
Params 在執行AsyncTask時需要傳入的參數,可用於在後臺任務中使用
Progress後臺執行時,在界面上顯示當前的進度
Result任務執行完畢後,需要對結果進行返回
需要重寫的方法:
onPreExecute() 在後臺任務開始執行之前調用,進行一些界面上的初始化操作,比如顯示一個進度條對話框
doInbackGround(Param…)在子線程中進行,所以不能進行UI操作,反饋進度使用publishProgress(Progress…) 方法完成
onProgressUpdate(Progress…),從後臺傳遞過來就在這個方法中執行UI操作
onPostExecute(Result)當後臺執行完畢

舉例一個模板(不能運行,只是看看)

public class DownloadTask extends AsyncTask<Void,Integer,Boolean>{
    ProgressDialog progressDialog;
    Context context;
    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        try {
            while (true){
                int downloadPercent = Download();
                publishProgress(downloadPercent);
                if (downloadPercent >= 100){
                    break;
                }
            }
        }catch (Exception e){
            return false;
        }
        return true;
    }

    private int Download() {
        int a = 10;
        return a;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        super.onProgressUpdate(values);
        progressDialog.setMessage("Downloaded"+values[0]+"%");
    }

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        super.onPostExecute(aBoolean);
        progressDialog.dismiss();
        if (aBoolean){
            Toast.makeText(context, "succeed", Toast.LENGTH_SHORT).show();
        }else {
            Toast.makeText(context, "failed", Toast.LENGTH_SHORT).show();
        }
    }
}

即運行的過程爲:
在doInBacground()方法中執行耗時任務,通過publishProgress()傳出進度,傳入到onProgressUpdate()中進行UI的更新,在onPreExecute()中進行任務的收尾

最後別忘了啓動任務:
new DownloadTask().execute();

服務的基本用法

onCreate()在服務第一次創建的時候調用
onStartCommand()在每次啓動服務的時候都會調用
onBind()在活動中指揮服務幹什麼

舉例說明:


import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;

public class MyService extends Service {
    private static final String TAG = "Myservice";
    private DownloadBinder binder = new DownloadBinder();

    public MyService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate: " );
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand: " );
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy: ");
    }

    class DownloadBinder extends Binder{

        public void startDownload(){
            Log.e(TAG, "startDownload: " );
        }

        public  int progress(){
            Log.e(TAG, "progress: " );
            return 0;
        }
    }
}

public class SecondActivity extends AppCompatActivity {
    Button back;
    Button start;
    Button stop;
    Button bind;
    Button unbind;
    private MyService.DownloadBinder downloadBinder;
    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            downloadBinder = (MyService.DownloadBinder) service;
            downloadBinder.startDownload();
            downloadBinder.progress();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_second);
        back = findViewById(R.id.back);
        start = findViewById(R.id.start_service);
        stop = findViewById(R.id.stop_service);
        bind = findViewById(R.id.bind_service);
        unbind = findViewById(R.id.unbind_service);
        start.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SecondActivity.this,MyService.class);
                startService(intent);
            }
        });
        stop.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SecondActivity.this,MyService.class);
                stopService(intent);
            }
        });
        bind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(SecondActivity.this,MyService.class);
                bindService(intent,connection,BIND_AUTO_CREATE);
            }
        });
        unbind.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                unbindService(connection);
            }
        });
    }
}

依次點擊start,bind,unbind,stop的運行結果:
在這裏插入圖片描述

可以從中看出服務的生命週期:

調用了startService()方法時,會回調到startCommand()方法,但如果是第一次創建服務時,會在startCommand之前先調用onCreate()方法,雖然onStartCommand可以調用多次,但是服務只會有一個實例,
bindService是來獲取一個服務的持久連接,會回調服務的onBind方法,通過這個方法的返回值可以和活動進行通信
只需調用一次stopService(),服務就會停止下來但是如果同時使用了startService()方法和bindService()方法,必須同時使用stopService()和unbindService()纔會執行onDestroy()方法

使用前臺服務

startForeground()方法接受一個id,一個notification對象,是一個服務變爲一個前臺服務,並在系統狀態欄中顯示出來

Intent intent = new Intent(this, SecondActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
        Notification notification = new NotificationCompat.Builder(this)
                .setContentTitle("title")
                .setContentText("text,text")
                .setWhen(System.currentTimeMillis())
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentIntent(pendingIntent)
                .build();
        startForeground(1,notification);

使用IntentService

我們知道,服務中的代碼默認運行在主線程中,這就很容易出現ANR(Application Not Responding)
因此一個標準寫法爲

@Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand: ");
        new Thread(new Runnable() {
            @Override
            public void run() { 
                //處理邏輯
                stopSelf();
            }
        }).start();
        return super.onStartCommand(intent, flags, startId);
    }

但是有時候會忘記創建線程,所以專門提供了一個IntentService類,可以解決忘記

public class MyService2 extends IntentService {
    private static final String TAG = "MyService2";

    public MyService2() {
        super("MyService2");
    }

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Log.e(TAG, "onHandleIntent: "+Thread.currentThread().getId() );
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.e(TAG, "onDestroy: " );
    }
}
startintent.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "onClick: "+Thread.currentThread().getId() );
                Intent intent = new Intent(SecondActivity.this,MyService2.class);
                startService(intent);
            }
        });

在這裏插入圖片描述
從運行結果可以看出:
Myservice2中不是運行在主線程中了,且在服務結束後會自動停止

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章