关于startService的基本使用概述及其生命周期可参见博客《Android中startService的使用及Service生命周期》。

本文通过批量下载文件的简单示例,演示startService以及stopService(startId)的使用流程。

系统界面如下:
Android通过startService实现批量下载示例_第1张图片

界面很简单,就一个按钮“批量下载文章”,通过该Activity上的按钮启动DownloadService。

DownloadService是用来进行下载CSDN上博客文章的服务,代码如下:

package com.ispring.startservicedemo;import android.app.Service;import android.content.Intent;import android.os.Handler;import android.os.IBinder;import android.os.Message;import android.util.Log;import android.widget.Toast;import java.io.IOException;import java.io.InputStream;import java.net.HttpURLConnection;import java.net.MalformedURLException;import java.net.URL;import java.util.ArrayList;import java.util.List;public class DownloadService extends Service {    //存储所有的startId    private List allStartIdList = new ArrayList<>();    //存储已经下载完成的startId    private List finishedStartIdList = new ArrayList<>();    private Handler handler = new Handler(){        @Override        public void handleMessage(Message msg) {            if(msg.what == 1){                String tip = (String)msg.obj;                Toast.makeText(DownloadService.this, tip, Toast.LENGTH_LONG).show();            }        }    };    class DownloadThread extends Thread {        //对应的intent的startId信息        private int startId = 0;        //要下载的文章名称        private String blogName = null;        //要下载的文章地址        private String blogUrl = null;        public DownloadThread(int startId, String name, String url){            this.startId = startId;            this.blogName = name;            this.blogUrl = url;        }        @Override        public void run() {            HttpURLConnection conn = null;            InputStream is = null;            try{                //下载指定的文件                URL url = new URL(this.blogUrl);                conn = (HttpURLConnection)url.openConnection();                if(conn != null){                    //我们在此处得到所下载文章的输入流,可以将其以文件的形式写入到存储卡上面或                    //将其读取出文本显示在App中                    is = conn.getInputStream();                }            }catch (MalformedURLException e){                e.printStackTrace();            }catch (IOException e){                e.printStackTrace();            }finally {                if(conn != null){                    conn.disconnect();                }            }            finishedStartIdList.add(startId);            if(finishedStartIdList.containsAll(allStartIdList)){                String tip = "全部下载完成, 数量" + finishedStartIdList.size();                Message msg = handler.obtainMessage(1);                msg.obj = tip;                handler.sendMessage(msg);            }            Log.i("DemoLog", "stopSelf(" + startId + ")");            stopSelf(startId);        }    }    @Override    public void onCreate() {        super.onCreate();        Log.i("DemoLog", "DownloadService -> onCreate");    }    @Override    public int onStartCommand(Intent intent, int flags, int startId) {        allStartIdList.add(startId);        String name = intent.getStringExtra("name");        String url = intent.getStringExtra("url");        Log.i("DemoLog", "DownloadService -> onStartCommand, startId: " + startId + ", name: " + name);        DownloadThread downloadThread = new DownloadThread(startId, name, url);        downloadThread.start();        return START_REDELIVER_INTENT;    }    @Override    public IBinder onBind(Intent intent) {        return null;    }    @Override    public void onDestroy() {        super.onDestroy();        Log.i("DemoLog", "DownloadService -> onDestroy");    }}

DownloadActivity的代码如下:

package com.ispring.startservicedemo;import android.app.Activity;import android.content.Intent;import android.os.Bundle;import android.view.View;import android.widget.Button;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.List;import java.util.Map;public class DownloadActivity extends Activity implements Button.OnClickListener {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_download);    }    @Override    public void onClick(View v) {        List list = new ArrayList<>();        list.add("Android中Handler的使用;http://blog.csdn.net/iispring/article/details/47115879");        list.add("深入源码解析Android中的Handler,Message,MessageQueue,Looper;http://blog.csdn.net/iispring/article/details/47180325");        list.add("Android新线程中更新主线程UI中的View方法汇总;http://blog.csdn.net/iispring/article/details/47300819");        list.add("Android中HandlerThread的使用及原理解析;http://blog.csdn.net/iispring/article/details/47320407");        list.add("Android中Looper的quit方法和quitSafely方法;http://blog.csdn.net/iispring/article/details/47622705");        Iterator iterator = list.iterator();        while (iterator.hasNext()){            String str = (String)iterator.next();            String[] splits = str.split(";");            String name = splits[0];            String url = splits[1];            Intent intent = new Intent(this, DownloadService.class);            intent.putExtra("name", name);            intent.putExtra("url", url);            startService(intent);        }    }}

当我们单击了按钮“批量下载文章”时,我们会多次调用Activity的startService方法,其中我们在其参数intent中存储了文章名name以及文章的地址url,由于我们多次调用了startService方法,所以会批量下载文章。

点击按钮后,控制台运行结果如下所示:
Android通过startService实现批量下载示例_第2张图片

调用了startService之后,Android Framework接收到了intent信息,第一次会先创建DownloadService的实例,然后执行其onCreate回调方法,onCreate在Service的生命周期中只会调用一次。

调用了onCreate方法后,Android会自动回调其onStartCommand方法,其实每次调用Context的startService都会触发onStartCommand回调方法,所以onStartCommand在Service的生命周期中可能会被调用多次。在onStartCommand方法中我们可以获得intent和startId,intent即我们调用startService方法时传入的参数,startId是Android自动分配的,每次调用startService都会自动得到一个startId,一个startId就意味着一个job,也就是意味着一次下载任务。我们在DownloadService中有两个字段allStartIdList和finishedStartIdList。allStartIdList存储着所有的startId,我们在onStartCommand方法一开始就把我们得到的startId放入到allStartIdList中存储,然后我们根据intent读取到文章名name和文章地址url,并根据这些信息创建一个新的线程DownloadThread,该线程用于执行实际的网络请求下载工作。需要注意的是,为了代码简化起见我们在onStartCommand中只要得到intent就开辟一个新线程(DownloadThread),但是在实际生产环境中这样的开销比较大(线程新建、线程销毁),应该尽量使用线程池以节约开销。

执行了DownloadThread的start方法后,就会执行DownloadThread线程的run方法,在该方法中我们会执行网络请求,获取博客文章的输入流,当我们获取到该输入流之后,我们就认为下载完成了,此时我们可以将其以文件的形式写入到存储卡上,也可以将其读取出文本显示在App上,此处我们没有对输入流做任何处理,我们就认为下载完成了。下载完成后,我们把startId存入到finishedStartIdList中,finishedStartIdList存储着所有已经完成的job的startId。当finishedStartIdList中已经包含了allStartIdList的所有startId时,说明我们所有的下载任务完成了,我们会通过handler让主线程显示Toast告知用户文章下载完成。在run方法的最后我们会执行Service的stopSelf(startId)方法。需要注意的是我们在stopSelf方法中传入了startId,这意味着我们不是直接停止Service的运行,我们只是停止该startId对应的job的执行,如果所有的startId所对应的job都停止了,那么整个Service才会停止,当整个Service停止时,才会触发执行Service的onDestroy回调方法。

本文只是通过批量下载博文这一简单示例演示通过startService以及stopSelf(startId)使用Service基本使用流程,代码没有进行优化,希望对大家学习Service有所帮助。

更多相关文章

  1. Android进程与线程
  2. android中完全退出当前应用程序的四种方法
  3. Android创建桌面快捷方式几种方法
  4. Android中获取控件宽高的4大方法
  5. Android 6.0开发实现关机菜单添加重启按钮的方法
  6. google被墙后,Android SDK下载和更新失败的解决方法!
  7. HierarchyView的实现原理和Android设备无法使用HierarchyView的
  8. android跨线程通信eventbus
  9. SONY 系列手机 Android 5.1 系统 Root 方法

随机推荐

  1. android Binder工作流程
  2. Android(安卓)studio 上 EventBus的初步
  3. android WebView结合jQuery mobile之基础
  4. eclipse上Android(安卓)NDK开发环境搭建
  5. Android(安卓)Vibrator 的用法
  6. Android:UI控件AutoCompleteTextView、Mul
  7. Android(安卓)使用Javascript作为脚本计
  8. 使用Uiautomator遇到的问题
  9. Android设备兼容性 1
  10. Android(安卓)Studio安装及首次运行遇到