服务Service的基本用法

Posted 曹半斤

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了服务Service的基本用法相关的知识,希望对你有一定的参考价值。

作为 android四大组件之一, 服务也少不了有很多非常重要的知识点,那自然要从最基本的用法开始学习了。

定义一个服务

public class MyService extends Service {
    /**
     * onBind是继承Service后唯一的一个抽象方法所以必须要重写的一个方法
     */
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
    /**
     * 服务每次启动的时候调用
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("MyService", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }
    /**
     * 在服务创建的时候调用
     */
    @Override
    public void onCreate() {
        Log.i("MyService", "onCreate");
        super.onCreate();
    }
    /**
     * 会在服务销毁的时候调用
     */
    @Override
    public void onDestroy() {
        Log.i("MyService", "onDestroy");
        super.onDestroy();
    }

启动和停止服务

public class MyServiceActivity extends Activity{
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            // TODO Auto-generated method stub
            super.onCreate(savedInstanceState);
            setContentView(R.layout.androidservice_activity);
            Button start=(Button) findViewById(R.id.start);
            start.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Intent intent=new Intent(MyServiceActivity.this,MyService.class);
                    startService(intent);//启动服务
                }
            });

            Button stop=(Button) findViewById(R.id.stop);
            stop.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Intent intent=new Intent(MyServiceActivity.this,MyService.class);
                    stopService(intent);//停止服务
                }
            });
        }
}

注意,这里完全是由活动来决定服务何时停止的,如果没有点击Stop Service 按钮, 服务就会一直处于运行状态。 那服务有没有什么办法让自已停止下来呢?当然可以, 只需要在 MyService 的任何一个位置调用 stopSelf()方法就能让这个服务停止下来了。

我们可以看到Logcat打印的信息:
这里写图片描述

点击start服务成功启动,点击stop然后成功销毁, 运行成功后可以在正在运行的应用列表当中找到这个应用,停止掉之后就找不到了:

这里写图片描述

这里写图片描述

已经学会了启动服务以及停止服务的方法,不知道你心里现在有没有一个疑惑,那就是 onCreate()方法和 onStartCommand()到底有什么区别呢?因为刚刚点击Start Service按钮后两个方法都执行了。其实 onCreate()方法是在服务第一次创建的时候调用的,而 onStartCommand()方法则在每次启动服务的时候都会调用,由于刚才我们是第一次点击 Start Service 按钮,服务此时还未创建过,所以两个方法都会执行,之后如果你再连续多点击几次 Start Service 按钮,你就会发现只有 onStartCommand()方法可以得到执行了。

活动和服务进行通信

我们在活动里调用了 startService()方法来启动 MyService这个服务,然后 MyService的 onCreate()和onStartCommand()方法就会得到执行。之后服务会一直处于运行状态,但具体运行的是什么逻辑,活动控制不了了。这就类似于活动通知了服务一下: “你可以启动了! ”然后服务就去忙自己的事情了,但活动并不知道服务到底去做了什么事情,以及完成的如何。那么有没有什么办法能让活动和服务的关系更紧密一些呢?例如在活动中指挥服务去干什么,服务就去干什么。当然可以,这就需要借助我们刚刚忽略的 onBind()方法了。
比如说目前我们希望在 MyService里提供一个下载功能,然后在活动中可以决定何时开始下载, 以及随时查看下载进度。 实现这个功能的思路是创建一个专门的 Binder对象来对下载功能进行管理,修改 MyService中的代码,如下所示:

public class MyService extends Service {
    /**
     * 创建binder对象
     */
    DownLoadBind bind = new DownLoadBind();

    class DownLoadBind extends Binder {
        /**
         * 一个开始下载的方法
         */
        public void startDownload() { 
            Log.i("DownLoadBind", "startDownload");
        }
        /**
         * 一个获取进度的方法
         * @return
         */
        public int getProgress() {
            Log.i("DownLoadBind", "getProgress");
            return 0;
        }
    }

    /**
     * onBind是继承Service后唯一的一个抽象方法所以必须要重写的一个方法
     * 该方法是为了服务可以与服务进行通信,例如在活动中指挥服务去干什么,服务就去干什么
     * 返回binder对象
     */
    @Override
    public IBinder onBind(Intent intent) {
        return bind;
    }

    /**
     * 服务每次启动的时候调用
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.i("MyService", "onStartCommand");
        return super.onStartCommand(intent, flags, startId);
    }

    /**
     * 在服务创建的时候调用
     */
    @Override
    public void onCreate() {
        Log.i("MyService", "onCreate");
        super.onCreate();
    }

    /**
     * 会在服务销毁的时候调用
     */
    @Override
    public void onDestroy() {
        Log.i("MyService", "onDestroy");
        super.onDestroy();
    }

接下来看下MyServiceActivity中的代码,在MyServiceActivity新增了两个按钮

public class MyServiceActivity extends Activity{
        private MyService.DownLoadBind down;
        /**
         * 创建ServiceConnection的匿名内部类并重写两个方法
         */
        private ServiceConnection connection=new ServiceConnection() {
            /**
             * 在与服务解除绑定的时候调用
             */
            @Override
            public void onServiceDisconnected(ComponentName name) {


            }
            /**
             * 绑定成功的时候调用
             */
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                down=(DownLoadBind) service;//通过向下转型得到DownLoadBind的实例有了这个实例活动与服务的关系就变得非常紧密了
                down.startDownload();
                down.getProgress();
            }
        };
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.androidservice_activity);
            Button start=(Button) findViewById(R.id.start);
            start.setOnClickListener(new OnClickListener(){

                @Override
                public void onClick(View v) {
                    Intent intent=new Intent(MyServiceActivity.this,MyService.class);
                    startService(intent);//开始服务
                }
            });

            Button stop=(Button) findViewById(R.id.stop);
            stop.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Intent intent=new Intent(MyServiceActivity.this,MyService.class);
                    stopService(intent);//停止服务
                }
            });

            Button bind=(Button) findViewById(R.id.BindService);
            bind.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Intent intent=new Intent(MyServiceActivity.this,MyService.class);
                    /**
                     * 第一个参数刚刚构建出的 Intent对象  第二个参数前面创建出的 ServiceConnection的实例,第三个则是一个标志位
                     * 这里传入 BIND_AUTO_CREATE 表示在活动和服务进行绑定后自动创建服务。这会使得 
                     * MyService 中的 onCreate()方法得到执行,但 onStartCommand()方法不会执行。
                     */
                    bindService(intent, connection, BIND_AUTO_CREATE);//绑定服务
                }
            });

            Button unbind=(Button) findViewById(R.id.UnbindService);
            unbind.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    unbindService(connection);//解绑服务点击了就会服务就会停止运行
                }
            });
        }
}

到此为止activity可以调用服务当中任何public方法。实现了在活动当中去控制服务当中的方法,想让服务干嘛就干嘛。
运行下程序:

这里写图片描述

Logcat当中可以看到成功运行服务当中的两个方法以及onCreate方法。另外需要注意,任何一个服务在整个应用程序范围内都是通用的,即 MyService不仅可以和 MainActivity 绑定,还可以和任何一个其他的活动进行绑定,而且在绑定完成后它们都可以获取到相同的 DownloadBinder实例。

服务的生命周期

学习过了活动以及碎片的生命周期。类似地,服务也有自己的生命周期,前面我们使用到的

onCreate()、onStartCommand()、onBind()和 onDestroy()等方法都是在服务的生命周期内可能回调的方法。一旦在项目的任何位置调用了 Context 的startService()方法,相应的服务就会启动起来,并回调 onStartCommand()方法。

如果这个服务之前还没有创建过,onCreate()方法会先于onStartCommand()方法执行。服务启动了之后会一直保持运行状态,直到stopService()或stopSelf()方法被调用。注意虽然每调用一次 startService()方法,onStartCommand()就会执行一次, 但实际上每个服务都只会存在一个实例。

所以不管你调用了多少次 startService()方法,只需调用一次 stopService()或stopSelf()方法,服务就会停止下来了。另外,还可以调用 Context 的bindService()来获取一个服务的持久连接,这时就会回调服务中的onBind()方法

类似地,如果这个服务之前还没有创建过,onCreate()方法会先于onBind()方法执行。之后,调用方可以获取到onBind()方法里返回的 IBinder对象的实例,这样就能自由地和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。当调用了 startService()方法后,又去调用 stopService()方法

这时服务中的 onDestroy()方法就会执行,表示服务已经销毁了。类似地,当调用了 bindService()方法后,又去调用unbindService()方法,
onDestroy()方法也会执行,这两种情况都很好理解。但是需要注意,我们是完全有可能对一个服务既调用了 startService()方法,又调用了 bindService()方法的,

这种情况下该如何才能让服务销毁掉呢?根据 Android系统的机制,一个服务只要被启动或 者被绑定了之后,就会一直处于运行状态,必须要让以上两种条件同时不满足,服务才能被 销毁。所以,这种情况下要同时调用stopService()和 unbindService()方法,onDestroy()方法才 会执行。

这样你就已经把服务的生命周期完整地走了一遍。

使用 IntentService

服务中的代码都是默认运行在主线程当中的,如果直接在服务里去处理一些耗时的逻辑,就很容易出现 ANR(Application NotResponding)的情况。所以这个时候就需要用到 Android多线程编程的技术了,我们应该在服务的每个具体的方法里开启一个子线程,然后在这里去处理那些耗时的逻辑。因此,一个比较标准的服务就可以写成如下形式:

public class MyService extends Service {
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
// 处理具体的逻辑
stopSelf();
}
}).start();
return super.onStartCommand(intent, flags, startId);
}
}

虽说这种写法并不复杂, 但是总会有一些程序员忘记开启线程, 或者忘记调用 stopSelf()
方法。为了可以简单地创建一个异步的、会自动停止的服务,Android 专门提供了一个
IntentService 类,这个类就很好地解决了前面所提到的两种尴尬,下面我们就来看一下它的
用法。

public class MyIntentService extends IntentService{
    /**
     * 调用父类的有构造函数
     * @param name
     */
    public MyIntentService() {
        super("MyIntentService");
    }
    /**
     * 这个方法可以去处理一些具体的逻辑问题,不用担心ARN问题
     * 这个方法已经是在子线程中运行的了
     */
    @Override
    protected void onHandleIntent(Intent intent) {
        // 打印当前线程的id
        Log.d("MyIntentService", "子线程ID" + Thread.currentThread().getId());
    }
    /**
     * 在创建服务后是会自动停止的
     */
    @Override
    public void onDestroy() {
        // TODO Auto-generated method stub
        super.onDestroy();
        Log.i("MyIntentService", "onDestroy");
    }
}

在MyServiceActivity新增一个按钮分别打印子线程ID以及主线程ID
这里写图片描述

Button startintent=(Button) findViewById(R.id.startintent);
            startintent.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Log.i("MyServiceActivity", "主线程ID"+Thread.currentThread().getId());
                    Intent intent=new Intent(MyServiceActivity.this,MyIntentService.class);
                    startService(intent);
                }
            });

运行程序:Logcat打印信息:
这里写图片描述

可以看到, 不仅 MyIntentService和 MainActivity 所在的线程 id 不一样, 而且 onDestroy()方法也得到了执行, 说明 MyIntentService在运行完毕后确实自动停止了。 集开启线程和自动停止于一身IntentService 还是博得了不少程序员的喜爱。

后台执行的定时任务

下面我们就来创建一个可以长期在后台执行定时任务的服务。Android中的定时任务一般有两种实现方式,一种是使用 Java API 里提供的 Timer 类,一种是使用 Android的 Alarm机制。我们来看看Alarm机制。

public class LongRunningService extends Service{

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                //打印时间信息
                Log.d("LongRunningService", "executed at " + new Date().
                        toString());
            }
        }).start();
        AlarmManager manager=(AlarmManager) getSystemService(ALARM_SERVICE);
        int hour=3000;//每隔三秒刷新一次
        /**
         * 使用 SystemClock.elapsedRealtime()方法可以获取到系统开机至今所经历时间的毫秒数,
         * 使用 System.currentTimeMillis()方法可以获取到 1970年 1 月 1日 0点至今所经历时间的毫秒数。
         */
        long triggerAtTime = SystemClock.elapsedRealtime() + hour;
        Intent i = new Intent(this, AlarmReceiver.class);
        PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, 0);
        /**
         * set第一个参数整型参数,用于指定 AlarmManager的工作类型,有四种值可选
         * ELAPSED_REALTIME、ELAPSED_REALTIME_WAKEUP、RTC 和 RTC_WAKEUP
         * ELAPSED_REALTIME: 表示让定时任务的触发时间从系统开机开始算起,但不会唤醒 CPU
         * ELAPSED_REALTIME_WAKEUP: 同样表示让定时任务的触发时间从系统开机开始算起,但会唤醒 CPU
         * RTC:表示让定时任务的触发时间从 1970 年 1月 1 日 0点开始算起,但不会唤醒 CPU
         * RTC_WAKEUP: 同样表示让定时任务的触发时间从1970 年 1 月 1 日 0 点开始算起,但会唤醒 CPU
         * 
         * 第二个参数表示就是定时任务触发的时间,以毫秒为单位。
         * 如果第一个参数使用的是 ELAPSED_REALTIME或 ELAPSED_REALTIME_WAKEUP,
         * 则这里传入开机至今的时间再加上延迟执行的时间。如果第一个参数使用的是 RTC 或RTC_WAKEUP,
         * 则这里传入 1970年 1月 1日 0点至今的时间再加上延迟执行的时间。
         * 
         * 第三个参数是一个 PendingIntent,这里我们一般会调用 getBroadcast()方法来获取一个能够执行广播的 PendingIntent。 
         * 这样当定时任务被触发的时候,广播接收器的 onReceive()方法就可以得到执行。
         */
        manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, triggerAtTime, pi);
        return super.onStartCommand(intent, flags, startId);

    }

}

MyServiceActivity:

Button button=(Button) findViewById(R.id.startAlarm);
            button.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    Intent intent=new Intent(MyServiceActivity.this,LongRunningService.class);
                    startService(intent);
                }
            });

AlarmReceiver :

public class AlarmReceiver extends BroadcastReceiver{

    @Override
    public void onReceive(Context context, Intent intent) {
        Intent intent2=new Intent(context,LongRunningService.class);
        context.startService(intent2);
    }

}

onReceive()方法里的代码非常简单,就是构建出了一个 Intent 对象,然后去启动LongRunningService 这个服务。那么这里为什么要这样写呢?其实在不知不觉中,这就已经将一个长期在后台定时运行的服务完成了。因为一旦启动 LongRunningService,就会在onStartCommand()方法里设定一个定时任务,这样一小时后 AlarmReceiver 的 onReceive()方法就将得到执行,然后我们在这里再次启动 LongRunningService,这样就形成了一个永久的循环,保证 LongRunningService 可以每隔一段时间就会启动一次,一个长期在后台定时运行的服务自然也就完成了。

最后别忘了在AndroidManifest.xml注册

运行:
Logcat打印信息:
这里写图片描述

可以看到程序已经成功运行了,每隔3秒钟刷新一次时间。实际运用当中可以把LOG信息换成逻辑即可。

最后来看一个利用服务Service,Handler多线程异步处理机制,HttpURLConnection,写的一个通知栏版本升级的例子:

public class UpdateService extends Service {
    public static final String Install_Apk = "Install_Apk";
    /******** download progress step *********/
    private static final int down_step_custom = 3;

    private static final int TIMEOUT = 10 * 1000;// 超时
    private static String down_url;
    private static final int DOWN_OK = 1;
    private static final int DOWN_ERROR = 0;

    private String app_name;

    private NotificationManager notificationManager;
    private Notification notification;
    private Intent updateIntent;
    private PendingIntent pendingIntent;
    private RemoteViews contentView;

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return null;
    }

    /**
     * 方法描述:onStartCommand方法
     * 
     * @param Intent
     *            intent, int flags, int startId
     * @return int
     * @see UpdateService
     */
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        app_name = intent.getStringExtra("Key_App_Name");
        down_url = intent.getStringExtra("Key_Down_Url");
        //create file,应该在这个地方加一个返回值的判断SD卡是否准备好,文件是否创建成功,等等!
        FileUtil.createFile(app_name);
        if (FileUtil.isCreateFileSucess == true) {
            createNotification();
            createThread();
        } else {
            Toast.makeText(this, R.string.insert_card, Toast.LENGTH_SHORT).show();
            /*************** stop service ************/
            stopSelf();
        }
        return super.onStartCommand(intent, flags, startId);
    }

    /********* update UI ******/
    private final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
            case DOWN_OK:
                /********* 下载完成,点击安装 ***********/
                Uri uri = Uri.fromFile(FileUtil.updateFile);
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(uri, "application/vnd.android.package-archive");
                pendingIntent = PendingIntent.getActivity(UpdateService.this, 0, intent, 0);

                notification.flags = Notification.FLAG_AUTO_CANCEL;
                notification.setLatestEventInfo(UpdateService.this, app_name, getString(R.string.down_sucess),
                        pendingIntent);
                // notification.setLatestEventInfo(UpdateService.this,app_name,
                // app_name + getString(R.string.down_sucess), null);
                notificationManager.notify(R.layout.notification_item, notification);

                /***** 安装APK ******/
                // installApk();

                // stopService(updateIntent);
                /*** stop service *****/
                stopSelf();
                break;

            case DOWN_ERROR:
                notification.flags = Notification.FLAG_AUTO_CANCEL;
                // notification.setLatestEventInfo(UpdateService.this,app_name,
                // getString(R.string.down_fail), pendingIntent);
                notification.setLatestEventInfo(UpdateService.this, app_name, getString(R.string.down_fail), null);
                /*** stop service *****/
                // onDestroy();
                stopSelf();
                break;

            default:
                // stopService(updateIntent);
                /****** Stop service ******/
                // stopService(intentname)
                // stopSelf();
                break;
            }
        }
    };

    private void installApk() {
        // TODO Auto-generated method stub
        /********* 下载完成,点击安装 ***********/
        Uri uri = Uri.fromFile(FileUtil.updateFile);
        Intent intent = new Intent(Intent.ACTION_VIEW);

        /**********
         * 加这个属性是因为使用Context的startActivity方法的话,就需要开启一个新的task
         **********/
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

        intent.setDataAndType(uri, "application/vnd.android.package-archive");
        UpdateService.this.startActivity(intent);
    }

    /**
     * 方法描述:createThread方法, 开线程下载
     * 
     * @param
     * @return
     * @see UpdateService
     */
    public void createThread() {
        new DownLoadThread().start();
    }

    private class DownLoadThread extends Thread {
        @Override
        public void run() {
            // TODO Auto-generated method stub
            Message message = new Message();
            try {
                long downloadSize = downloadUpdateFile(down_url, FileUtil.updateFile.toString());
                if (downloadSize > 0) {
                    // down success
                    message.what = DOWN_OK;
                    handler.sendMessage(message);
                }
            } catch (Exception e) {
                e.printStackTrace();
                message.what = DOWN_ERROR;
                handler.sendMessage(message);
            }
        }
    }

    /**
     * 方法描述:createNotification方法
     * 
     * @param
     * @return
     * @see UpdateService
     */
    public void createNotification() {
        /**
         * 定义一个前台服务
         */
        // notification = new Notification(R.drawable.dot_enable,app_name +
        // getString(R.string.is_downing) ,System.currentTimeMillis());
        notification = new Notification(
                // R.drawable.video_player,//应用的图标
                R.drawable.icon, // 应用的图标
                app_name + getString(R.string.is_downing), System.currentTimeMillis());
        notification.flags = Notification.FLAG_ONGOING_EVENT;
        // notification.flags = Notification.FLAG_AUTO_CANCEL;

        /*** 自定义 Notification 的显示 ****/
        contentView = new RemoteViews(getPackageName(), R.layout.notification_item);
        contentView.setTextViewText(R.id.notificationTitle, app_name + getString(R.string.is_downing));
        contentView.setTextViewText(R.id.notificationPercent, "0%");
        contentView.setProgressBar(R.id.notificationProgress, 100, 0, false);
        notification.contentView = contentView;

        // updateIntent = new Intent(this, AboutActivity.class);
        // updateIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
        // //updateIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        // pendingIntent = PendingIntent.getActivity(this, 0, updateIntent, 0);
        // notification.contentIntent = pendingIntent;

        notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(R.layout.notification_item, notification);
    }

    /***
     * down file
     * 
     * @return
     * @throws MalformedURLException
     */
    public long downloadUpdateFile(String down_url, String file) throws Exception {

        int down_step = down_step_custom;// 提示step
        int totalSize;// 文件总大小
        int downloadCount = 0;// 已经下载好的大小
        int updateCount = 0;// 已经上传的文件大小

        InputStream inputStream;
        OutputStream outputStream;

        URL url = new URL(down_url);
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        httpURLConnection.setConnectTimeout(TIMEOUT);
        httpURLConnection.setReadTimeout(TIMEOUT);
        // 获取下载文件的size
        totalSize = httpURLConnection.getContentLength();

        if (httpURLConnection.getResponseCode() == 404) {
            throw new Exception("fail!");
            // 这个地方应该加一个下载失败的处理,但是,因为我们在外面加了一个try---catch,已经处理了Exception,
            // 所以不用处理
        }

        inputStream = httpURLConnection.getInputStream();
        outputStream = new FileOutputStream(file, false);// 文件存在则覆盖掉

        byte buffer[] = new byte[1024];
        int readsize = 0;

        while ((readsize = inputStream.read(buffer)) != -1) {

            // /*********如果下载过程中出现错误,就弹出错误提示,并且把notificationManager取消*********/
            // if (httpURLConnection.getResponseCode() == 404) {
            // notificationManager.cancel(R.layout.notification_item);
            // throw new Exception("fail!");
            // //这个地方应该加一个下载失败的处理,但是,因为我们在外面加了一个try---catch,已经处理了Exception,
            // //所以不用处理
            // }

            outputStream.write(buffer, 0, readsize);
            downloadCount += readsize;// 时时获取下载到的大小
            /*** 每次增张3% **/
            if (updateCount == 0 || (downloadCount * 100 / totalSize - down_step) >= updateCount) {
                updateCount += down_step;
                // 改变通知栏
                contentView.setTextViewText(R.id.notificationPercent, updateCount + "%");
                contentView.setProgressBar(R.id.notificationProgress, 100, updateCount, false);
                notification.contentView = contentView;
                notificationManager.notify(R.layout.notification_item, notification);
            }
        }
        if (httpURLConnection != null) {
            httpURLConnection.disconnect();
        }
        inputStream.close();
        outputStream.close();

        return downloadCount;
    }
}
public class FileUtil {
    public static File updateDir = null;
    public static File updateFile = null;
    /***********保存升级APK的目录***********/
    public static final String KonkaApplication = "konkaUpdateApplication";

    public static boolean isCreateFileSucess;

    /** 
    * 方法描述:createFile方法
    * @param   String app_name
    * @return 
    * @see FileUtil
    */
    public static void createFile(String app_name) {

        if (android.os.Environment.MEDIA_MOUNTED.equals(android.os.Environment.getExternalStorageState())) {
            isCreateFileSucess = true;

            updateDir = new File(Environment.getExternalStorageDirectory()+ "/" + KonkaApplication +"/");
            updateFile = new File(updateDir + "/" + app_name + ".apk");

            if (!updateDir.exists()) {
                updateDir.mkdirs();
            }
            if (!updateFile.exists()) {
                try {
                    updateFile.createNewFile();
                } catch (IOException e) {
                    isCreateFileSucess = false;
                    e.printStackTrace();
                }
            }

        }else{
            isCreateFileSucess = false;
        }
    }
}

Activity:

// 点击更新
        update.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(context,UpdateService.class);
                intent.putExtra("Key_App_Name",appName);
                intent.putExtra("Key_Down_Url",appUrl);         
                startService(intent);
                popupWindow.dismiss();
            }
        });

以上是关于服务Service的基本用法的主要内容,如果未能解决你的问题,请参考以下文章

服务的用法

Android四大组件——服务以及实例

Service系统服务:rsync基本用法rsync+SSH同步配置rsync服务端访问rsync共享资源使用inotifywait工具配置Web镜像同步配置并验证Split分离解析

dubbo的基本用法

Python3 - k8s创建Service服务实例详解

Python3 - k8s创建Service服务实例详解