Android 学习笔记 - 《第一行代码 Android 第二版》 第10章 后台默默的劳动者——探究服务
本章主要学习了
- 动态申请权限
- 服务的用法(前台服务、后台服务)
- Android中多线程的应用 (AsyncTask 类)
- 最后通过一个综合例子实现下载功能。基本搞懂这个例子本章就学完了。
先介绍下几个主要的类
/**
* 主活动(UI线程)工作如下:
* 1、初始化 UI 界面,绑定按钮事件。
* 2、实例化 ServiceConnection 通过他的 onServiceConnected 回调函数获取 service 的控制权
* 3、动态申请权限
*/
public class MainActivity extends AppCompatActivity implements View.OnClickListener{...}
/**
* 下载服务(注意服务还是在主线程,DownloadTask中的 doInBackground 才在子线程里跑)
*/
public class DownloadService extends Service {
/**
* onBind(Intent) 会将此内部类的实例传给活动 MainActivity
* 此类中定义了 【开始下载】【暂停下载】【取消下载】三个方法
* 这样在活动中就可以通过不同的按钮控制服务干活了。
*/
class DownloadBinder extends Binder {...}
}
/**
* 下载任务类,继承了异步任务 AsyncTask
* 真正下载这干活的代码都在这里面
*/
public class DownloadTask extends AsyncTask<String, Integer, Integer> {
public DownloadTask(DownloadListener listener) {
this.listener = listener;
}
}
/**
* DownloadService 中匿名实现 DownloadListener,通过构造函数传给 DownloadTask
* 这样当下载进行到哪一步,就可以调用相应的方法进行处理了
* 比如进行(移除事务、停止服务、推送通知、显示提示)等操作
*/
public interface DownloadListener {
void onProgress(int progress);
void onSuccess();
void onFailed();
void onPaused();
void onCanceled();
}
说了半天还是上张图直观点:
知识点
1、动态申请权限 (MainActivity 中)
/**
* 1、初始化UI界面
* 2、绑定按钮事件
* 3、启动服务 + 绑定服务
* 4、动态申请权限
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...省略部分代码...
// ----------- 动态申请权限 -----------
// 如果有权限则返回PackageManager.PERMISSION_GRANTED,否则返回PackageManager.PERMISSION_DENIED
int permissionCheck = ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE);
// 判断如果没有授权,则动态申请
if ( permissionCheck != PackageManager.PERMISSION_GRANTED) {
// 申请获取权限,弹出确认框等用户操作,然后会回调 onRequestPermissionsResult
// 参数1:目标活动,MainActivity 弹框询问自然就传 MainActivity
// 参数2:要申请的权限,放到数组里(就是说可以同时申请多个权限啦)
// 参数3:请求码,就是暗号,不然回调怎么知道是哪个请求的结果返回来了(自己定,别和其他请求重复就行)
ActivityCompat.requestPermissions(MainActivity.this, new String[]{ Manifest.permission. WRITE_EXTERNAL_STORAGE }, 1);
}
}
/**
* 用户选择授权与否,会回调这里
* @param requestCode 这就是上面那个“请求码”
* @param permissions 权限数组
* @param grantResults 结果数组
*/
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, 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:
}
}
2、主线程与子线程通信
在子线程里跑的貌似只有 DownloadTask.doInBackground(String… params)
在服务 DownloadService 中实现接口 DownloadListener 并实例化,当实例化 DownloadTask 时作为构造参数传进去。这样 DownloadTask 里的子线程就能利用 DownloadListener 实例与主线程通信了。(在很多地方安卓都是这么干的)
我们来捋一捋。
主活动类 MainActivity 中主要做了什么:
1、初始化UI界面
2、绑定按钮事件
3、启动服务 + 绑定服务
4、动态申请权限
5、实现 ServiceConnection 接口,MainActivity 能通过它拿到 DownloadService.onBind 传过来的 IBinder 进而调用服务中的方法。(按钮点击就是调用的它的方法。当然需要执行什么功能,定义什么方法,是我们按需求来定的)
6、活动销毁时解绑服务 unbindService(connection);
服务类 DownloadService 中主要做了什么:
1、匿名实现 DownloadListener 接口,用于处理下载任务的五种状态。 按具体情况执行(移除事务、停止服务、推送通知、显示提示)
2、定义内部类 class DownloadBinder extends Binder 通过 onBind 将实例传给 MainActivity
3、DownloadBinder 的【开始下载】startDownload(String url) 中 new DownloadTask(listener);
异步事务 DownloadTask 中主要做了什么:
1、下载前,做点准备,创建进度条啥的
2、开始下载,下载的过程中要判断下载状态。DownloadTask提供了两个方法用来设置状态,UI上的按钮可以调动。
3、下载过程中,更新进度。
4、下载完成,收尾。
/**
* 这个方法是在执行异步任务之前的时候执行,并且是在UI Thread当中执行的。
* 通常我们在这个方法里做一些UI控件的初始化的操作,例如弹出要给ProgressDialog
*/
@Override
protected void onPreExecute(){...}
/**
* 在onPreExecute()方法执行完之后,会马上执行这个方法,这个方法就是来处理异步任务的方法,
* Android操作系统会在后台的线程池当中开启一个worker thread来执行我们的这个方法,
* 所以这个方法是在worker thread当中执行的,
* 这个方法执行完之后就可以将我们的执行结果发送给我们的最后一个 onPostExecute 方法,
* 在这个方法里,我们可以从网络当中获取数据等一些耗时的操作
* @param params
* @return
*/
@Override
protected Integer doInBackground(String... params) {...}
/**
* 这个方法也是在UI Thread当中执行的,我们在异步任务执行的时候,
* 有时候需要将执行的进度返回给我们的UI界面,
* 例如下载一张网络图片,我们需要时刻显示其下载的进度,就可以使用这个方法来更新我们的进度。
* 这个方法在调用之前,我们需要在 doInBackground 方法中调用 publishProgress(Progress) 方法
* 来将我们的进度时时刻刻传递给 onProgressUpdate 方法来更新
* @param values
*/
@Override
protected void onProgressUpdate(Integer... values) {...}
/**
* 当我们的异步任务执行完之后,就会将结果返回给这个方法,这个方法也是在UI Thread当中调用的。
* 我们可以将返回的结果显示在UI控件上
* @param status
*/
@Override
protected void onPostExecute(Integer status) {...}