Android第一行代码--Service(四大组件之一)
Posted 钢铁-程序猿
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android第一行代码--Service(四大组件之一)相关的知识,希望对你有一定的参考价值。
文章目录
android第一行代码-Service
Android支持后台功能,这使得应用程序即使在关闭的情况下仍然可以在后台继续运行。
服务是什么
非常适合执行那些不需要和用户交互而且需要长时间运行的任务。
服务并不是运行在一个独立的进程中,而是依赖于创建服务时所在的应用程序进程。当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止。
服务并不会自动开启线程,所有的代码都是默认运行在主线程中,也就是说,我们需要在服务的内部手动创建子线程,并且在这里执行具体的任务,否则就有可能出现主线程被阻塞的情况。
Android多线程编程
线程的基本用法
- 1、继承Thread类,实现run方法
- 2、实现Runnable接口,实现里面的run方法。
在子线程中更新UI
Android的UI和其他许多GUI库一样,也是线程不安全的。有就是说,如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常。
package com.example.androidthreadtest;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener
private TextView text;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView) findViewById(R.id.text);
Button button = (Button) findViewById(R.id.change_text);
button.setOnClickListener(this);
@Override
public void onClick(View v)
switch (v.getId())
case R.id.change_text:
new Thread(new Runnable()
@Override
public void run()
text.setText("Nice to meet you");
).start();
break;
default:
break;
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
当运行上面的代码时会报错,这是因为在子线程中更新UI导致的。
但是有时候必须要在子线程中执行一些耗时任务,然后根据任务的执行结果来更新相应的UI控件,这该怎么解决呢?
对于这种情况,Android提供了一套异步消息处理机制,完美解决了在子线程中进行UI操作的问题。
通过异步消息处理机制解决在子线程中更新UI导致的报错问题
package com.example.androidthreadtest;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity implements View.OnClickListener
private TextView text;
public static final int UPDATE_TEXT = 1;
private Handler handler = new Handler()
@Override
public void handleMessage(@NonNull Message msg)
switch (msg.what)
case UPDATE_TEXT:
text.setText("Nice to meet you");
break;
default:
break;
;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = (TextView)findViewById(R.id.text);
Button changeText = (Button)findViewById(R.id.change_text);
changeText.setOnClickListener(this);
@Override
public void onClick(View v)
switch (v.getId())
case R.id.change_text:
new Thread(new Runnable()
@Override
public void run()
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message);
).start();
break;
default:
break;
解析异步消息处理机制
Android中的异步消息处理主要分为四个部分:
- 1、Message
- 2、Handler
- 3、MessageQueue
- 4、Looper
1、Message(传递消息)
用于在线程间传递消息,可以携带少量的信息,用于在不同线程之间交换数据。除此之外,Message还有arg1和arg2字段来携带一些整型数据,obj属性来携带一些Object对象。
2、Handler(处理者,用于发送和处理消息,发出的消息最终会传递到Handler的handleMessage方法中)
用于发送和处理消息。发送消息一般是使用sendMessage方法,而发出的消息经过一系列辗转处理后,最终会传递到Handler的handleMessage方法中。
3、MessageQueue(消息队列)
用于存放Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理,每个线程只会有一个MessageQueue对象。
4、Looper(MessageQueue的管家,MessageQueue中的消息取出传递给handleMessage方法中)
调用Looper的loop方法后,就会进入到一个无限循环中,当发现MessageQueue中存在一条消息,就会将它取出,并传递到Handler的handleMessage方法中,每个线程只会有一个Looper对象。
消息处理过程
- 1、创建Handler对象,重写handleMessage方法
- 2、当子线程需要进行UI操作时,创建Message对象,通过Handler将消息传递出去。
- 3、将消息添加到MessageQueue
- 4、Looper不断尝试从MessageQueue取出待处理消息,最后发回Handler的handleMessage方法中。
由于Handler是主线程中创建的,所以此时handleMessage方法的代码也会在主线程中运行,于是就可以安心进行UI操作。
AsyncTask
通过AsyncTask,即使你对异步消息处理机制完全不了解,也可以十分简单从子线程切换到主线程。AsyncTask背后的实现原理也是基于异步消息处理机制的,知识Android帮我们做了很好的封装而已。
使用AsyncTask(抽象类)
如果要使用AsyncTask,就需要创建一个子类去继承它,在继承时,我们就可以为AsyncTask类指定三个泛型参数。
- 1、Params:doInBackground函数的参数类型,在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
- 2、Progress:onProgressUpdate函数的参数类型,后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位
- 3、Result:onPostExecute函数的参数类型,当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
class DownloadTask extends AsyncTask<Void,Integer,Boolean>
- Void表示不需要传入参数给后台任务
- Integer表示使用整型数据作为进度显示单位
- Boolean表示使用Boolean数据来反馈执行结果
需要重写AsyncTask中的几个方法才能完成对任务的定制,经常需要去重写的方法有4个:
- 1、onPreExecute():这个方法在任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框。
- 2、doInBackground(Params…):这个方法中的所有代码都会在子线程中运行,我们需要在这里去处理所有的耗时任务。任务一旦完成就可以通过return语句来将任务的执行结果返回。如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress…)方法来完成。
- 3、onProgressUpdate(Progress…):当在后台任务中调用了publishProgress(Progress…)方法后,onProgressUpdate(Progress…)方法就会很快被调用,该方法中携带的参数就是在后台任务中传递过来的,在这个方法中可以对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新
- 4、onPostExecute(Result):当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中。可以利用返回的数据进行一些UI操作,比如说提醒任务执行的结果以及关闭进度条对话框等。
class DownloadTask extends AsyncTask<Void,Integer,Boolean>
@Override
protected void onPreExecute()
progressDialog.show(); //显示进度对话框
@Override
protected Boolean doInBackground(Void... Params)
try
while (true)
int downloadPercent = doDownload(); //这是一个虚构的方法
publishProgress(downloadPercent); //传入当前的下载进度,这样onProgressUpdate()方法就会很快被调用。
if (downloadPercent >= 100)
break;
catch (Exception e)
return false;
return true;
@Override
protected void onProgressUpdate(Integer... values)
progressDialog.setMessage("Downloaded"+values[0]+"%");
@Override
protected void onPostExecute(Boolean result)
progressDialog.dismiss(); //关闭进度条对话框
if (result)
Toast.makeText(context,"Download succeed",Toast.LENGTH_SHORT).show();
else
Toast.makeText(context,"Download failed",Toast.LENGTH_SHORT).show();
简单来说,使用AsyncTask的诀窍就是在DoInBackground()方法中执行具体的耗时任务,在onProgressUpdate()方法中进行UI操作,在onPostExecute()方法中执行一些任务的收尾工作。
如果想要调动这个任务,只需要编写以下代码即可,new DownloadTask().execute();
服务的基本用法
1、定义一个服务(继承Service类)
通过New–》Service–》Service可以定义一个服务。
在创建时Exported表示是否允许除了当前程序之外的其他程序访问这个服务。Enable表示是否启动这个服务。
既然是定义一个服务,自然应该在服务中去处理一些事情,那处理事情的逻辑应该写在哪里呢?需要重写Service中的一些方法。如下所示:
- onCreate():在服务创建的时候调用
- onStartCommand():在服务启动的时候调用
- onDestroy():在服务销毁的时候调用
每个服务都需要在AndroidManifest.xml中进行注册才能生效,通过Android Studio创建Service会自动在AndroidManifest文件中进行注册。
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
启动和停止服务(主要借助Intent来实现)
完全是由活动决定服务何时停止。
例子:
MyService:
package com.example.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service
public MyService()
@Override
public IBinder onBind(Intent intent)
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
@Override
public void onCreate()
super.onCreate();
Log.d("MyService","onCreate executed");
@Override
public int onStartCommand(Intent intent, int flags, int startId)
Log.d("MyService","onStartCommand executed");
return super.onStartCommand(intent, flags, startId);
@Override
public void onDestroy()
super.onDestroy();
Log.d("MyService","onDestroy executed");
MainActivity:
package com.example.servicetest;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = (Button)findViewById(R.id.start_service);
Button stopService = (Button)findViewById(R.id.stop_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
@Override
public void onClick(View v)
switch (v.getId())
case R.id.start_service:
Intent startIntent = new Intent(this,MyService.class);
//startService()和stopService()方法都是定义在Context类中的,所以我们在活动中可以直接调用这两个方法
startService(startIntent); //启动服务
break;
case R.id.stop_service:
Intent stopIntent = new Intent(this,MyService.class);
stopService(stopIntent); //停止服务
break;
default:
break;
服务在活动中启动的
服务如果启动成功,可以在Settings->Developer options->Running Service中找到它。
onCreate()和onStartCommand()方法的区别:onCreate()是在服务第一次创建的时候调用的,而onStartCommand()方法则在每次启动服务的时候都会调用。
活动和服务进行通信
有没有什么办法可以让活动和服务的关系更紧密呢?比如在活动中指挥服务去干什么,服务就去干什么。
- 借助onBind()方法来实现
活动绑定服务,当一个活动和服务绑定了之后,就可以调用服务里的Binder提供的方法了。
MyService类
package com.example.servicetest;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.IBinder;
import android.util.Log;
public class MyService extends Service
private DownloadBinder mbinder = new DownloadBinder();
class DownloadBinder extends Binder
public void startDownload()
Log.d("MyService","startDownload executed");
public int getProgress()
Log.d("MyService","getProgress executed");
return 0;
public MyService()
@Override
public IBinder onBind(Intent intent)
// TODO: Return the communication channel to the service.
return mbinder;
@Override
public void onCreate()
super.onCreate();
Log.d("MyService","onCreate executed");
@Override
public int onStartCommand(Intent intent, int flags, int startId)
Log.d("MyService","onStartCommand executed");
return super.onStartCommand(intent, flags, startId);
@Override
public void onDestroy()
super.onDestroy();
Log.d("MyService","onDestroy executed");
MainActivity类
package com.example.servicetest;
import androidx.appcompat.app.AppCompatActivity;
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener
private MyService.DownloadBinder downloadBinder;
private ServiceConnection connection = new ServiceConnection()
@Override
public void onServiceConnected(ComponentName name, IBinder service)
downloadBinder = (MyService.DownloadBinder)service;
downloadBinder.startDownload();
downloadBinder.getProgress();
@Override
public void onServiceDisconnected(ComponentName name)
;
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button bindService = (Button)findViewById(R.id.bind_service);
Button unbindService = (Button)findViewById(R.id.unbind_service);
bindService.setOnClickListener(this);
unbindService.setOnClickListener(this);
@Override
public void onClick(View v)
switch (v.getId())
case R.id.bind_service:
Intent bindIntent = new Intent(this,MyService.class);
///将MainActivity和MyService进行绑定
//BIND_AUTO_CREATE表示活动和服务进行绑定后自动创建服务
//这会使得MyService中的onCreate方法得到执行,onStartComma以上是关于Android第一行代码--Service(四大组件之一)的主要内容,如果未能解决你的问题,请参考以下文章