服务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的基本用法的主要内容,如果未能解决你的问题,请参考以下文章
Service系统服务:rsync基本用法rsync+SSH同步配置rsync服务端访问rsync共享资源使用inotifywait工具配置Web镜像同步配置并验证Split分离解析