zoukankan      html  css  js  c++  java
  • 第一行代码----服务的最佳实践(体会,问题,解决)

    package com.example.zhudashi.servicebestpractice;
    
    import android.Manifest;
    import android.app.Activity;
    import android.content.ComponentName;
    import android.content.Context;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.content.pm.PackageManager;
    import android.os.IBinder;
    import android.os.storage.StorageManager;
    import android.support.annotation.NonNull;
    import android.support.v4.app.ActivityCompat;
    import android.support.v4.content.ContextCompat;
    import android.support.v7.app.AppCompatActivity;
    import android.os.Bundle;
    import android.view.View;
    import android.widget.Button;
    import android.widget.Toast;
    
    import java.lang.reflect.Method;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    
        private DownloadService.DownloadBinder downloadBinder;
    
        private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder service) {
                //活动和服务绑定的时候执行
                downloadBinder = (DownloadService.DownloadBinder) service;
            }
    
            @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);
    
    //        Method mMethodGetPaths = null;
    //        String[] paths = null;
    //        //通过调用类的实例mStorageManager的getClass()获取StorageManager类对应的Class对象
    //        //getMethod("getVolumePaths")返回StorageManager类对应的Class对象的getVolumePaths方法,这里不带参数
    //        Context context = MainActivity.this;
    //        StorageManager mStorageManager = (StorageManager)context
    //                .getSystemService(context.STORAGE_SERVICE);//storage
    //        try {
    //            mMethodGetPaths = mStorageManager.getClass().getMethod("getVolumePaths");
    //            paths = (String[]) mMethodGetPaths.invoke(mStorageManager);
    //        } catch (Exception e) {
    //            // TODO Auto-generated catch block
    //            e.printStackTrace();
    //        }
    //        System.out.println("zhxing33 path = "+paths[0]);
    //        System.out.println("zhxing33 path = "+paths[1]);
    
            Intent intent = new Intent(this,DownloadService.class);
            startService(intent);//连接服务
            bindService(intent,connection,BIND_AUTO_CREATE);
            //绑定服务,通过connection进行主线程和子线程之间的交互
            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 onClick(View view) {
            if(downloadBinder == null){//未绑定的情况
                return ;
            }
            switch (view.getId()){
                case R.id.start_download:
                    String url = "https://raw.githubusercontent.com/guolindev/eclipse/master/eclipse-inst-win64.exe";
                    downloadBinder.startDownload(url);
                    break;
                case R.id.pause_download:
                    downloadBinder.pauseDownload();
                    break;
                case R.id.cancel_download:
                    downloadBinder.cancelDownload();
                    break;
                    default:
                        break;
            }
    
        }
    
        @Override
        public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
            switch (requestCode){
                case 1:
                    if(grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED){
                        Toast.makeText(this,"拒绝权限无法使用程序",Toast.LENGTH_SHORT).show();
                        finish();
                    }
                    break;
                default:
    
            }
        }
    
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            unbindService(connection);
        }
    }

    1.onCreate()方法中值得注意的是,服务的启动和绑定,

    Intent intent = new Intent(this,DownloadService.class);
            startService(intent);//启动服务
    启动服务只需要一个intent来展现MainActivity和服务之间的关系即可

    bindService(intent,connection,BIND_AUTO_CREATE);
    绑定服务,使用参数connection对象中的service对象来达到在活动中操作服务中方法的目的。

    这里有一个疑问,为什么需要启动服务,直接绑定服务不就行了吗?书上说是为了让DownloadService一直在后台运行,我认为是不成立的,因为后面又把该活动绑定了,所以DownloadService并不会一直在后台运行,他会随着活动的结束而结束,
    所以书上说启动和绑定都需要就让人很费解了。

    注:
    1.通过startservice开启的服务.一旦服务开启, 这个服务和开启他的调用者之间就没有任何的关系了. 
    调用者不可以访问 service里面的方法. 调用者如果被系统回收了或者调用了ondestroy方法, service还会继续存在  
    2.通过bindService开启的服务,服务开启之后,调用者和服务之间 还存在着联系 , 
    一旦调用者挂掉了.service也会跟着挂掉 .


    2.
    private ServiceConnection connection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder service) {
                //活动和服务绑定的时候执行
                downloadBinder = (DownloadService.DownloadBinder) service;
            }
    
            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                //活动和服务断开连接时执行
    
            }
        };
    运用匿名类,继承ServiceConnection类,重写的onServiceConnected方法在活动和服务绑定的时候执行,其中把service赋值为downloadBinder是为了能在活动中通过downloadBinder调用服务中的方法(onClick()中就有调用)。

    package com.example.zhudashi.servicebestpractice;
    
    import android.app.Notification;
    import android.app.NotificationManager;
    import android.app.PendingIntent;
    import android.app.Service;
    import android.content.Intent;
    import android.graphics.BitmapFactory;
    import android.os.Binder;
    import android.os.Environment;
    import android.os.IBinder;
    import android.support.v4.app.NotificationCompat;
    import android.util.Log;
    import android.widget.Toast;
    
    import java.io.File;
    
    public class DownloadService extends Service {
    
        private DownloadTask downloadTask;
        private String downloadUrl;
    
        //匿名类,继承downloadlistener接口,UI操作
        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,"pause",Toast.LENGTH_SHORT).show();
    
            }
    
            @Override
            public void onCanceled() {
                downloadTask = null;
                stopForeground(true);
                Toast.makeText(DownloadService.this,"Cancled",Toast.LENGTH_SHORT).show();
    
            }
        };
    
        private DownloadBinder mBinder = new DownloadBinder();//用来连接主线程和子线程的联结器,继承至Binder。
    
    
        @Override
        public IBinder onBind(Intent intent) {
            // TODO: Return the communication channel to the service.
            return mBinder;
        }
    
    
        class DownloadBinder extends Binder {
    
            public void startDownload(String url){//启动下载
                if(downloadTask == null){
                    downloadUrl = url;
                    downloadTask = new DownloadTask(listener);//开启异步类(下载和ui更新在里面处理)
                    downloadTask.execute(downloadUrl);//用于启动该异步类
                    startForeground(1,getNotification("Downloading...",0));//前台服务开启,1是通知id,类似notify()方法的第一个参数
                    //第二个参数是构建出来的Notification对象。
                    //调用该方法会让该service变成一个前台服务。
                    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();
    //                    String directory = System.getenv("EXTERNAL_STORAGE");
                        File file = new File(directory+fileName);
                        System.out.println("zhixing2 path = "+directory+fileName);
                        if(file.exists()){
                            file.delete();
                            System.out.println("zhixng4");
                       }
                    getNotificationManager().cancel(1);//取消通知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){
            //用来构建一个通知,title是该通知的id。
            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();//生成一个通知
        }
    }

    1.

     @Override
        public IBinder onBind(Intent intent) {
            // TODO: Return the communication channel to the service.
            return mBinder;
        }
    继承了service就需要重写onBind()方法,onBind()方法返回的对象mBinder就是用来连接活动和服务的IBinder类型的对象,返回到了MainActivity活动中,在活动和服务绑定时执行onServiceConnected()方法赋值给了downloadBinder以保证
    可以在MainActivity活动中操作该服务。、


    2.这里又用到一个匿名类,实现DownloadListener接口,里面有五个方法,分别实现以下功能:
    void onProgress(int progress);//下载通知
    void onSuccess();//下载成功通知和提示
    void onFailed();//下载失败通知和提示
    void onPaused();//下载暂停提示
    void onCanceled();//下载取消的提示,关闭前台通知。

    3.
    public void startDownload(String url){//启动下载
        if(downloadTask == null){
    downloadUrl = url;
    downloadTask = new DownloadTask(listener);//开启异步类(下载和ui更新在里面处理)
    downloadTask.execute(downloadUrl);//用于启动该异步类
    startForeground(1,getNotification("Downloading...",0));//前台服务开启,1是通知id,类似notify()方法的第一个参数
    //第二个参数是构建出来的Notification对象。
    //调用该方法会让该service变成一个前台服务。
    Toast.makeText(DownloadService.this,"Downloading...",Toast.LENGTH_SHORT).show();
    }
    }
    downloadTask.execute(downloadUrl);
    这条语句可以理解为开启了子线程,里面有一些耗时的操作以及一些UI操作,该类具体怎么运作,可以看下嘛的代码




    package com.example.zhudashi.servicebestpractice;
    
    import android.os.AsyncTask;
    import android.os.Environment;
    import android.os.storage.StorageManager;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.RandomAccessFile;
    
    import okhttp3.OkHttpClient;
    import okhttp3.Request;
    import okhttp3.Response;
    
    /**
     * Created by zhudashi on 2018/2/14.
     */
    
    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[] paths = null;
                //通过调用类的实例mStorageManager的getClass()获取StorageManager类对应的Class对象
                //getMethod("getVolumePaths")返回StorageManager类对应的Class对象的getVolumePaths方法,这里不带参数
    
                String directory = Environment.getExternalStoragePublicDirectory(
                        Environment.DIRECTORY_DOWNLOADS).getPath();
    //            String directory = System.getenv("EXTERNAL_STORAGE");
                    file = new File(directory+filename);
                    System.out.println("zhixing file = "+file.getPath());
                    if(file.exists()){
                    downloadedLength = file.length();
                }
                long contentLength = getContentLength(downloadUrl);
                    System.out.println("zhixing3 length="+downloadedLength);
                    //获取的是要下载文件的总长度
                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();
                        //此时response.body().contentLength()获取的是还需要下载的字节长度。
                        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) {
            //后台任务调用publishProgress()方法后调用该方法,主线程中运行,进行UI操作
            int progress = values[0];
            if(progress > lastProgress){//这次的下载进度和上次的下载进去进行比较
                listener.onProgress(progress);
                lastProgress = progress;
            }
        }
    
    
        @Override
        protected void onPostExecute(Integer status) {
            //当后台任务执行完毕通过return语句进行返回的时侯,这个方法会被调用,返回数据会传到该
            //方法中,可以利用返回数据进行一些UI操作。
            //根据参数传入的下载状态进行回调。
            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;
        }
    
    
    }

    1.该类继承AsyncTask异步类,是为了方便我们在子线程中对UI进行操作,其具体用法书上347页有详细的解答。

    简单来说我们把耗时的操作(下载操作)放在doInBackground()(可以简单的认为在执行完downloadTask.execute(downloadUrl);后执行该函数中的语句)中实现,该函数中的代码全部在子线程中运行。

    onProgressUpdate()方法(在执行publishprogress()方法后执行该方法)中实现一些UI操作,因为其代码在主线程中运行。

    onPostExecute()用来接收doInBackground()中的返回数据,通过不同的返回数据执行不同的UI操作。doInBackground()返回数据时,会在返回数据前先执行finally|{}中的语句。

    2.

      String filename = downloadUrl.substring(downloadUrl.lastIndexOf("/"));  

      //读取最后一个/后面的字符串,即下载文件的名字

      String directory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).getPath();

     //得到内置sd卡的路径(这里强调内置sd卡)/storage/emulated/0/Download/。

    file = new File(directory+filename);


    http://blog.csdn.net/qq_34471736/article/details/54964385
    http://blog.csdn.net/tobacco5648/article/details/70314621
    //获取内置和外置sd卡路径的方法

    我获取的外置sd卡路径是: /storage/0403-0201 不同的sd卡可能不一样,不知道为什么就是存不进去,测试了很多遍。




  • 相关阅读:
    剑指Offer 13.机器人的运动范围
    笔试题目-无向图是否全连通
    面试题目-最小代价的寻路问题
    京东一面问题
    剑指Offer 07.重建二叉树
    剑指Offer 12.矩阵中的路径
    剑指Offer 10-I.斐波那契数列
    剑指Offer 06.从尾到头打印链表
    剑指Offer 05.替换空格
    剑指Offer 04.二维数组中的查找
  • 原文地址:https://www.cnblogs.com/aloney/p/8449404.html
Copyright © 2011-2022 走看看