一 服务的基本用法
1.定义服务
在包中新建一个service,命名为MyService。发现MyService类继承自Service。
要在服务中处理一些逻辑,所以重写Service中的一些方法如下:
1 public class MyService extends Service { 2 3 /*在服务创建时被调用*/ 4 @Override 5 public void onCreate() { 6 super.onCreate(); 7 } 8 9 /*在服务每次启动时候调用*/ 10 @Override 11 public int onStartCommand(Intent intent, int flags, int startId) { 12 return super.onStartCommand(intent, flags, startId); 13 } 14 15 /*在服务被销毁时候调用*/ 16 @Override 17 public void onDestroy() { 18 super.onDestroy(); 19 } 20 21 public MyService() { 22 } 23 24 @Override 25 public IBinder onBind(Intent intent) { 26 // TODO: Return the communication channel to the service. 27 throw new UnsupportedOperationException("Not yet implemented"); 28 } 29 }注:服务是四大组件之一,自然也需要被注册,当然我们新建时候已经被自动在AndroidManifest.xml中注册了。
2.启动和停止服务
布局文件如下:
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical"> 5 6 <Button 7 android:id="@+id/start_service" 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" 10 android:text="start service" /> 11 12 <Button 13 android:id="@+id/stop_service" 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" 16 android:text="stop service" /> 17 </LinearLayout>MyService类同上一部分一样,然后MainActivity类如下:
1 public class MainActivity extends AppCompatActivity implements View.OnClickListener { 2 3 private Button startService; 4 private Button stopService; 5 6 @Override 7 protected void onCreate(Bundle savedInstanceState) { 8 super.onCreate(savedInstanceState); 9 setContentView(R.layout.activity_main); 10 11 startService = (Button)findViewById(R.id.start_service); 12 stopService = (Button)findViewById(R.id.stop_service); 13 startService.setOnClickListener(this); 14 stopService.setOnClickListener(this); 15 } 16 17 @Override 18 public void onClick(View view) { 19 switch (view.getId()){ 20 case R.id.start_service: 21 Intent startIntent = new Intent(this,MyService.class); 22 startService(startIntent); //启动服务 23 break; 24 case R.id.stop_service: 25 Intent stopIntent = new Intent(this,MyService.class); 26 stopService(stopIntent); //停止服务 27 break; 28 default: 29 break; 30 } 31 } 32 }可以看到,在点击事件中,我们构建了显性的Intent,目的将其跳转到服务。然后调用startService()和stopService()方法来开始和停止活动。这里如果没有点击停止运行的按键的话,服务会一直执行下去,而如果再MyService中调用stopSelf()方法的话就会停止服务自身。
其中,onCreate()和onStartCommand()方法在 点击startService时都会使用,不同的是,onCreate只在第一次创建的时候使用,再点击的的时候onCreate就不会再执行了。
3.活动和服务进行通信
如果我们想实现一个活动来控制一个下载服务,可以在活动中决定何时开始下载和查看下载进度。
这里要用到之前新建服务时候自带的onBind()方法。更新MyService如下:
1 public class MyService extends Service { 2 3 private DownloadBinder mBinder = new DownloadBinder(); 4 5 class DownloadBinder extends Binder { 6 public void startDownload(){ 7 Log.d("MyService","startDownload executed"); 8 } 9 public int getProgress(){ 10 Log.d("MyService","getProgress executed"); 11 return 0; 12 } 13 } 14 15 ······ 16 @Override 17 public IBinder onBind(Intent intent) { 18 // TODO: Return the communication channel to the service. 19 return mBinder; 20 } 21 }可见,先新建了一个Binder(绑定器)对象来对下载功能进行管理。而在DownloadBinder类中里写了对服务进行控制的功能,即开始下载的逻辑和查看下载进度的逻辑。
然后在onBind()方法里返回了这个实例,即Binder的对象。
然后在布局中添加两个按键:绑定服务和解除绑定。
在MainActivity中修改如下:
1 public class MainActivity extends AppCompatActivity implements View.OnClickListener { 2 3 private Button startService; 4 private Button stopService; 5 private Button bindService; 6 private Button unbindService; 7 private MyService.DownloadBinder downloadBinder; 8 private ServiceConnection connection = new ServiceConnection() { 9 @Override 10 public void onServiceConnected(ComponentName componentName, IBinder service) { 11 /*在活动成功绑定时候执行*/ 12 downloadBinder = (MyService.DownloadBinder) service; 13 downloadBinder.startDownload(); 14 downloadBinder.getProgress(); 15 } 16 17 @Override 18 public void onServiceDisconnected(ComponentName componentName) { 19 /*在活动和服务没有成功绑定时候执行*/ 20 } 21 }; 22 23 protected void onCreate(Bundle savedInstanceState) { 24 super.onCreate(savedInstanceState); 25 setContentView(R.layout.activity_main); 26 27 startService = (Button)findViewById(R.id.start_service); 28 stopService = (Button)findViewById(R.id.stop_service); 29 bindService = (Button)findViewById(R.id.bind_service); 30 unbindService = (Button)findViewById(R.id.unbind_service); 31 startService.setOnClickListener(this); 32 stopService.setOnClickListener(this); 33 bindService.setOnClickListener(this); 34 unbindService.setOnClickListener(this); 35 } 36 37 @Override 38 public void onClick(View view) { 39 switch (view.getId()){ 40 case R.id.start_service: 41 Intent startIntent = new Intent(this,MyService.class); 42 startService(startIntent); //启动服务 43 break; 44 case R.id.stop_service: 45 Intent stopIntent = new Intent(this,MyService.class); 46 stopService(stopIntent); //停止服务 47 break; 48 case R.id.bind_service: 49 Intent bindIntent = new Intent(this,MyService.class); 50 bindService(bindIntent,connection,BIND_AUTO_CREATE);//绑定服务 51 break; 52 case R.id.unbind_service: 53 unbindService(connection); //解绑服务 54 break; 55 default: 56 break; 57 } 58 } 59 }如上可见,我们先创建一个ServiceConnection的匿名类,然后在里面重写了onServiceConnected()方法和onServiceDisconnected()方法用来在服务绑定成功 和 服务断开时候调用。在onServiceConnected()中,通过向下转型得到DownloadBinder的实例。现在就可以执行所有DownloadBinder中所有的方法了。即实现了指挥服务干什么功能。
然而目前活动和服务还是没有绑定,绑定是在绑定按键的点击事件中。绑定中先构建一个Intent对象,调用bindService方法进行绑定,传入三个参数,分别是刚构建的intent对象、ServiceConnection实例、和标示位(BIND_AUTO_CREATE表示绑定后自动创建)。(自动创建后会执行onCreate方法,但不会执行onStartCommand方法)
而解除绑定只需要调用unbindService()方法即可,传入参数ServiceConnection实例。
4.服务的生命周期
当项目调用了context的startService()方法后,相应的服务就会启动,并回调开启服务onStartCommand()方法,如果服务还没被创建,就先执行创建的onCreate()方法。然后服务就会一直处于运行状态,直到调用了stopService()方法或者是stopSelf()方法。因为服务只有一个实例,无论开启多少次,只要调用一次停止方法,服务就会停止了。
同时可以调用bindService()方法来绑定服务,并会回调onBind()方法,如果服务还没被创建,就先执行创建的onCreate()方法。之后调用方可以获取到onBind中返回的所有IBinder实例,就可以自由通信了。当调用解除绑定方法后,服务也会被销毁。
当对一个服务即调用了startService()后,又调用了bindService()方法。必须不满足以上两种条件,服务才会销毁。也就是说要调用stopService()和unBindService()两个方法才会使服务onDestroy()。
5.服务使用技巧
(1)使用前台服务
前台服务相比普通服务,会有一个正在运行的图标在系统的状态栏显示,效果类似于通知。而且不会被系统回收。
1 public class MyService extends Service { 2 ··· 3 /*在服务创建时被调用*/ 4 @Override 5 public void onCreate() { 6 super.onCreate(); 7 Log.d("MyService", "服务已创建"); 8 Intent intent = new Intent(this,MainActivity.class); 9 PendingIntent pi = PendingIntent.getActivity(this,0,intent,0); 10 Notification notification = new NotificationCompat.Builder(this) 11 .setContentTitle("this is content title") 12 .setContentText("this is content text") 13 .setWhen(System.currentTimeMillis()) 14 .setSmallIcon(R.mipmap.ic_launcher) 15 .setLargeIcon(BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher)) 16 .setContentIntent(pi) 17 .build(); 18 startForeground(1,notification); 19 } 20 ··· 21 }可以看出来和通知创建的方法很像,基本相同。只是最后调用startForeground()方法将其显示出来。第一个参数是通知id,第二个参数是notification对象。
(2)使用IntentService
因为在服务中多执行子线程,同时在子线程中多使用stopSelf()来使其工作结束后结束服务。使用IntentService类可以很好的完成以上的两个要求。
首先创建一个类MyIntentService继承自IntentService。如下:
1 public class MyIntentService extends IntentService { 2 3 public MyIntentService() { 4 super("MyIntentService"); //调用父类的有参构造函数 5 } 6 7 @Override 8 protected void onHandleIntent(@Nullable Intent intent) { 9 //打印当前线程的id 来证明处理逻辑部分在子线程中 10 Log.d("MyIntentService","Thread id is "+Thread.currentThread().getId()); 11 } 12 13 @Override 14 public void onDestroy(){ 15 super.onDestroy(); 16 Log.d("MyIntentService","onDestroy executed"); 17 } 18 }类中首先提供了一个无参的构造函数,并在内部调用父类的有参构造函数。在子类中实现onHandleIntent()这个抽象方法,在这个方法中处理具体的逻辑,这个方法就是在子线程中了。因为运行完是自动停止的,我们重写onDestroy()方法来打印一条状态来证明一下。
布局中添加一个开始IntentService的按钮,在MainActivity中修改按钮的点击事件如下:
1 case R.id.start_intent_service: 2 //打印主线程的id 3 Log.d("MainActivity","Thread id is "+Thread.currentThread().getId()); 4 Intent intentService = new Intent(this,MyIntentService.class); 5 startService(intentService); 6 break;可以看到。启动intentService和启动普通服务一样,都是新建一个intent对象,然后使用startService()进行跳转。
二 实例
一 添加依赖
配置build.gradle:
compile'com.squareup.okhttp3:okhttp:3.11.0'
二 下载监听接口
1 public interface DownloadListener { 2 /*回调接口,用来对下载过程状态进行监听和回调*/ 3 void onProgress(int progress); //通知当前下载进度 4 5 void onSuccess(); //通知下载成功事件 6 7 void onFailed(); //通知下载失败事件 8 9 void onPaused(); //通知下载暂停事件 10 11 void onCanceled(); //通知下载取消事件 12 }
三 下载功能DownloadTask类(使用AsyncTask)
新建一个DownloadTask类继承自AsyncTask类如下:
1 public class DownloadTask extends AsyncTask<String, Integer, Integer> { 2 /*传入三个参数 第一个是传一个字符串给后台任务, 3 * 第二个是表示用整数显示进度,第三个是用整数来反馈结果*/ 4 5 /*定义了四种下载状态*/ 6 public static final int TYPE_SUCCESS = 0; 7 public static final int TYPE_FAILED = 1; 8 public static final int TYPE_PAUSED = 2; 9 public static final int TYPE_CANCELED = 3; 10 11 private DownloadListener listener; 12 private boolean isCanceled = false; 13 private boolean isPaused = false; 14 private int lastProgress; 15 16 //在DownloadTask的构造函数中传入刚刚定义的DownloadListener参数。 17 public DownloadTask(DownloadListener listener) { 18 this.listener = listener; 19 } 20 21 @Override 22 /*在后台执行的下载逻辑*/ 23 protected Integer doInBackground(String... params) { 24 InputStream is = null; 25 RandomAccessFile savedFile = null; 26 File file = null; 27 try { 28 long downloadedLength = 0;//记录已下载文件的长度 29 String downloadUrl = params[0]; //参数中获得下载的URL地址 30 String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/")); //根据url解析出下载的文件名 31 String directory = Environment.getExternalStoragePublicDirectory 32 (Environment.DIRECTORY_DOWNLOADS).getPath(); //指定将文件下载到Download目录下 33 34 /*判断Download中是否有要下载的文件,如果有读取已经下载的字节数 35 * 可以在后面启动断点续传功能*/ 36 file = new File(directory + fileName); 37 if (file.exists()) { 38 downloadedLength = file.length(); 39 } 40 /*获取待下载文件的长度,如果为0则文件有误 下载失败,如果等于已下载文件长度则说明下载完成*/ 41 long contentLength = getContentLength(downloadUrl); //待下载文件的总长度 42 if (contentLength == 0) { 43 return TYPE_FAILED; 44 } else if (contentLength == downloadedLength) { 45 return TYPE_SUCCESS; 46 } 47 48 /*发送一条网络请求*/ 49 OkHttpClient client = new OkHttpClient(); 50 //断点下载,指定从那个字节开始下载 51 Request request = new Request.Builder() 52 .addHeader("RANGE", "bytes=" + downloadedLength + "-") //断点下载 53 .url(downloadUrl) 54 .build(); 55 56 Response response = client.newCall(request).execute(); 57 if (response != null) { 58 is = response.body().byteStream(); 59 savedFile = new RandomAccessFile(file, "rw"); 60 savedFile.seek(downloadedLength);//跳过已经下载的字节 61 byte[] b = new byte[1024]; 62 int total = 0; //本次已下载的总长度 63 int len; //要下载的长度 64 /*当还有下载任务时就一直执行,在下载过程中不停判断是否有触发取消和暂停操作*/ 65 while ((len = is.read(b)) != -1) { 66 if (isCanceled) { 67 return TYPE_CANCELED; //如果下载过程中取消 68 } else if (isPaused) { 69 return TYPE_PAUSED; //如果下载过程中暂停 70 } else { 71 total += len; 72 savedFile.write(b, 0, len); 73 //计算已经下载的百分比 74 int progress = (int) ((total + downloadedLength) * 100 / contentLength); 75 publishProgress(progress); //传入下载百分比 76 } 77 } 78 } 79 } catch (Exception e) { 80 e.printStackTrace(); 81 } finally { 82 /*清空下载过程中产生的数据和文件*/ 83 try { 84 if (is != null) { 85 is.close(); 86 } 87 if (savedFile != null) { 88 savedFile.close(); 89 } 90 if (isCanceled && file != null) { 91 file.delete(); 92 } 93 } catch (Exception e) { 94 e.printStackTrace(); 95 } 96 } 97 return TYPE_FAILED; //如果在之前没有返回下载完成和其他情况,就是下载失败了。 98 } 99 100 @Override 101 /*在界面上更新下载进度*/ 102 protected void onProgressUpdate(Integer... values) { 103 int progress = values[0]; 104 if (progress > lastProgress) { 105 listener.onProgress(progress); 106 lastProgress = progress; 107 } 108 } 109 110 @Override 111 /*通知最终的下载结果*/ 112 protected void onPostExecute(Integer status) { 113 switch (status) { 114 case TYPE_SUCCESS: 115 listener.onSuccess(); 116 break; 117 case TYPE_FAILED: 118 listener.onFailed(); 119 break; 120 case TYPE_PAUSED: 121 listener.onPaused(); 122 break; 123 case TYPE_CANCELED: 124 listener.onCanceled(); 125 break; 126 default: 127 break; 128 } 129 } 130 131 public void pauseDownload() { 132 isPaused = true; 133 } 134 135 public void cancelDownload() { 136 isCanceled = true; 137 } 138 139 /*得到下载任务总长度方法*/ 140 private long getContentLength(String downloadUrl) throws IOException { 141 OkHttpClient client = new OkHttpClient(); 142 Request request = new Request.Builder() 143 .url(downloadUrl) 144 .build(); 145 Response response = client.newCall(request).execute(); 146 if (response != null && response.isSuccessful()) { 147 long contentLength = response.body().contentLength(); 148 response.body().close(); 149 return contentLength; 150 } 151 return 0; 152 } 153 }
四 下载服务DownloadService
1 public class DownloadService extends Service { 2 3 private DownloadTask downloadTask; 4 private String downloadUrl; 5 6 /*创造一个DownloadListener匿名类实例,在匿名类中实现具体的五个方法*/ 7 private DownloadListener listener = new DownloadListener() { 8 @Override 9 public void onProgress(int progress) { 10 /*使用getNotification构造一个显示下载进度的通知 11 * 使用NotificationManager的notify方法去触发这个通知*/ 12 getNotificationManager().notify(1, getNotification("Downloading...", progress)); 13 } 14 15 @Override 16 public void onSuccess() { 17 downloadTask = null; 18 //下载成功时将前台服务通知关闭,并创建一个下载成功的通知 19 stopForeground(true); //关闭前台服务 20 getNotificationManager().notify(1, getNotification("Download success", -1)); 21 Toast.makeText(DownloadService.this, "Download Success", Toast.LENGTH_LONG).show(); 22 } 23 24 @Override 25 public void onFailed() { 26 downloadTask = null; 27 //下载失败将前台通知关闭,并创建一个下载失败的通知 28 stopForeground(true); 29 getNotificationManager().notify(1, getNotification("Download failed", -1)); 30 Toast.makeText(DownloadService.this, "Download Failed", Toast.LENGTH_LONG).show(); 31 } 32 33 @Override 34 public void onPaused() { 35 downloadTask = null; 36 Toast.makeText(DownloadService.this, "Download Paused", Toast.LENGTH_LONG).show(); 37 } 38 39 @Override 40 public void onCanceled() { 41 downloadTask = null; 42 stopForeground(true); 43 Toast.makeText(DownloadService.this, "Canceled", Toast.LENGTH_LONG).show(); 44 } 45 }; 46 47 /*为了服务和活动可以通信,创建一个DownloadBinder*/ 48 private DownloadBinder mBinder = new DownloadBinder(); 49 50 @Override 51 public IBinder onBind(Intent intent) { 52 // TODO: Return the communication channel to the service. 53 return mBinder; 54 } 55 /*DownloadBinder中提供了开始,暂停 取消三种方法*/ 56 class DownloadBinder extends Binder { 57 public void startDownload(String url) { 58 if (downloadTask == null) { 59 downloadUrl = url; 60 downloadTask = new DownloadTask(listener); 61 downloadTask.execute(downloadUrl); 62 /*设置为前台服务*/ 63 startForeground(1, getNotification("Downloading...", 0)); 64 Toast.makeText(DownloadService.this, 65 "Downloading,,,", Toast.LENGTH_LONG).show(); 66 } 67 } 68 69 public void pauseDownload() { 70 if (downloadTask != null) { 71 downloadTask.pauseDownload(); 72 } 73 } 74 75 public void cancelDownload() { 76 if (downloadTask != null) { 77 downloadTask.cancelDownload(); 78 } 79 if (downloadUrl != null) { 80 //取消下载要删除文件,关闭通知 81 String fileName = downloadUrl.substring(downloadUrl.lastIndexOf("/"));//下载文件名 82 String directory = Environment.getExternalStoragePublicDirectory 83 (Environment.DIRECTORY_DOWNLOADS).getPath(); //下载地址 84 File file = new File(directory + fileName);//下载文件的目录 85 if(file.exists()){ 86 file.delete(); 87 } 88 getNotificationManager().cancel(1); //关闭通知 89 stopForeground(true); //取消前台通知 90 Toast.makeText(DownloadService.this,"Canceled", 91 Toast.LENGTH_LONG).show(); 92 } 93 } 94 } 95 96 /*返回通知管理器的方法*/ 97 private NotificationManager getNotificationManager() { 98 return (NotificationManager) getSystemService(NOTIFICATION_SERVICE); 99 } 100 101 /*构造一个显示下载进度的通知*/ 102 private Notification getNotification(String title, int progress) { 103 /*点击跳转intent*/ 104 Intent intent = new Intent(this, MainActivity.class); 105 PendingIntent pi = PendingIntent.getActivity(this, 0, intent, 0); 106 NotificationCompat.Builder builder = new NotificationCompat.Builder(this); 107 builder.setSmallIcon(R.mipmap.ic_launcher); 108 builder.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher)); 109 builder.setContentIntent(pi); 110 builder.setContentTitle(title); 111 if (progress >= 0) { 112 //当progress大于或等于0才显示下载进度 113 builder.setContentText(progress + "%"); 114 builder.setProgress(100, progress, false); 115 } 116 return builder.build(); 117 } 118 }服务类主要可以分成三部分,
DownloadListener匿名类实例,在匿名类中实现具体的五个方法。为了服务和活动绑定,创建的DownloadBinder和其实力,类中包含三种具体操作方法。通知管理器和创建通知的getNotification()部分
五 布局文件
布局中包含三个按键,开始、暂停和取消。
1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="match_parent" 3 android:layout_height="match_parent" 4 android:orientation="vertical"> 5 6 <Button 7 android:id="@+id/start_download" 8 android:layout_width="match_parent" 9 android:layout_height="wrap_content" 10 android:text="start_download" /> 11 12 <Button 13 android:id="@+id/pause_download" 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" 16 android:text="pause download" /> 17 18 <Button 19 android:id="@+id/cancel_download" 20 android:layout_width="match_parent" 21 android:layout_height="wrap_content" 22 android:text="cancel download" /> 23 24 </LinearLayout>
六 MainActivity代码
1 public class MainActivity extends AppCompatActivity implements View.OnClickListener { 2 3 private DownloadService.DownloadBinder downloadBinder; 4 5 private ServiceConnection connection = new ServiceConnection() { 6 @Override 7 public void onServiceConnected(ComponentName componentName, IBinder service) { 8 //服务被绑定时的操作 获取了DownloadBinder的实例,有了实例就可以在活动中调用服务提供的方法。 9 downloadBinder = (DownloadService.DownloadBinder) service; 10 } 11 12 @Override 13 public void onServiceDisconnected(ComponentName componentName) { 14 //服务解除绑定时的操作 15 } 16 }; 17 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.activity_main); 21 Button startDownload = (Button) findViewById(R.id.start_download); 22 Button pauseDownload = (Button) findViewById(R.id.pause_download); 23 Button cancelDowmload = (Button) findViewById(R.id.cancel_download); 24 startDownload.setOnClickListener(this); 25 pauseDownload.setOnClickListener(this); 26 cancelDowmload.setOnClickListener(this); 27 28 /*启动服务并绑定服务 29 * 启动服务可以保证DownloadService一直在后台运行*/ 30 Intent intent = new Intent(this, DownloadService.class); 31 startService(intent); 32 bindService(intent, connection, BIND_AUTO_CREATE); 33 /*sd卡读写权限申请判断*/ 34 if(ContextCompat.checkSelfPermission(MainActivity.this, 35 Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){ 36 ActivityCompat.requestPermissions(MainActivity.this, 37 new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1); 38 } 39 } 40 41 @Override 42 public void onClick(View view) { 43 if(downloadBinder==null){ 44 return; 45 } 46 switch (view.getId()) { 47 case R.id.start_download: 48 String url = "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=" + 49 "1535868543461&di=9d49c6cd4d161e0f4d09795fe6b5ac37&imgtype=0&src=" + 50 "http://photocdn.sohu.com%" + 51 "2F20151013/mp35444706_1444730447601_1_th.jpeg"; 52 downloadBinder.startDownload(url); 53 break; 54 case R.id.pause_download: 55 downloadBinder.pauseDownload(); 56 break; 57 case R.id.cancel_download: 58 downloadBinder.cancelDownload(); 59 break; 60 default: 61 break; 62 } 63 } 64 65 //requestPermissions的回调函数,用来判断权限 66 @Override 67 public void onRequestPermissionsResult(int requesCode,String[] permissions,int[] grantResults){ 68 switch (requesCode){ 69 case 1: 70 if (grantResults.length > 0 && grantResults[0] != PackageManager.PERMISSION_GRANTED) { 71 Toast.makeText(this,"拒绝授权无法使用程序",Toast.LENGTH_LONG) 72 .show(); 73 finish(); 74 } 75 break; 76 default: 77 break; 78 } 79 } 80 81 /*活动销毁的时候要解除服务的绑定*/ 82 @Override 83 protected void onDestroy(){ 84 super.onDestroy(); 85 unbindService(connection); 86 } 87 }
七 权限声明
程序中使用到了网络和SD卡存储读取的权限,所以声明相关权限。
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
转载于:https://www.cnblogs.com/Mask-D/p/9572117.html