Android四大组件之Service全面学习
2017-11-18 12:50
736 查看
什么是服务
Android 的服务是 Android 四大组件之一,其他的三大组件是Activity、Broadcast Receiver、Content Provider。它是程序运行在后台的解决方案。
服务依赖于应用程序进程,它创建它的进程被杀掉时,服务就会停止。
服务不会主动创建在子线程中,需要我们自己手动在服务内部创建子线程,否则主线程就有可能被阻塞住。
所以在学习服务之前,需要补充一点多线程编程的知识。
Android 多线程编程
线程的多种用法Android 的多线程基本上跟 Java 是一致的。
创建一个类,继承自 Thread并重写父类的 run()方法:
class MyThread extends Thread{ @Override public void run(){ // 处理具体的逻辑 } }
调用这个线程:
new MyThread().start();
使用继承的方式耦合性较高,可以选择实现Runnable接口的方式来定义一个线程:
class MyThread implements Runnable{ @Override public void run(){ // 处理具体的逻辑 } }
实现 Runnable 接口的方式定义一个线程,调用它跟之前的继承的调用有些区别:
MyThread myThread = new MyThread(); new Thread(myThread).start();
如果连实现接口都觉得麻烦,可以使用匿名类的方式,这种方法更加普遍:
new Thread(new Runnable(){ @Override public void run(){ // 处理具体的逻辑 } }).start();
在子线程中更新 UI
Android 的 UI 也是线程不安全的,也就是说它的 UI 元素必须在主线程中进行。
我们可以通过一个例子,来演示 UI 元素如何在子线程中更新就是不安全的,会报错的。
这个例子是这样的,有两个控件,一个按钮,一个 textView,点击按钮改变textView的文字,不过改变文字是在子线程中进行的。
首先在res/layout/activity_main.xml中添加按钮和textView:
<Button android:id="@+id/change_text" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Change Text"/> <TextView android:id="@+id/text" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="Hello world" android:textSize="20sp" />
然后在主程序中开辟线程,在线程中更新UI
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private TextView text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text = (TextView)findViewById(R.id.text); Button changeText = (Button)findViewById(R.id.change_text); changeText.setOnClickListener(this); } @Override public void onClick(View v){ switch (v.getId()){ case R.id.c 1601d hange_text: new Thread(new Runnable() { @Override public void run() { text.setText("Nice to see you"); } }).start(); break; default: break; } } }
结果当然是,app崩溃。
但是我们确实需要在子线程中执行一些耗时任务,然后根据结果更新UI,应该怎么办?
Android提供了一套异步消息处理机制,完美地解决了在子线程中更新UI的问题。
我们将代码修改如下:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { public static final int UPDATA_TEXT = 1; // 添加 private Handler handler = new Handler(){ // 添加 public void handleMessage(Message msg){ // 添加 switch(msg.what){ case UPDATA_TEXT: text.setText("Nice to meet you"); break; default: break; } } }; private TextView text; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); text = (TextView)findViewById(R.id.text); Button changeText = (Button)findViewById(R.id.change_text); changeText.setOnClickListener(this); } @Override public void onClick(View v){ switch (v.getId()){ case R.id.change_text: new Thread(new Runnable() { @Override public void run() { Message message = new Message(); // 添加 message.what = UPDATA_TEXT; // 添加 handler.sendMessage(message); // 添加 } }).start(); break; default: break; } } }
解析异步消息处理机制
通过异步消息的处理,可以成功的在子线程中更新UI了(本质上是传递给Handler的handleMessage()在主线程中进行)。
Android的异步消息处理包括4个部分:Message、Handler、MessageQueue和Looper。
其中Message和Handler在之前已经接触过了。
接下来详细解析这几个部分:
Message:之前已经接触了它的what字段,它还可以使用arg1和arg2携带整型,使用obj携带Object对象。
Handler:它的使用方式是先在子线程中sendMessage(),最终传递到Handler的handleMessage()方法中。
MessageQueue:用于存放Handler发送的所有消息,这些消息放在队列中,等待被处理。每个线程中只有一个MessageQueue对象。
Looper:Looper就是MessageQueue的管家,调用Looper的loop()方法后,就会进行死循环中,然后发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage()方法中,每个线程中同样也只有一个Looper对象。
异步处理机制可以很好的解决子线程中更新UI的问题,Android将它封装成了一个更简单的接口:
runOnUiThread()
使用AsyncTask
为了方便在子线程中对UI进行操作,Android还提供了另外一些好用的工具,比如AsyncTask。
借助AsyncTask,即使你对异步消息处理机制不了解,也可以很简单的从子线程切换到主线程。
AsyncTask的背后也是基于异步消息处理机制的,只是Android帮我们做了很好的封装。
AsyncTask基本用法
AsyncTask是一个抽象类,如果要使用它就要创建一个子类去继承它。
在继承时,可以为AsyncTask指定3个泛型参数。这3个参数的用途如下:
Params。执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
Progress。后台任务执行时,如果需要在界面上显示进行,就使用这个参数作为进度单位。
Result。任务执行完毕时,如果需要结果返回,则可以使用这里指定的泛型作为返回值类型。
一个最简单的AsyncTask可以写成如下形式:
class DownloadTask extends AsyncTask<Void,Integer,Boolean>{ }
这只是一个空任务,需要重写几个方法才能完成对方法的定制。
// 在任务开始前执行,主要是对界面的一些初始化操作 onPreExecute()
// 这个方法中的所有代码都会在子线程中运行,应该在这里处理耗时任务。 // 如果AsyncTask的第三个参数是Void,可以不用返回。 // 这个方法是不可以进行UI操作的。 doInBackground(Params...)
// 在执行doInBackground(Params...)时,如果要更新UI元素,比如说任务进度,可以在这个方法中完成 publishProgress(Progress...)
// 如果在后台任务中调用了publishProgress(Progress...),这个方法很快就会被调用。 // 它的参数就是后台任务中传递过来的。 // 在这个方法中可以进行UI操作。 onProgressUpdate(Progress...)
// 当后台任务执行完毕后,这个方法很快就会被调用。 // 返回的参数会作为参数传递到这个方法中。 onPostExecute(Result)
// 如果要启动这个任务 new Download().execute();
服务的基本用法
定义一个服务右键包名/New/Service/Service,就可以新建一个继承于Services的类了。
新建页上面有个属性Exported,是指是否允许给其他应用使用该服务,Enabled是指是否开启这个服务。
下面就是标准的服务代码:
public class MyService extends Service { public MyService() { } @Override // 这个方法是Service中唯一的抽象方法,必须在子类中实现。 public IBinder onBind(Intent intent) { // TODO: Return the communication channel to the service. throw new UnsupportedOperationException("Not yet implemented"); } @Override // 只会在服务第一次启动时调用。 public void onCreate(){ super.onCreate(); Log.d("MyService","onCreate executed"); } @Override // 它会在服务每次启动时调用。 public int onStartCommand(Intent intent,int flags,int startId){ Log.d("MyService","onStartCommand executed"); return super.onStartCommand(intent,flags,startId); } @Override public void onDestroy(){ super.onDestroy(); Log.d("MyService","onDestroy executed"); } }
每一个服务需要进行注册才能生效。
Android的四大组件都需要进行注册才能生效,不过Android Studio已经帮我们做好这一切了,只要我们是使用标准向导创建的四大组件。
打开AndroidManifest.xml,已经可以看到注册好了。
<service android:name=".MyService" android:enabled="true" android:exported="true"></service>
启动和停止服务
启动和停止服务主要是借助Intent来实现的。
我们创建两个按钮,一个是启动Service的,一个是停止Service的。
在res/layout/activity_main.xml中添加两个按钮。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <Button android:id="@+id/start_service" android:text="Start Service" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/end_service" android:text="End Service" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
然后写点击按钮的逻辑。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button startService = (Button)findViewById(R.id.start_service); Button endService = (Button)findViewById(R.id.end_service); startService.setOnClickListener(this); endService.setOnClickListener(this); } @Override public void onClick(View v){ switch (v.getId()){ case R.id.start_service: Intent startIntent = new Intent(this,MyService.class); // 启动服务 startService(startIntent); break; case R.id.end_service: Intent endIntent = new Intent(this,MyService.class); // 停止服务 // 在MyService类中的任何地方调用stopSelf()也可以停止服务 stopService(endIntent); break; default: break; } } }
活动和服务进行通信
前面服务开启以后就会一直运行,似乎跟活动没啥关系了。也不需要活动去控制什么,一切都是自动化的。
那么要在活动中指挥服务去干什么,应该怎么办呢?
还记得前面重写的onBind()抽象方法吗?
它就是干这个的!
onBind()使用的一个小例子
假如服务提供了一个下载的功能,在活动中我想随时开始,以及查看进度,这时候就可以在服务中创建一个专门的Binder对象来对下载功能进行管理。
public class MyService extends Service { public MyService() { } private DownloadBinder mBinder = new DownloadBinder();//添加 class DownloadBinder extends Binder{ // 添加 public void startDownload(){ Log.d("MyService","startDownload executed"); } public int getProgress(){ Log.d("MyService","getProgress executed"); return 0; } } @Override public IBinder onBind(Intent intent) { return mBinder; // 添加 } @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(); } }
那么如何在活动中去调用服务里的这些方法呢?
首先要创建两个按钮用来绑定活动和服务
<Button android:id="@+id/bind_service" android:text="Bind Service" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/unbind_service" android:text="UnBind Service" android:layout_width="match_parent" android:layout_height="wrap_content" />
在主窗口中点击绑定按钮,就会绑定服务和活动,点击解绑按钮,就会解绑服务和活动
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private MyService.DownloadBinder downloadBinder; private ServiceConnection connection = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName componentName, IBinder service) { downloadBinder = (MyService.DownloadBinder)service; downloadBinder.startDownload(); downloadBinder.getProgress(); } @Override public void onServiceDisconnected(ComponentName componentName) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button bindService = (Button)findViewById(R.id.bind_service); Button unbindService = (Button)findViewById(R.id.unbind_service); bindService.setOnClickListener(this); unbindService.setOnClickListener(this); } @Override public void onClick(View v){ switch (v.getId()){ case R.id.bind_service: Intent bindIntent = new Intent(this,MyService.class); bindService(bindIntent,connection,BIND_AUTO_CREATE); break; case R.id.unbind_service: unbindService(connection); break; default: break; } } }
服务的生命周期
以onCreate()(如果不是第一次就是以onStartCommand())开始,以stopService()或stopSelf()结束。
一个服务只要被启动或者被绑定以后,就会一直运行,除非两者都停止或解绑,服务才会被销毁,才会调用onDestory()方法。
服务的更多技巧
使用前台服务后台服务的优先级比较低,当内存不足的时候,有可能就被释放了。
前台服务就不会被任意释放掉。
前台服务会有一个图标显示在系统的状态栏上。
下拉通知栏还会有详细的信息。
public class MyService extends Service { public MyService() { } @Override public void onCreate(){ super.onCreate(); Intent intent = new Intent(this,MainActivity.class); PendingIntent pi = PendingIntent.getActivity(this,0,intent,0); Notification notification = new NotificationCompat.Builder(this) .setContentTitle("This is content title") .setContentText("This is content text") .setWhen(System.currentTimeMillis()) .setSmallIcon(R.mipmap.ic_launcher) .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher)) .setContentIntent(pi) .build(); startForeground(1,notification); } @Override // 它会在服务每次启动时调用? public int onStartCommand(Intent intent,int flags,int startId){ return super.onStartCommand(intent,flags,startId); } @Override public void onDestroy(){ super.onDestroy(); } @Nullable @Override public IBinder onBind(Intent intent) { return null; } }
使用IntentService
服务默认是在主线程中运行的,所以我们要在服务的每个具体方法中开启一个子线程。
为了创建一个异步的、会自动停止的服务,我们可能要写如下代码:
public int onStartCommand(Intent intent,int flags,int startId){ new Thread(new Runnable() { @Override public void run() { // 处理具体的逻辑 stopSelf(); } }); return super.onStartCommand(intent,flags,startId); }
但是这样写的问题的是,如果忘了开辟子线程或是忘记stopSelf(),服务就会产生问题。
会了更简单的处理这样的事,Android提供了一个IntentService()。
新建一个服务继承自IntentService:
public class MyIntentService extends IntentService { public MyIntentService() { super("MyIntentService"); } @Override // 这个方法已经在子线程中运行了。 protected void onHandleIntent(Intent intent) { Log.d("MyIntentService","Thread id is" + Thread.currentThread().getId()); } @Override // 当子线程中的代码执行完,就会调用它。 public void onDestroy(){ super.onDestroy(); Log.d("MyIntentService","onDestroy executed"); } }
为了证明它会自动在子线程中运行,我们在onHandleIntent()方法里打印了当前线程号。
为了证明它会自动销毁服务,我们没有使用停止服务按钮,而是在onDestroy()方法中打印了一下,证明它在服务运行结束后,会自动停止。
使用向导创建服务会自动生成注册服务的代码:
<service android:name=".MyIntentService" />
但是使用向导创建IntentService会生成一大堆不需要的代码,所以采用手动的方式,记住要注册服务才会生效。
服务的最佳实践
这一节会写一个基于服务的下载器,里面会用到之前学过的内容,由于代码较多,我做好了注释,一起贴出来。这个小 demo 里会用到okhttp3,它是用于Java和 Android 的下载框架。
在app/build.gradle里面的dependencies添加:
dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:24.2.1' compile 'com.squareup.okhttp3:okhttp:3.4.1' }
res/layout/activity_main里面添加三个按钮,分别用于下载、暂停、取消。
<Button android:id="@+id/start_download" android:text="start download" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:id="@+id/pause_download" android:text="pause_download" android:layout_width="match_parent" android:layout_height="wrap_content" /> <Button android:text="cancel download" android:id="@+id/cancel_download" android:layout_width="match_parent" android:layout_height="wrap_content" />
MainActivity:
public class MainActivity extends AppCompatActivity implements View.OnClickListener { // Binder:用于在活动中操作服务的对象 private DownloadService.DownloadBinder downloadBinder; // 通过binder连接服务和活动 private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName componentName, IBinder iBinder) { downloadBinder = (DownloadService.DownloadBinder) iBinder; } @Override public void onServiceDisconnected(ComponentName componentName) { } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // 三个按钮,开始下载,暂停下载,取消下载 Button startDownload = (Button)findViewById(R.id.start_download); Button pauseDownload = (Button)findViewById(R.id.pause_download); Button cancelDownload = (Button)findViewById(R.id.cancel_download); // 按钮响应 startDownload.setOnClickListener(this); pauseDownload.setOnClickListener(this); cancelDownload.setOnClickListener(this); // 开启服务 Intent intent = new Intent(this,DownloadService.class); startService(intent); // 绑定服务和活动 bindService(intent,connection,BIND_AUTO_CREATE); // 请求外部存储器权限 if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED){ ActivityCompat.requestPermissions(MainActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1); } } @Override // 请求外部存储器权限结果 public void onRequestPermissionsResult(int requestCode,String[] permisson,int[] grantResult){ switch (requestCode){ case 1: if (grantResult.length > 0 && grantResult[0] != PackageManager.PERMISSION_GRANTED){ Toast.makeText(this,"拒绝权限将无法使用程序",Toast.LENGTH_SHORT).show(); finish(); } break; default: } } @Override public void onClick(View v){ if (downloadBinder == null) return; switch (v.getId()){ case R.id.start_download: // 调用服务进行下载 String url = "http://sw.bos.baidu.com/sw-search-sp/software/6c864ea1874b3/setup_bd.exe"; downloadBinder.startDownload(url); break; case R.id.pause_download: // 调用服务暂停下载 downloadBinder.pauseDownload(); break; case R.id.cancel_download: // 调用服务取消下载 downloadBinder.cancelDownload(); break; default: break; } } @Override protected void onDestroy(){ super.onDestroy(); // 销毁时取消服务和活动的绑定 unbindService(connection); } }
DownloadListener,接口,用于在服务中回调:
public interface DownloadListener { void onProgress(int progress); void onSuccess(); void onFailed(); void onPaused(); void onCanceled(); }
DownloadService 继承自Service:
public class DownloadService extends Service { public DownloadService() { } // 实现回调接口 // downloadtask下载、暂停等状态会发送消息给服务\ // 这里的实现就是收到状态信息后的处理 private DownloadListener listener = new DownloadListener() { @Override // downloadtask正在下载,通知栏显示downloading public void onProgress(int progress) { getNotificationManager().notify(1,getNotification("Downloading...",progress)); } @Override // downloadtask下载完毕,关闭task,停止前台服务,通知栏显示download success等 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 // downloadtask下载失败,关闭task,停止前台服务,通知栏显示download failed等 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 // downloadtask下载暂停,关闭task等 public void onPaused() { downloadTask = null; Toast.makeText(DownloadService.this,"Paused",Toast.LENGTH_SHORT).show(); } @Override // downloadtask下载取消,关闭task、停止前台服务等 public void onCanceled() { downloadTask = null; stopForeground(true); Toast.makeText(DownloadService.this,"Canceld",Toast.LENGTH_SHORT).show(); } }; // 继承于AsyncTask,用于处理下载任务 private DownloadTask downloadTask; private String downloadUrl; // binder对象,用于连接活动和服务,用于在活动中操作服务 private DownloadBinder mBinder = new DownloadBinder(); class DownloadBinder extends Binder { // 开始下载 public void startDownload(String url){ if (downloadTask == null){ downloadUrl = url; // 传递监听对象,用于在task状态更新时通知回调 downloadTask = new DownloadTask(listener); // task开始执行,并在task中的doInBackground函数的子线程中执行代码 downloadTask.execute(downloadUrl); // 开启前台服务,并在通知栏显示downloading startForeground(1,getNotification("Downloading...",0)); Toast.makeText(DownloadService.this,"Downloading...",Toast.LENGTH_SHORT).show(); } } public void pauseDownload(){ if (downloadTask != null){ // 标记改为pause,使doInBackground中的下载暂停 downloadTask.pauseDownload(); } } public void cancelDownload(){ if (downloadTask != null){ // 标记改为cancel,使doInBackground中的下载取消 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,"Canceld",Toast.LENGTH_SHORT).show(); } } } } @Override public IBinder onBind(Intent intent) { return mBinder; } 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){ builder.setContentText(progress + "%"); builder.setProgress(100,progress,false); } return builder.build(); }
DownloadTask,继承于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 boolean isCanceled = false; private boolean isPaused = false; private int lastProgress; private DownloadListener listener; // 接收一个listener对象,listener会在服务中实现接口,用于回调 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); // 每次读1024个字节,然后不断接到下载的文件上 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 // static来自于doInBackground的返回值 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(); break; 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; } }
给这个示例程序作一个总结:
活动与服务之间有一个连接者binder,用于在活动中控制服务要做的事情。
服务处理任务时交给AsyncTask的子类,这个子类会在子线程中异步的处理任务。服务与这个子类之间有若干个接口方法实现监听子类的状态。
监听到的状态实时的反应到通知栏里。
就是这么简单。~
参考:
《第一行代码 Android 第2版》.郭霖.人民邮电出版社.2016.12
相关文章推荐
- Android 四大组件学习之Service七
- Android基础学习【历史流程重走】 ---- 四大组件之Service
- Android 四大组件学习之Service四
- Android学习--四大组件之 Service(一)
- Android 四大组件学习之Service五
- Android成长日记-Android四大组件之Service组件的学习
- 【android学习】四大组件-Service
- Android四大组件之Activity全面学习
- Android四大组件之Service服务学习笔记(一)
- android 四大组件之service学习总结(一)
- Android学习--四大组件之 Service(二)
- Android四大组件之一Service介绍-android学习之旅(十二)
- Android 四大组件学习之Service六
- Android四大组件学习之Service
- Android四大组件之一Service介绍-android学习之旅(十二)
- Android 学习之四大组件(二)——service
- Android 四大组件学习之Service三
- Android学习日志11__四大组件02__service
- Android学习——四大组件之Service
- Android 四大组件学习之Service六