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中不是運行在主線程中了,且在服務結束後會自動停止