Android应用开发之Android 服务下载示例
凌雪 2018-10-24 来源 :网络 阅读 1553 评论 0

摘要:本文将带你了解Android应用开发之Android 服务下载示例,希望本文对大家学Android有所帮助。

本文将带你了解Android应用开发之Android  服务下载示例,希望本文对大家学Android有所帮助。


最近重读了下《第一行代码》,看到《第一行代码》 的一个小项目,特写本文梳理下流程。首先终结一句话,在Android 的多线程处理中,尽量做到在子线程中进行耗时操作,在主线程中更新界面UI。好了,下面开始写这个项目。
一. 首先创建一个回调接口,用于对下载过程中的各种状态进行监听和回调,代码如下:
public interface DownloadListener   {    // 通知下载进度    void onProgress(int   progress);    // 通知下载成功    void   onSuccess();    // 通知下载失败    void   onFailed();    // 通知下载暂停    void   onPaused();    //通知下载失败    void onCanceled();}
ok,这里插一点题外话,回调函数。主要说一下回调函数的机制
(1).设置接口,定义回调函数
public interface MyListener   {    void onClick();}
(2).提供函数实现的一方 在初始化的时候,将回调函数的函数指针 注册给调用者。
public class Realize   {    private MyListener   myListener;     public void setMyListener(MyListener   myListener) {        this.myListener   = myListener;    }     public   void doSth()   {        myListener.onClick();    }}
(3).当特定的时间或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。
public class Test   {     public static void main(String[] args)   {        Realize realize = new   Realize();        realize.setMyListener(new   MyListener()   {            @Override            public   void onClick()   {                System.out.println("特定事件或条件发生了");            }        });         realize.doSth();    }}
上面就是一个回调函数的流程,仔细理解下,就能理解回调的实现了,下面回到正题。
二.使用AsyncTask 实现下载功能的处理。
emmm,这里还要插入一下AsyncTask的解释。AsyncTask是一种轻量级的异步任务类,这个类可以执行后台操作,并在用户界面上发布结果,而不必处理线程和处理程序。OK,先举个例子:
public class DownloadTask extends   AsyncTask<void, boolean="">   {     /**     * 刚开始执行的时候调用,可以用于进行一些界面上的初始化操作,比如说显示一个进度条对话框     */    @Override    protected   void onPreExecute()   {        super.onPreExecute();     }     /**     *   这里的代码会在子线程中执行,可以执行耗时操作     *     *   @param voids     *   @return     */    @Override    protected   Boolean doInBackground(Void... voids) {        //    反馈当前任务的进度,执行完这个方法会调用onProgressUpdate   方法        publishProgress(50);        return   null;    }     /**     *   根据返回的数据更新UI     *     *   @param   values     */    @Override    protected   void onProgressUpdate(Integer... values) {        super.onProgressUpdate(values);     }         /**     *   后台任务执行完毕通过return语句进行返回时  也可以根据返回的数据更新UI     *     *   @param   aBoolean     */    @Override    protected   void onPostExecute(Boolean aBoolean) {        super.onPostExecute(aBoolean);     }}</void,>
简单的来说,使用AsyncTask的诀窍就是,在doInBackground() 方法中执行具体的耗时任务,在onProgressUpdate()方法中进行UI操作,在onPostExecute() 方法中执行一些任务的收尾工作。 如果想要启动这个任务,只需编写以下代码即可:
new DownloadTask().execute();
继续回到正题(这样讲着讲着会不会歪楼呀) 先在app/build.gradle 文件中的 dependencies 中添加OKHttp 依赖
compile 'com.squareup.okhttp3:okhttp:3.4.1'
下面编写下载功能,新建一个DownloadFileTask   继承AsyncTask。
//   第一个参数  传给后台参数   第二个 使用整型数据作为进度显示单位   第三个  使用整型数据反馈执行结果public class DownloadFileTask extends AsyncTask<string,   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 isCancelled = false;    // 是否暂停    private   boolean isPaused = false;    // 当前进度    private int   lastProgress;     /**     *   带监听的构造函数     *     *   @param listener     */    public   DownloadFileTask(DownloadListener listener)   {        this.listener = listener;    }     /**     *   在后台执行具体的下载逻辑  是在子线程里面 可以执行耗时操作     */    @Override    protected   Integer doInBackground(String... strings)   {        // 文件输入流        InputStream is   = null;        RandomAccessFile   accessFile = null;        File file =   null;        // 记录已下载的文件长度        long   downloadedLength = 0;        // 获取下载的URL地址        String   downloadUrl = strings[0];        // 从URL下载地址中截取下载的文件名        String fileName   =   downloadUrl.substring(downloadUrl.lastIndexOf("/"));        //   获取SD卡的Download 目录        String   directory =   Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();        //    得到要保存的文件        file   = new File(directory +   fileName);        // 如果文件已经存在  获取文件的长度        if   (file.exists())   {            downloadedLength   =   file.length();        }        try   {            //     获取待下载文件的字节长度            long   contentLength =   getContentLength(downloadUrl);            //    如果待下载文件的字节长度为0 说明待下载文件有问题            if   (contentLength == 0)   {                return   TYPE_FAILED;            }   else if (contentLength == downloadedLength)   {                //     已下载字节和文件总字节相等 说明已经下载完了                return   TYPE_SUCCESS;            }            //    获取OkHttpClient 对象            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();                //   获取随机读取文件类  可以随机读取一个文件中指定位置的数据                accessFile   = new RandomAccessFile(file,   "rw");                //   跳过已下载的字节                accessFile.seek(downloadedLength);                //指定每次读取文件缓存区的大小为1KB                byte[]   b = new   byte[1024];                int   total = 0;                int   len;                //     每次读取的字节长度                while   ((len = is.read(b)) != -1)   {                    if   (isCancelled)   {                        return   TYPE_CANCELED;                    }   else if (isPaused)   {                        return   TYPE_PAUSED;                    }   else   {                        //   读取的全部字节的长度                        total   +=   len;                        //   写入每次读取的字节长度                        accessFile.write(b,   0,   len);                        //   计算已下载的百分比                        int   progress = (int) ((total + downloadedLength) * 100 /   contentLength);                        //   更新进度条                        publishProgress(progress);                    }                }                //   关闭连接  返回成功                response.body().close();                return   TYPE_SUCCESS;            }        }   catch (IOException e)   {            e.printStackTrace();        }   finally   {            try   {                //   关闭输入流                if   (is != null)   {                    is.close();                }                //   关闭文件                if   (accessFile != null)   {                    accessFile.close();                }                Log.d("TAG",   "这里永远都会执行   ");                //   如果是取消的  就删除掉文件                if   (isCancelled && file != null)   {                    file.delete();                }            }   catch (IOException e) {                e.printStackTrace();            }        }         return   null;    }     /**     *   获取下载文件的长度     *     *   @param downloadUrl     *   @return     * @throws   IOException     */    private   long getContentLength(String downloadUrl) throws IOException   {        // 获取OkHttpClient        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;    }     /**     *   在界面上更新当前的下载进度     *     *   @param   values     */    @Override    protected   void onProgressUpdate(Integer... values)   {        super.onProgressUpdate(values);        int   progress = values[0];        if   (progress > lastProgress)   {            listener.onProgress(progress);            lastProgress   =   progress;        }    }     /**     *   用于通知最后的下载结果     *     *   @param   integer     */    @Override    protected   void onPostExecute(Integer integer) {        super.onPostExecute(integer);        switch   (integer)   {            case   TYPE_SUCCESS:                listener.onSuccess();                break;            case   TYPE_FAILED:                listener.onFailed();                break;            case   TYPE_PAUSED:                listener.onPaused();                break;            case   TYPE_CANCELED:                listener.onCanceled();                break;        }    }     /**     *   暂停下载     */    public   void pauseDownload()   {        isPaused =   true;    }     /**     *   取消下载     */    public   void cancelDownload()   {        isCancelled =   true;    }}</string,>
   
    三. 创建一个下载的服务
没错,还得简单的介绍下服务(这么墨迹的吗)。一般我们都调用startService()方法来启动服务,并调用stopService()方法来停止这个服务,但这样启动服务后,活动无法干预到服务到底执行了怎样的逻辑。这时候就要用onBind()方法了,对服务进行绑定之后,就可以调用服务里的Binder   提供的方法了。OK,下面举个例子。首先创建一个MyService,代码如下:
public class MyService extends Service   {     private static final String TAG =   "MyService";     private DownloadBinder   mBinder = new DownloadBinder();     // 创建 DownloadBinder 实例  随便定义了两个方法    class DownloadBinder extends Binder   {         public void   startDownload()   {            Log.d(TAG,   "startDownload: executed");        }         public   int getProgress()   {            Log.d(TAG,   "getProgress: executed");            return   0;        }    }     /**     *   返回这个DownloadBinder 实例     *     *   @param intent     *   @return     */    @Override    public   IBinder onBind(Intent intent) {        return   mBinder;    }     }
然后在活动中绑定服务,实现活动去指挥服务去干什么。
public class FirstActivity extends   AppCompatActivity {     private Button   btnBind;    private Button   btnUnBind;     @Override    protected   void onCreate(Bundle savedInstanceState)   {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_first);         btnBind   = (Button)   findViewById(R.id.btnBind);        btnUnBind   = (Button)   findViewById(R.id.btnUnBind);         btnBind.setOnClickListener(new   View.OnClickListener() {            @Override            public   void onClick(View v)   {                //   绑定服务                Intent   intent = new Intent(FirstActivity.this,   MyService.class);                bindService(intent,   connection,   BIND_AUTO_CREATE);            }        });         btnUnBind.setOnClickListener(new   View.OnClickListener()   {            @Override            public   void onClick(View v) {                //    解绑服务                unbindService(connection);            }        });     }     private   MyService.DownloadBinder   downloadBinder;     ServiceConnection connection =   new ServiceConnection()   {        /**         *   活动与服务绑定成功后         *   @param name         * @param   service         */        @Override        public   void onServiceConnected(ComponentName name, IBinder service)   {            // 向下转型获得DownloadBinder 实例 就能调用DownloadBinder的方法 进而控制服务的逻辑            downloadBinder   = (MyService.DownloadBinder)   service;            downloadBinder.startDownload();            downloadBinder.getProgress();        }         /**         *   活动与服务解绑后         *   @param   name         */        @Override        public   void onServiceDisconnected(ComponentName name)   {         }    }; }
好的,了解到活动如何去控制服务以后,下面正式写 下载的服务,代码如下:
public class DownloadService extends Service   {    // 下载的异步操作类    private DownloadFileTask   downloadFileTask;    // 下载地址    private String   downloadUrl;     private static final String TAG =   "DownloadService";    // 下载状态的回调    private   DownloadListener listener = new DownloadListener()   {        /**         *    更新下载进度状态         *   @param   progress         */        @Override        public   void onProgress(int progress)   {            Log.d(TAG,   "onProgress: -------" +   progress);            getNotificationManager().notify(1,   getNotification("Downloading",   progress));        }         /**         *   下载成功         */        @Override        public   void onSuccess()   {            Log.d(TAG,   "onSuccess:   -------------");            downloadFileTask   =   null;            //下载成功时将前台服务通知关闭,并创建一个下载成功的通知            stopForeground(true);            getNotificationManager().notify(1,   getNotification("Download Success", -1));            Toast.makeText(DownloadService.this,   "Download Success",   Toast.LENGTH_SHORT).show();        }         /**         *   下载失败         */        @Override        public   void onFailed()   {            Log.d(TAG,   "onFailed:   -------------");            downloadFileTask   =   null;            //   下载失败时将前台服务通知关闭,并创建一个下载失败的通知            stopForeground(true);            getNotificationManager().notify(1,   getNotification("Download Failed", -1));            Toast.makeText(DownloadService.this,   "Download Failed",   Toast.LENGTH_SHORT).show();        }         /**         *   下载暂停         */        @Override        public   void onPaused()   {            Log.d(TAG,   "onPaused:   -------------");            downloadFileTask   =   null;            Toast.makeText(DownloadService.this,   "Download Paused",   Toast.LENGTH_SHORT).show();        }         /**         *   下载取消         */        @Override        public   void onCanceled()   {            Log.d(TAG,   "onCanceled:   -------------");            downloadFileTask   =   null;            stopForeground(true);            Toast.makeText(DownloadService.this,   "Download Canceled",   Toast.LENGTH_SHORT).show();        }    };     DownloadBinder   mBinder = new   DownloadBinder();     /**     *   返回这个DownloadBinder 实例     *     *   @param intent     *   @return     */    @Override    public   IBinder onBind(Intent intent) {        return   mBinder;    }     // 创建 DownloadBinder 实例     class   DownloadBinder extends Binder   {        // 开始下载        public void   startDownload(String url)   {            Log.d(TAG,   "startDownload--------: 开始下载");            if   (downloadFileTask == null)   {                downloadUrl   = url;                downloadFileTask   = new   DownloadFileTask(listener);                downloadFileTask.execute(downloadUrl);                startForeground(1,   getNotification("DownLoading",   0));                Toast.makeText(DownloadService.this,   "Downloading",   Toast.LENGTH_SHORT).show();            }        }         //   暂停下载        public   void pauseDownload()   {            Log.d(TAG,   "pauseDownload--------: 暂停下载");            if   (downloadFileTask != null)   {                downloadFileTask.pauseDownload();            }        }         //   取消下载        public   void cancelDownload()   {            Log.d(TAG,   "cancelDownload--------: 取消下载---" +   downloadFileTask);            if   (downloadFileTask != null)   {                downloadFileTask.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();                }            }        }    }     /**     *   获取通知栏管理器     *     *   @return     */    public   NotificationManager getNotificationManager()   {        return (NotificationManager)   getSystemService(NOTIFICATION_SERVICE);    }     /**     *   设置通知栏的样式 并获取通知栏的实例     *     *   @param title     * @param   progress     *   @return     */    private   Notification getNotification(String title, int progress)   {        Intent[] intents = new   Intent[]{(new Intent(this,   DownloadActivity.class))};        PendingIntent   pi = PendingIntent.getActivities(this, 0, intents,   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();    }     }
四.接下来在活动中绑定这个服务就可以了。
1.先实现下界面,修改xml 中的代码,创建三个按钮。启动 暂停 取消
?12<!--xml version="1.0"   encoding="utf-8"--><linearlayout   android:layout_height="match_parent"   android:layout_width="match_parent"   android:orientation="vertical" xmlns:android="https://schemas.android.com/apk/res/android"><button   android:id="@+id/btnStart" android:layout_height="60dp"   android:layout_width="200dp" android:text="Start   Download"></button><button android:id="@+id/btnPause"   android:layout_height="60dp" android:layout_width="200dp"   android:text="Pause Download"></button><button   android:id="@+id/btnCancel" android:layout_height="60dp"   android:layout_width="200dp" android:text="Cancel   Download"></button></linearlayout>
2.在活动中绑定服务,让活动与服务进行通信,因为要对文件下载,还要动态的申请写文件的权限,代码如下:
public class DownloadActivity extends   AppCompatActivity implements View.OnClickListener {    //   开始下载按钮    private   Button btnStart;    // 暂停下载按钮    private Button   btnPause;    // 取消下载按钮    private Button   btnCancel;    // 服务    private Intent   intent;    // 下载操作的实例    private DownloadService.DownloadBinder   downloadBinder;     private ServiceConnection   serviceConnection = new ServiceConnection()   {        /**         *   活动与服务绑定成功后         *   @param name         * @param   service         */        @Override        public   void onServiceConnected(ComponentName name, IBinder service)   {            downloadBinder   = (DownloadService.DownloadBinder) service;        }         /**         *   活动与服务解绑后         *   @param   name         */        @Override        public   void onServiceDisconnected(ComponentName name)   {         }    };     @Override    protected   void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_download);        btnStart   = (Button) findViewById(R.id.btnStart);        btnPause   = (Button)   findViewById(R.id.btnPause);        btnCancel   = (Button)   findViewById(R.id.btnCancel);         btnStart.setOnClickListener(this);        btnPause.setOnClickListener(this);        btnCancel.setOnClickListener(this);        //   启动服务并绑定        intent   = new Intent(this,   DownloadService.class);        startService(intent);        bindService(intent,   serviceConnection, BIND_AUTO_CREATE);        //   获取写的权限        if   (ContextCompat.checkSelfPermission(DownloadActivity.this,                Manifest.permission.WRITE_EXTERNAL_STORAGE)   != PackageManager.PERMISSION_GRANTED)   {            ActivityCompat.requestPermissions(DownloadActivity.this,                    new   String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},   1);        }    }     @Override    public   void onClick(View v) {        if   (downloadBinder == null)   {            return;        }        switch   (v.getId())   {            case   R.id.btnStart:                String   url = "https://raw.githubusercontent.com/guolindev/eclipse/master/eclipse-inst-win64.exe";                downloadBinder.startDownload(url);                break;            case   R.id.btnPause:                downloadBinder.pauseDownload();                break;            case   R.id.btnCancel:                downloadBinder.cancelDownload();                break;        }    }     /**     *   申请权限的返回结果     *     *   @param requestCode     * @param   permissions     * @param   grantResults     */    @Override    public   void onRequestPermissionsResult(int requestCode, @NonNull String[]   permissions, @NonNull int[] grantResults)   {        super.onRequestPermissionsResult(requestCode,   permissions, grantResults);        switch   (requestCode)   {            case   1:                if   (grantResults.length > 0 && grantResults[0] !=   PackageManager.PERMISSION_GRANTED)   {                    Toast.makeText(this,   "拒绝权限将无法使用程序",   Toast.LENGTH_SHORT).show();                    finish();                }                break;        }    }     /**     *   结束活动的时候关闭服务     */    @Override    protected   void onDestroy()   {        super.onDestroy();        unbindService(serviceConnection);        stopService(intent);    }}
最后在AndroidManifest.xml文件中声明使用的权限,还有别忘了服务也是需要声明的。
这个项目的总结就到这里就结束了。。。    

本文由职坐标整理并发布,希望对同学们有所帮助。了解更多详情请关注职坐标移动开发之Android频道!

本文由 @凌雪 发布于职坐标。未经许可,禁止转载。
喜欢 | 0 不喜欢 | 0
看完这篇文章有何感觉?已经有0人表态,0%的人喜欢 快给朋友分享吧~
评论(0)
后参与评论

您输入的评论内容中包含违禁敏感词

我知道了

助您圆梦职场 匹配合适岗位
验证码手机号,获得海同独家IT培训资料
选择就业方向:
人工智能物联网
大数据开发/分析
人工智能Python
Java全栈开发
WEB前端+H5

请输入正确的手机号码

请输入正确的验证码

获取验证码

您今天的短信下发次数太多了,明天再试试吧!

提交

我们会在第一时间安排职业规划师联系您!

您也可以联系我们的职业规划师咨询:

小职老师的微信号:z_zhizuobiao
小职老师的微信号:z_zhizuobiao

版权所有 职坐标-一站式AI+学习就业服务平台 沪ICP备13042190号-4
上海海同信息科技有限公司 Copyright ©2015 www.zhizuobiao.com,All Rights Reserved.
 沪公网安备 31011502005948号    

©2015 www.zhizuobiao.com All Rights Reserved