Android學習筆記(8)
服務
標籤: Android
1 服務基本概念
服務可以實現程式後臺執行,適合長期執行且不與使用者互動的任務。 服務不是執行在一個獨立的程式中,而是依賴於建立服務的應用程式程式。當它被kill掉後,依賴該程式的服務也會停止執行。 服務的程式碼執行在主執行緒中,但是執行具體任務的時候需要在服務內部建立新執行緒,否則會阻塞主執行緒
1.1 多執行緒
繼承,實現Runnable介面以及匿名類,3種方式
更新UI 子執行緒中不能直接更新UI元素,但可以利用非同步訊息處理機制,解決在子執行緒中進行UI操作
非同步訊息處理機制:
子執行緒中傳送訊息,主執行緒接收訊息並處理。
Message
Message是執行緒間傳遞的訊息,攜帶資訊,如Message的what欄位,arg1\arg2攜帶整形資料,obj攜帶Object物件
Handler
處理者,用於傳送訊息和處理訊息。 傳送訊息:
Handler.sendMessage()
處理訊息:
handleMessage
MessageQueue
訊息佇列,存放通過Handler傳送的訊息,每個執行緒只有一個MessageQueue物件
Looper
管理MessageQueue的物件,每個執行緒只有一個Looper物件,呼叫Looper的loop方法後,就能從MessageQueue取出訊息當存在一條訊息時。
public class MainActivity extends AppCompatActivity implements View.ONClickListener { public static final int Update_Text = 1; private TextView text; private Handler handler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case Update_Text: text.setText("Nice to meet you"); break; default: break; } } }; ... @Override public void onClick(View v) { switch (v.getId(){ case R.id.text: new Thread(new Runnable(){ @Override public void run() { Message message = new Message(); message.what = Update_Text; handler.sendMessage(message); } }).start(); break; default: break; } } }
主執行緒中建立一個Handler物件,重寫handleMessage方法 子執行緒建立一個Message物件,通過Handler傳送訊息 訊息進入到訊息佇列中並被取出分發回Handler的handleMessage方法中
runOnUIThread是非同步訊息處理機制的介面封裝
使用AsyncTask
AsyncTask是抽象類,需要繼承它,指定3個泛型引數,分別為
- Params:傳入引數,用於後臺任務
- Progress:任務進度
Result:返回結果的返回值型別
class DownloadTask extends AsyncTask<void, Integer, Boolean> { ... }
重寫方法
onPreExecute()
後臺任務開始執行前呼叫,初始化介面,如顯示一個進度條對話方塊
doInBackground(Params...)
在子執行緒中執行,此方法中不能進行UI操作,如果需要更新UI元素,如反饋當前任務的執行進度,可以呼叫publicProgress(Progress...)方法
onProgressUpdate(Pogress...)
當呼叫pullishProgress(Progress...)方法後,onProgressUpdate(Progress...)被呼叫。可以對UI進行操作,利用引數中攜帶的數值對介面元素進行相應的更新
onPostExecute(Result)
後臺任務執行完畢後通過return語句返回,會呼叫此方法。可以對UI進行操作,如提醒任務執行結果,以及關閉進度條對話方塊等
class DownloadTask extends AsyncTask<void, Integer, Boolean> {
}@Override protected void onPreExecute(){ progressDialog.show(); } @Override protected Boolean doInBackground(Void.. params) { try{ while(true) { int downloadPercent = doDownload(); publicProgress(downloadPercent); if (downloadPercent>=100) break; } }catch(Exception e){ return false; } return true; } @Override protected void onPostExecute(Boolean result) { progressDialog.dismiss(); if (result){ Toast.makeText(context, "Succeed", Toast.LENGTH_SHORT).show(); else Toast.makeText(context, "Failed", Toast.LENGTH_SHORT).show(); } }
啟動任務
new DownloadTask().execute();
1.2 服務基本用法
類似其它元件,利用AS快速新建一個服務並重寫方法
public class MyService extends Service {
public MyService() {
}
@Override
public IBinder onBind(Intent intent) {
//
}
@Override
public void onCreate() {//服務建立的時候呼叫
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {//服務啟動的時候呼叫
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {//服務銷燬時呼叫
super.onDestroy();
}
}
啟動和停止服務
使用Intent實現
Intent startIntent = new Intent(this, MyService.class);
startService(startintent);//啟動服務
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);//停止服務
當需要讓服務自己停止,需要在MyService的任一位置呼叫stopSelf方法
活動和服務進行通訊
比如在活動中可以決定何時開始下載,以及隨時檢視下載進度。通過建立一個Binder物件對下載功能進行管理
public class MyService extends Service {
private DonwloadBinder mBinder = ne DownloadBinder();
class DownloadBinder extends Binder {
public void startDownload() {
//
}
public int getProgress() {
//
return 0;
}
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
...
}
在活動中呼叫服務中的方法
public class MainActivity extends AppCompatActivity implements View.onClickListener {
private MyService.DownlloadBinder downloadBinder;
private ServiceConnection connectio = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
downloadBinder = (MyService.DownloadBinder) service;
downloadBinder.startDownload();
downloadBinder.getProgress();
}
};
...
}
活動與服務繫結以及解除繫結
Intent bindIntent = new Intent(this, MyService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
...
unbindService(connection);
當活動和服務成功繫結後,呼叫onServiceConnected方法,解除繫結呼叫onServiceDisconnected方法。
1.3 服務的高階技巧
前臺服務
前臺服務與普通服務不同的是它會在系統的狀態列顯示,下拉會顯示,類似通知。
在服務中的onCreate中指定建立一個前臺服務
public class MyService extends Service {
...
@Override
public void onCreate() {
...
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("title")
.setContentText("conent text")
.setWhen(System.currentTimeMills())
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
.setConetentIntnet(pi)
.builde();
startForeground(1, notification);
}
}
IntentService 為了避免ANR,需要在服務的每個具體方法中開啟執行緒,當處理完畢時,呼叫stopSelf或者stopService停止服務。IntentService可以解決忘記開啟執行緒以及呼叫stopSelf方法,建立一個服務
public class MyIntentService extends IntnetService {
public MyIntentService() {
super("MIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
//
}
@Override
public void onDestroy(){
super.onDestroy();
}
在活動內啟動
Intent intent = new Intent(this, MyIntentService.class);
startService(intentService);
1.4 下載功能實現
OkHttp實現網路請求
compile 'com.squareup.okhttp3:okhttp:3.7.0'
定義會調介面,監聽下載過程的狀態和回撥
public interface DownloadListener {
void onProgress(int progress);
void onSuccess();
void onFaild();
void onPaused();
void onCanceled();
}
使用AsyncTask實現下載功能
public class DownloadTask extends AsyncTask<String, Integer, Integer> {
public static final int TYPE_SUCCESS = 0;
public static final int TYPE_FAILED = 1;
public static final int TYPE_PAUSED = 2;
public static final int TYPE_CANCELED = 3;
private DownloadListener listener;
private boolean isCanceled = false;
private boolean isPaused = false;
private int lastProgress;
public DownloadTask(DownloadListener listener) {
this.listener = listener;
}
@Override
protected Integer doInBackground(String... params) {
InputStream is = null;
RandomAccessFile savedFile = null;
File file = null;
try {
long downloadedLength = 0; // 記錄已下載的檔案長度
String downloadUrl = params[0];
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
file = new File(directory + fileName);
if (file.exists()) {
downloadedLength = file.length();
}
long contentLength = getContentLength(downloadUrl);
if (contentLength == 0) {
return TYPE_FAILED;
} else if (contentLength == downloadedLength) {
// 已下載位元組和檔案總位元組相等,說明已經下載完成了
return TYPE_SUCCESS;
}
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
// 斷點下載,指定從哪個位元組開始下載
.addHeader("RANGE", "bytes=" + downloadedLength + "-")
.url(downloadUrl)
.build();
Response response = client.newCall(request).execute();
if (response != null) {
is = response.body().byteStream();
savedFile = new RandomAccessFile(file, "rw");
savedFile.seek(downloadedLength); // 跳過已下載的位元組
byte[] b = new byte[1024];
int total = 0;
int len;
while ((len = is.read(b)) != -1) {
if (isCanceled) {
return TYPE_CANCELED;
} else if(isPaused) {
return TYPE_PAUSED;
} else {
total += len;
savedFile.write(b, 0, len);
// 計算已下載的百分比
int progress = (int) ((total + downloadedLength) * 100 / contentLength);
publishProgress(progress);
}
}
response.body().close();
return TYPE_SUCCESS;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (is != null) {
is.close();
}
if (savedFile != null) {
savedFile.close();
}
if (isCanceled && file != null) {
file.delete();
}
} catch (Exception e) {
e.printStackTrace();
}
}
return TYPE_FAILED;
}
@Override
protected void onProgressUpdate(Integer... values) {
int progress = values[0];
if (progress > lastProgress) {
listener.onProgress(progress);
lastProgress = progress;
}
}
@Override
protected void onPostExecute(Integer status) {
switch (status) {
case TYPE_SUCCESS:
listener.onSuccess();
break;
case TYPE_FAILED:
listener.onFailed();
break;
case TYPE_PAUSED:
listener.onPaused();
break;
case TYPE_CANCELED:
listener.onCanceled();
default:
break;
}
}
public void pauseDownload() {
isPaused = true;
}
public void cancelDownload() {
isCanceled = true;
}
private long getContentLength(String downloadUrl) throws IOException {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(downloadUrl)
.build();
Response response = client.newCall(request).execute();
if (response != null && response.isSuccessful()) {
long contentLength = response.body().contentLength();
response.close();
return contentLength;
}
return 0;
}
}
新建一個服務用於下載
public class DownloadService extends Service {
private DownloadTask downloadTask;
private String downloadUrl;
private DownloadListener listener = new DownloadListener() {
@Override
public void onProgress(int progress) {
getNotificationManager().notify(1, getNotification("Downloading...", progress));
}
@Override
public void onSuccess() {
downloadTask = null;
// 下載成功時將前臺服務通知關閉,並建立一個下載成功的通知
stopForeground(true);
getNotificationManager().notify(1, getNotification("Download Success", -1));
Toast.makeText(DownloadService.this, "Download Success", Toast.LENGTH_SHORT).show();
}
@Override
public void onFailed() {
downloadTask = null;
// 下載失敗時將前臺服務通知關閉,並建立一個下載失敗的通知
stopForeground(true);
getNotificationManager().notify(1, getNotification("Download Failed", -1));
Toast.makeText(DownloadService.this, "Download Failed", Toast.LENGTH_SHORT).show();
}
@Override
public void onPaused() {
downloadTask = null;
Toast.makeText(DownloadService.this, "Paused", Toast.LENGTH_SHORT).show();
}
@Override
public void onCanceled() {
downloadTask = null;
stopForeground(true);
Toast.makeText(DownloadService.this, "Canceled", Toast.LENGTH_SHORT).show();
}
};
private DownloadBinder mBinder = new DownloadBinder();
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
class DownloadBinder extends Binder {
public void startDownload(String url) {
if (downloadTask == null) {
downloadUrl = url;
downloadTask = new DownloadTask(listener);
downloadTask.execute(downloadUrl);
startForeground(1, getNotification("Downloading...", 0));
Toast.makeText(DownloadService.this, "Downloading...", Toast.LENGTH_SHORT).show();
}
}
public void pauseDownload() {
if (downloadTask != null) {
downloadTask.pauseDownload();
}
}
public void cancelDownload() {
if (downloadTask != null) {
downloadTask.cancelDownload();
} else {
if (downloadUrl != null) {
// 取消下載時需將檔案刪除,並將通知關閉
String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));
String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();
File file = new File(directory + fileName);
if (file.exists()) {
file.delete();
}
getNotificationManager().cancel(1);
stopForeground(true);
Toast.makeText(DownloadService.this, "Canceled", Toast.LENGTH_SHORT).show();
}
}
}
}
private NotificationManager getNotificationManager() {
return (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
}
private Notification getNotification(String title, int progress) {
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0);
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher));
builder.setContentIntent(pi);
builder.setContentTitle(title);
if (progress >= 0) {
// 當progress大於或等於0時才需顯示下載進度
builder.setContentText(progress + "%");
builder.setProgress(100, progress, false);
}
return builder.build();
}
}
DownloadListener匿名類例項中實現了介面方法
DownloadBinder用於讓服務和活動通訊
其它細節東西參考<<第一行程式碼>>P375
相關文章
- CCNA學習筆記8筆記
- Android 學習筆記雜記Android筆記
- Android學習筆記·ANRAndroid筆記
- Android學習筆記·HandlerAndroid筆記
- Android SQLite學習筆記AndroidSQLite筆記
- Android學習筆記一Android筆記
- Android學習筆記(6)Android筆記
- Android學習筆記(3)Android筆記
- Android學習筆記(4)Android筆記
- Android學習筆記(5)Android筆記
- Android學習筆記(2)Android筆記
- Android學習筆記(1)Android筆記
- Android學習筆記(7)Android筆記
- Android GC 學習筆記AndroidGC筆記
- android學習筆記--ScannerAndroid筆記
- android學習筆記--AlarmManagerAndroid筆記
- android學習筆記二Android筆記
- android學習筆記三Android筆記
- Android學習筆記四Android筆記
- android學習筆記五Android筆記
- android學習筆記六Android筆記
- Android OpenGL 學習筆記Android筆記
- G01學習筆記-8筆記
- Tensorflow學習筆記No.8筆記
- asio學習筆記8——stackfull coroutine筆記
- k8s學習筆記K8S筆記
- Android Linker學習筆記Android筆記
- Android學習筆記·ADBAndroid筆記
- Android 學習筆記思考篇Android筆記
- Android 學習筆記核心篇Android筆記
- Android學習筆記(五)——FragmentAndroid筆記Fragment
- Android學習筆記之IntentAndroid筆記Intent
- Android 開發學習筆記Android筆記
- Android Studio學習筆記Android筆記
- JDK8 新特性學習筆記JDK筆記
- oracle學習筆記8: 分析函式Oracle筆記函式
- angular學習筆記(三十)-指令(8)-scopeAngular筆記
- DG學習筆記(8)_Switchover and Failover筆記AI