欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

服务Service的基本用法

程序员文章站 2024-03-11 14:03:37
...

作为 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();
    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

启动和停止服务

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);//停止服务
                }
            });
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

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

我们可以看到Logcat打印的信息: 
服务Service的基本用法

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

服务Service的基本用法

服务Service的基本用法

已经学会了启动服务以及停止服务的方法,不知道你心里现在有没有一个疑惑,那就是 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();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59

接下来看下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);//解绑服务点击了就会服务就会停止运行
                }
            });
        }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74

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

服务Service的基本用法

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);
}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

虽说这种写法并不复杂, 但是总会有一些程序员忘记开启线程, 或者忘记调用 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");
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

在MyServiceActivity新增一个按钮分别打印子线程ID以及主线程ID 
服务Service的基本用法

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);
                }
            });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

运行程序:Logcat打印信息: 
服务Service的基本用法

可以看到, 不仅 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);

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

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);
                }
            });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

AlarmReceiver :

public class AlarmReceiver extends BroadcastReceiver{

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

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

最后别忘了在AndroidManifest.xml注册

运行: 
Logcat打印信息: 
服务Service的基本用法

可以看到程序已经成功运行了,每隔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;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
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;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

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();
            }
        });