从广播一下跳到服务这章。。。已然没什么耐心了~~~
好吧。。。继续
啥是服务?
在Android系统中,服务是实现应用程序后台运行的解决方案。
1、服务依赖于创建它的应用程序,当这个应用程序被杀死时,所有依赖于该进程的服务也会停止运行。
2、服务的代码默认运行在主线程中,因此需要在服务的内部手动创建子线程,并在子线程中执行具体的任务,避免阻塞主线程。
一、创建线程的方式
方式1、新建一个类,继承Thread,重写父类的run()方法,在run()方法执行代码逻辑。
class MyThread extents Thread{
@Override
pubilc void run(){
// 处理具体的逻辑
}
}
启动线程:new出MyThread的实例,然后调用它的start()方法。这样run()方法中的代码就会在子线程中运行了。
new MyThread().start();
使用继承的方式,耦合性高,所以更多时候选择使用实现Runnable接口的方式来定义一个线程
啥是耦合性?
这个解释起来确实很麻烦,网上也有一堆的专业解释
这边我也引入网上举的例子来说明一下:(忘记哪个网站说的了)
有一百人分成10个团队做开发
你写了一个类A,供其他人调用,怎么办?
简单的方法就是把这个类打成jar包,然后给他们
他们就A a = new A();
然后调用a的方法。但是有一天,A类升级了,怎么办?
再打jar包,再给其他9个组每个组发一份,告诉他们,替换一下以前的jar包。
有可能你的a中,方法签名还发生了变化,那么他们就得重新改代码来适应你新的jar包了。
如果这样的事频繁发生呢,那么你就等着挨骂吧。
这就是紧耦合,他们的程序紧密地耦合着你的程序。如果是松耦合的话,我想我可能会定义一个接口给他们,然后IOC的方式将实现类给他们,
最好是远程的通过webservice的方式进行调用,这样我的A更新了,只需要切换掉远程的A的实现类,他们根本啥也不知道,啥也不用改,就更新了功能,怎么样,是不是很方便?这就是松耦合
方式2:使用Runnable接口方式
class MyThread implements Runnable{
@Override
pubilc void run(){
// 处理具体的逻辑
}
}
启动线程:
new Thread(new MyThread()).start();
方式3:使用匿名类的方式
new Thread(new Runnable(){
@Override
pubilc void run(){
// 处理具体的逻辑
}
}).start();
二、异步消息处理机制 – Handler的基本用法
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final int CHNAGE_TEXT = 1;
private int i = 0;
private TextView tvShowText;
private Button btnChangeText;
private Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case CHNAGE_TEXT:
tvShowText.setText("you click button: "+msg.arg1+" times");
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvShowText = (TextView)findViewById(R.id.tvShowText);
btnChangeText = (Button)findViewById(R.id.btnChangeText);
btnChangeText.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.btnChangeText:
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = CHNAGE_TEXT;
message.arg1 = ++i;
handler.sendMessage(message);
}
}).start();
break;
default:
break;
}
}
}
上述的示例中,当点击按钮时创建并启动了一个线程。如果在线程的run()方法中直接更新text view(tvShowText.setText(“you click button!”);),则会报错。
因为Android不允许在子线程中进行UI更新操作。
所以使用的handle方式。
三、解析异步消息处理机制
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue、和Looper.
1、Message
Message是在线程之间传递的消息。它可以在内部携带少量的信息,用于在不同的线程之间交换数据。使用what字段、arg1和arg2字段来携带整型数据;使用obj字段携带一个object对象。
2、Handler
Handler主要用于发送和处理消息的。发送消息一个使用Handler的sendMessage()方法。发出的消息最终会传递到Handler的handleMessage()方法中。
3、MessageQueue
MessageQueue是消息队列,主要用于存放所有通过Handler发送的消息。每个线程中只会有一个MessageQueue对象。
4、Looper
Looper是每个线程中的MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无限循环中。当发现MessageQueue中存放一条消息,就会将它取出,并传送到Handler的handleMessage()方法中。每个线程也只会有一个Looper对象。
对上述示例解析:
首先在主线程中创建一个Handler对象,并重写handleMessage()方法;然后当子线程中需要进行UI操作时,就创建一个Message对象,并通过Handler将消息发送出去。之后这条消息就会被添加到MessageQueue的队列中等待被处理,而Looper会一直尝试从MessageQueue中取出待处理的消息,最后分发回Handler的handleMessage()方法中。
四、AsyncTask的基本用法
AsyncTask背后的实现原理也是基于异步消息处理机制的,只是做了封装而已。
AsyncTask是一个抽象类,通过创建一个子类去继承它来使用。在继承时,可以为AsyncTask类指定3个泛型参数:
1、Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
2、Progress:后台执行任务时,如果需要在界面上显示当前的进度,则可以使用这里指定的泛型作为进度单位。
3、Result:当任务完成后,如果需要对结果进行返回,则可以使用这里指定的泛型作为返回值类型。
一个最简单的自定义AsyncTask如下:
class DownloadTask extents AsyncTask<Void, Integer, Boolean>{
}
这第一个泛型参数指定为Void,表示在执行AsyncTask时不需要传入参数给后台任务。
第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位。
第三个泛型参数指定为Boolean,表示使用布尔型数据来反馈执行结果。
重写AsyncTask中的几个方法
1、onPreExecute()
这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作。
2、doInBackground(Params…)
这个方法中的所有代码都会在子线程中运行,需要在这个方法中处理耗时任务,任务完成后可以通过return语句来将任务的执行结果返回。
如果AsyncTask的第三个泛型参数指定的是Void,则不会返回任务的执行结果。
当需要更新UI元素时,可以调用publishProgress(Progress…)方法来完成,这样就能够从子线程切换到主线程了。
3、onProgressUpdate(Progress…)
当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就会被调用。
该方法中携带的参数就是从后台任务中传递过来的,利用这个参数中的数值就可以更新UI了
4、onPostExecute(Result)
当后台任务执行完毕,并通过return返回时,这个方法就会被调用。
返回的数据可作为参数传递到这个方法中,可以利用返回的数据进行UI操作。
因此,一个完整的AsyncTask就可以写成如下方式:
class DownloadTask extents AsyncTask<Void, Integer, Boolean>{
@Override
protected void OnPreExecute(){
}
@Override
protected Boolean doInBackground(Void...params){
return true;
}
@Override
protected void onProgressUpdate(Integer...values){
}
@Override
protected void onPostExecute(Boolean result){
}
}
简单来说,使用AsyncTask的诀窍是:
在doInBackground()方法中执行耗时任务,
在onProgressUpdate()方法中更新UI,
在onPostExecute()方法中执行任务的收尾工作。
启动任务:
new DownloadTask().execute();