运用IntentService类下载文件

来源:互联网 发布:留香久的香水知乎 编辑:程序博客网 时间:2024/05/17 09:30

什么是IntentService?
官方的解析:
IntentService is a base class for Services that handle asynchronous requests (expressed as Intents) on demand. Clients send requests through android.content.Context.startService(Intent) calls; the service is started as needed, handles each Intent in turn using a worker thread, and stops itself when it runs out of work.

This “work queue processor” pattern is commonly used to offload tasks from an application’s main thread. The IntentService class exists to simplify this pattern and take care of the mechanics. To use it, extend IntentService and implement onHandleIntent(Intent). IntentService will receive the Intents, launch a worker thread, and stop the service as appropriate.

All requests are handled on a single worker thread – they may take as long as necessary (and will not block the application’s main loop), but only one request will be processed at a time.

大致意思:IntentService是一个通过Context.startService(Intent)启动处理异步请求的Service,使用时你只需要继承IntentService和重写其中的onHandleIntent(Intent)方法接收一个Intent对象,在处理完事物之后会停止自己(Service). 所有的请求的处理都在一个工作线程中完成,它们会交替执行(但不会阻塞主线程的执行),一次只能执行一个请求.

而IntentService继承Service :
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
private String mName;
private boolean mRedelivery;

private final class ServiceHandler extends Handler {    public ServiceHandler(Looper looper) {        super(looper);    }    @Override    public void handleMessage(Message msg) {        onHandleIntent((Intent)msg.obj);        stopSelf(msg.arg1);    }}public IntentService(String name) {    super();    mName = name;}public void setIntentRedelivery(boolean enabled) {    mRedelivery = enabled;}@Overridepublic void onCreate() {    super.onCreate();    HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");    thread.start();    mServiceLooper = thread.getLooper();    mServiceHandler = new ServiceHandler(mServiceLooper);}@Overridepublic void onStart(Intent intent, int startId) {    Message msg = mServiceHandler.obtainMessage();    msg.arg1 = startId;    msg.obj = intent;    mServiceHandler.sendMessage(msg);}@Overridepublic int onStartCommand(Intent intent, int flags, int startId) {    onStart(intent, startId);    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;}@Overridepublic void onDestroy() {    mServiceLooper.quit();}@Overridepublic IBinder onBind(Intent intent) {    return null;}protected abstract void onHandleIntent(Intent intent);

}

通过对以上源码分析(源码注视太多,被我删除了):
这是一个基于消息的服务,每次启动该服务并不是马上处理你的工作,而是首先会创建对应的Looper,Handler并且在MessageQueue中添加的附带客户Intent的Message对象,当Looper发现有Message的时候接着得到Intent对象通过在onHandleIntent((Intent)msg.obj)中调用你的处理程序.处理完后即会停止自己的服务.意思是Intent的生命周期跟你的处理的任务是一致的.所以这个类用下载任务中非常好,下载任务结束后服务自身就会结束退出.

那么Service 和 IntentService又有什么区别呢?
Service的官方解析:
1.A Service is not a separate process. The Service object itself does not imply it is running in its own process; unless otherwise specified, it runs in the same process as the application it is part of.

2.A Service is not a thread. It is not a means itself to do work off of the main thread (to avoid Application Not Responding errors).
大致意思可以理解为:
Service不是一个单独的进程 ,它和应用程序在同一个进程中,Service不是一个线程,所以我们应该避免在Service里面进行耗时的操作。

对比可以发现:
Service里面是不能进行耗时的操作的,必须要手动开启一个工作线程来处理耗时操作,IntentService里面是可以进行耗时的操作的,IntentService使用队列的方式将请求的Intent加入队列,然后开启一个worker thread(线程)来处理队列中的Intent,对于异步的startService请求,IntentService会处理完成一个之后再处理第二个(IntentService采用单独的线程每次只从队列中拿出一个请求进行处理)

//好了,进入主题
第一步:创建保存下载升级文件的路径
public class FileUtil {

private static String TAG;private static File updatePath; /** *数据储存目录(获得本机sd卡内存) *getExternalStorageDirectory()获取默认的存储路径,默认是sd卡存储是获得的是sd卡的根路径, *默认手机内存则获取的手机内存的根路劲 */public static final String DATA_PATH= Environment.getExternalStorageDirectory()+"/Whithers";public static File createFile() {    /**     * 判断SD卡是否存在     * 并且可以进行写操作     */    if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {        updatePath = new File(MainUrl.DATA_PATH);        if (updatePath.exists()) {            Log.v(TAG, "多级目录已经存在不需要创建!");        } else {            updatePath.mkdirs();        }    }    /**     * 构造一个新文件,使用指定的目录和名称。     */    return new File(updatePath,"whithers.apk");}/** * 获取文件大小 * @param file * @return */public  static int getFileSize(File file,Context context){    int size = 0;    if (file.exists()) {        FileInputStream inputStream;        try {            inputStream = new FileInputStream(file);            size = inputStream.available();        } catch (FileNotFoundException e) {            e.printStackTrace();        } catch (IOException e) {            e.printStackTrace();        }        return size;    } else {        SharedpreferencesTools.saveVersionSize(context,0);       return size;    }}/** * 删除文件 * @param file * @return */public  static void deleteFile(File file){    if (file.exists()) {        file.delete();    }}

}

第二步:创建静态方法来读取保存app更新文件的最新下载时间跟大小
//这里用SharedPreferences保存,也可改用sqlite保存

/** * 保存下载app的最新时间 */public static void savaVersionTime(Context context,String time){    sharedPreferences=context.getSharedPreferences("VersionTime",Context.MODE_PRIVATE);    Editor editor=sharedPreferences.edit();    editor.putString("time",time);    editor.commit();}/** * 用于保存网络读取的最新版本号 */public static String getVersionTime(Context context){    sharedPreferences=context.getSharedPreferences("VersionTime",Context.MODE_PRIVATE);    return sharedPreferences.getString("time","2015-09-10 18:08");}/** * 保存下载app的最新大小 */public static void saveVersionSize(Context context,int size){    sharedPreferences=context.getSharedPreferences("VersionSize",Context.MODE_PRIVATE);    Editor editor=sharedPreferences.edit();    editor.putInt("size", size);    editor.commit();}/** * 获取下载app的最新大小 */public static int getVersionSize(Context context){    sharedPreferences=context.getSharedPreferences("VersionSize",Context.MODE_PRIVATE);    return sharedPreferences.getInt("size", 0);}

第三步:创建根据网络类型设置HttpURLConnection自适应
//getDefaultHost()和getDefaultPort()提示已过期,不知道有什么新的方法呢
//大神请指教
public class HttpURLConnections {

private HttpURLConnection httpURLConnection = null;private int CONNECTION_TIME = 5 * 1000;private int CONNECTION_OUT = 10 * 1000;private Context context;public HttpURLConnections(Context context) {    this.context = context;}/** * 设置在移动网络下自适应 * @param url_path * @return */public HttpURLConnection openConnection(String url_path) {    URL url;    HttpURLConnection connection = null;    try {        url = new URL(url_path);        //获取网络服务系统组件        ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);        //获得网络类型        NetworkInfo networkInfo = connectivityManager.getActiveNetworkInfo();        if (networkInfo.getType() == ConnectivityManager.TYPE_MOBILE) {            //获取默认代理主机            String host = android.net.Proxy.getDefaultHost();            //获得端口            int port = android.net.Proxy.getDefaultPort();            if (host != null && port != -1) {                //封装代理主机ip与端口号                InetSocketAddress inAddress = new InetSocketAddress(host, port);                //根据URL链接获取代理类型,本链接适用于TYPE.HTTP                java.net.Proxy.Type proxyType = java.net.Proxy.Type.valueOf(url.getProtocol().toUpperCase());                java.net.Proxy javaProxy = new java.net.Proxy(proxyType, inAddress);                connection = (HttpURLConnection) url.openConnection(javaProxy);            }        } else {            connection = (HttpURLConnection) url.openConnection();        }        //设置连接超时时间        connection.setConnectTimeout(CONNECTION_OUT);        //设置读取超时时间        connection.setReadTimeout(CONNECTION_TIME);        /**         * 允许输入         * 默认是true         */        connection.setDoInput(true);    } catch (MalformedURLException e) {        e.printStackTrace();    } catch (IOException e) {        e.printStackTrace();    }    return connection;}

}

第四步:创建一个类继承IntentService
public class UpDataIntentService extends IntentService {
// TODO: Rename actions, choose action names that describe tasks that this
// IntentService can perform, e.g. ACTION_FETCH_NEW_ITEMS
private static final String ACTION_FOO =”com.example.whithers.VersionUpdates.action.FOO”;

// TODO: Rename parameters
private static final String EXTRA_PARAM1 = “com.example.whithers.VersionUpdates.extra.PARAM1”;

private static Context contexts;
private static String version_path;
/**
* httpURLConnection的相关设置
*/
private final int TIME_OUT = 10 * 1000;
private final int DOWN_OK = 1;
private final int DOWN_ERRER = 0;

//通知管理器
public static NotificationManager notificationManager;
/**
* 通知栏设置
*/
private Notification notification;
private Intent UpdateIntent;
private RemoteViews remoteViews;
private PendingIntent pendingIntent;
private final int notification_id = 1;
private final int HTTP_OK = 200;
private final int HTTP_OK_TWO = 206;
private Handler handler = new Handler();
//获取下载文件长度
private int FileSize;
//获取当前下载大小
private int DownloadSize;
private SimpleDateFormat simpleDateFormat;
private String now_time;
//一天的毫秒数
private static long Millisecond = 1000 * 24L * 60L * 60L;
private static HttpURLConnections httpURLConnections;
private static File file;

/** * Starts this service to perform action Foo with the given parameters. If * the service is already performing a task this action will be queued. * * @see IntentService */// TODO: Customize helper methodpublic static void startActionFoo(Context context, String param) {    contexts = context;    httpURLConnections = new HttpURLConnections(context);    Intent intent = new Intent(context, UpDataIntentService.class);    intent.setAction(ACTION_FOO);    intent.putExtra(EXTRA_PARAM1, param);    context.startService(intent);}public UpDataIntentService() {    super("UpDataIntentService");}@Overrideprotected void onHandleIntent(Intent intent) {    if (intent != null) {        final String action = intent.getAction();        if (ACTION_FOO.equals(action)) {            final String param1 = intent.getStringExtra(EXTRA_PARAM1);            handleActionFoo(param1);        }    }}/** * Handle action Foo in the provided background thread with the provided * parameters. */private void handleActionFoo(String param) {    // TODO: Handle action Foo    createNotification(param);}/** * 启动一个通知栏 * 通知用户下载的进去 */private void createNotification(String param) {    /**     * 自定义Notification视图     */    remoteViews = new RemoteViews(getPackageName(),      R.layout.activity_update_version_layout);    remoteViews.setTextViewText(R.id.notificationTitle, getString(R.string.app_name)            + "--" + getString(R.string.download));    remoteViews.setTextViewText(R.id.notificationPercent, "0%");    remoteViews.setProgressBar(R.id.notificationProgress, 100, 0, false);    UpdateIntent = new Intent();    UpdateIntent.setClass(this, MainActivity.class);    UpdateIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);    pendingIntent = PendingIntent.getActivity(this, 0, UpdateIntent, 0);    /**     * 初始化通知栏     */    String name = getString(R.string.start_download) + " " + getString(R.string.app_name);    notification = new Notification(R.mipmap.icon, name, System.currentTimeMillis());    //设置不能清除此通知栏    notification.flags |= Notification.FLAG_NO_CLEAR;    notification.contentView = remoteViews;    notification.contentIntent = pendingIntent;    //获取通知栏系统服务    notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);    //发出通知    notificationManager.notify(notification_id, notification);    //获取现在的时间    now_time = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(new Date());    //设置时间格式    simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");    HttpURLConnection httpURLConnection =httpURLConnections.openConnection(param);    //获取下载文件大小    FileSize = httpURLConnection.getContentLength();   httpURLConnection.disconnect();    //开始下载文件    downloadFile(param);}/** * 下载文件 */private void downloadFile(String param) {    //字节流    InputStream stream = null;    FileOutputStream outputStream = null;    //用于随机读写    RandomAccessFile randomAccessFile = null;    /**     * 创建文件     * 获得文件路劲     */    file = FileUtil.createFile();    try {        //获取上次下载文件的时间        String beforeTime = SharedpreferencesTools.getVersionTime(contexts);        long timeDifference = (simpleDateFormat.parse(now_time).getTime() -                simpleDateFormat.parse(beforeTime).getTime()) / Millisecond;        //在此路径下是否有文件,返回文件大小,否则返回0        DownloadSize = FileUtil.getFileSize(file, contexts);        //获得保存文件大小        int saveSize = SharedpreferencesTools.getVersionSize(contexts);        //时间在一天内        if (timeDifference <= 1 && timeDifference >= 0) {            if (saveSize > 0 && DownloadSize == saveSize                    && saveSize == FileSize && DownloadSize==FileSize) {                Message.sendEmptyMessage(DOWN_OK);                return;            }        } else {            saveSize = 0;        }        //用HttpHttpURLConnection下载文件        HttpURLConnection httpURLConnection = httpURLConnections.openConnection(param);        //设置读取数据范围        if (saveSize != 0) {            httpURLConnection.setRequestProperty("Range", "bytes=" + DownloadSize + "-" + FileSize);        }else{            DownloadSize = 0;            //删除指定文件            FileUtil.deleteFile(file);        }        //设置访问方式        httpURLConnection.setRequestMethod("GET");        /**         * 获得下载文件输入流,获取字节流         * 正式建立连接         */        stream = httpURLConnection.getInputStream();        //设置可读写操作        randomAccessFile = new RandomAccessFile(file, "rw");        //设置文件大小        //randomAccessFile.setLength(FileSize);        //outputStream = new FileOutputStream(file);        //建立一个byte数组作为缓冲区        byte buffer[] = new byte[1024];        int read_size;        /**         * httpURLConnection.getResponseCode()服务器响应码         * 200代表HTTP_OK         * 206为读取不同位置数据响应码         */        if (httpURLConnection.getResponseCode() == HTTP_OK                || httpURLConnection.getResponseCode() == HTTP_OK_TWO) {            //开始检查下载进度            handler.post(runnable);            randomAccessFile.seek(DownloadSize);            //保存文件大小            SharedpreferencesTools.saveVersionSize(contexts, FileSize);            //保存时间            SharedpreferencesTools.savaVersionTime(contexts,now_time);            while ((read_size = stream.read(buffer)) != -1) {                //outputStream.write(buffer, 0, read_size);                randomAccessFile.write(buffer, 0, read_size);                //获取当前下载大小                DownloadSize += read_size;            }            Message.sendEmptyMessage(DOWN_OK);        } else {            throw new Exception("fail!");        }    } catch (MalformedURLException e) {        e.printStackTrace();    } catch (IOException e) {        Message.sendEmptyMessage(DOWN_ERRER);        e.printStackTrace();    } catch (Exception e) {        Message.sendEmptyMessage(DOWN_ERRER);        e.printStackTrace();    } finally {        try {            /*if (outputStream != null) {                outputStream.close();            }*/            if (randomAccessFile != null) {                randomAccessFile.close();            }            if (stream != null) {                stream.close();            }        } catch (IOException e) {            e.printStackTrace();        }    }     if(httpURLConnection != null){            httpURLConnection.disconnect();        }}private Runnable runnable = new Runnable() {    @Override    public void run() {        //改变通知栏        remoteViews.setTextViewText(R.id.notificationPercent, DownloadSize * 100 / FileSize + "%");        remoteViews.setProgressBar(R.id.notificationProgress, 100, DownloadSize * 100 / FileSize, false);        notification.contentView = remoteViews;        //展示通知        notificationManager.notify(notification_id, notification);        //设置定时更新进度        handler.postDelayed(runnable, 1000);    }};@Overridepublic void onDestroy() {    //当服务停止后移除定时的线程    handler.removeCallbacks(runnable);    super.onDestroy();}/** * 处理完成是的操作 */private Handler Message = new Handler() {    @Override    public void handleMessage(Message msg) {        switch (msg.what) {            //下载完成点击安转            case DOWN_OK:                Uri uri = Uri.fromFile(file);                Intent intent = new Intent(Intent.ACTION_VIEW);                //执行的数据类型                intent.setDataAndType(uri, "application/vnd.android.package-archive");                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);                contexts.startActivity(intent);                pendingIntent = PendingIntent.getActivity(UpDataIntentService.this, 0, intent, 0);                //通知栏显示在状态栏时,更换信息                notification.tickerText = getString(R.string.app_name) + " " + getString(R.string.complete);                notification.setLatestEventInfo(UpDataIntentService.this, getString(R.string.app_name),                        getString(R.string.download_ok), pendingIntent);                //正在更新                MainApplication.isupdated = false;                break;            //下载出错            case DOWN_ERRER:                //通知栏显示在状态栏时,更换信息                notification.tickerText = getString(R.string.app_name) + " " + getString(R.string.download_errer);                notification.setLatestEventInfo(UpDataIntentService.this, getString(R.string.app_name),                        getString(R.string.download_errer), pendingIntent);                //正在更新                MainApplication.isupdated = false;                break;            default:                break;        }        //该标志表示当用户点击 Clear 之后,能够清除该通知        notification.flags |= Notification.FLAG_AUTO_CANCEL;        //更新通知栏状态        notificationManager.notify(notification_id, notification);    }};

}

获取服务器版本与本地版本号对比后,升级时调UpDataIntentService.startActionFoo(context,url_path);即可。
有不妥当的地方请大神指教。

0 0
原创粉丝点击