一、服务定义
1、服务(Service)是Android中实现后台运行的解决方案。
2、服务并不是运行在一个独立的进程当中,而是依赖于创建服务时所在的应用程序进程,当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止运行。
3、服务不会自动开启线程,所有的代码都是默认运行在主线程当中,需要在服务的内部手动创建子线程,并在这里执行具体的任务,否则就可能出现主线程被阻塞主的情况。
二、Android多线程编程
《一》线程的基本用法
1、新建线程方法:
《1》、新建一个类继承Thread,然后重写父类的run()方法,并在里面编写耗时逻辑即可。
《2》、新建一个类实现Runnable接口的方式。
《3》、使用匿名类去实现Runnable接口。
new Thread (new Runnable(){
@Override
public void run(){
//处理具体代码
}
}).start();
2、通过继承Thread方式创建的线程,启动线程,new MyThread().start(),这样run()方法中的代码就会在子线程当中运行。通过实现接口的方式启动线程:
MyThread myThread=new MyThread();
new Thread(myThread).start();
《二》、在子线程中更新UI
1、Android的UI也是线程不安全的。想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常。
2、Android提供了一套异步消息处理机制,解决了在子线程中进行UI操作的问题。
3、异步消息处理的使用方法。
《1》、定义一个整形常量 UPDATE_TEXT 表示更新TextView这个动作。新增Handler对象,并重写父类的handleMessage()方法,在这里对具体的Message 进行处理。发现Message的what=UPDATE_TEXT就将TextView显示的内容改成Nice to mrrt you.
《2》、没有在子线程直接进行UI操作,而是在点击事件里创建一个Message对象,将它的what字段的值指定为UPDATE_TEXT,调用Handler的sendMessage()方法将这条Message发送出去。
《3》、Handler就会接收到这条Message,并在handleMessage()对它进行处理。
注意:此时handleMessage()方法中的代码就是在主线程当中运行的。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private final static int UPDATE_TEXT = 1;
private TextView text;
@SuppressLint("HandlerLeak")
private Handler handler = new Handler() {
@SuppressLint("SetTextI18n")
@Override
public void handleMessage(Message msg) {
if (msg.what == UPDATE_TEXT) {
//在这里进行UI处理
text.setText("Nice to meet you");
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
text = findViewById(R.id.text);
Button button = findViewById(R.id.change_text);
button.setOnClickListener(this);
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.change_text) {
new Thread(new Runnable() {
@Override
public void run() {
Message message = new Message();
message.what = UPDATE_TEXT;
handler.sendMessage(message);
}
}).start();
}
}
}
《三》、解析异步消息处理机制
Android中的异步消息处理主要由4个部分组成:Message、Handler、MessageQueue、Looper。
1、Message
Message是线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间交换数据。
2、Handler
处理者,主要用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列的辗转处理后,最终会传递到Handler的handleMessage()方法中。
3、MessageQueue
是消息队列,主要用于存放所有通过Handler发送的消息,这部分消息会一直存放在消息队列中,等代处理,每个线程中只会有一个MessageQueue对象。
4、Looper
一个Looper是每个MessageQueue的管家,调用Looper的loop()方法后,就会进入到一个无县循环当中,然后每当发现MessageQueue中存在一条消息,就会将他取出,并传递到Handler的handleMessage()方法中。每个线程中也只会有一个Looper对像。
5、异步消息处理流程
《1》、首先在主线程中创建一个Handler对象,并重写handleMessage()方法。
《2》、当子线程中需要进行处理UI操作时,就创建一个Message对象,并通过Handler的sendMessage()方法将这条消息发送出去,之后这条消息就会被加到MessageQueue的队列中等待被处理,而Looper则会一直尝试从MessageQueue中取出待处理消息,最后分发回Handler的handleMessage()方法中。
注意:Handler是在主线程中创建的,所以handleMessage()方法中的代码也会在主线程中运行。
《四》、AsyncTask工具
1、AsyncTask也是基于异步消息处理机制的,只是Android帮我们做了封装而已。
2、AsyncTask基本使用:AsyncTask是一个抽象类,所以想使用它就必须要创建一个类去继承它,在继承时可以为AsyncTask类指定3个泛型参数。
《1》、Params:在执行AsyncTask时需要传入的参数,可用于在后台任务中使用
《2》、Progress:后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
《3》、Result:当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
3、重写AsyncTask中的方法完成对任务的定制。经常需要重写的方法有以下4个。
《1》、onPreExecute()
这个方法在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框。
《2》、doInBackground(Params····)
这个方法中的所有代码都会在子线程中运行,在这里处理所有的耗时任务。任务完成就可以通过return将任务的执行结果返回,如果AsyncTask的第三个参数为void,就可以不返回任务执行结果。
注意:在这个方法不可以进行UI操作的,如需要更新UI元素,可以调用publishProgress(Params····)方法来完成。
《3》、onProgressUpdate(Params······)
当后台任务调用publishProgress(Params····)方法后,onProgressUpdate(Params······)就会很快被调用,该方法中携带的参数就是后台任务中传递过来的,在这个方法中对UI进行操作,利用参数中的数值就可以对界面元素进行相应的更新。
《4》、onPostExecute(Result)
当后台任务执行完毕并通过return语句进行返回时,这个方法就会很快被调用。返回的数据作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作。
4、一个比较完整的自定义AsyncTask就可以写成如下:
服务的基本用法
一、定义一个服务
1、新建一个项ServiceTest项目,然后右击com.example.servicetest—>New---->Service---->Service,弹出一个窗口,将服务命名为MyService,Exported表示是否允许除了当前程序之外的其他程序访问这个服务,Enable属性表示是否启用这个服务。勾选两个。
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");
}
}
《1》、MyService是继承自Service类,说明是一个服务。onBind()方法是Service中的唯一的一个抽象方法,所以必须子类去实现。
2、重写Service中的另外一些方法,让其处理事情的逻辑。重写onCreate()、onStartCommand()和onDestroy()这三个方法。
@Override
public void onCreate(){
super.onCreate();
}
@Override
public int onStartCommand(Intent intent,int flags,int startId){
return super.onStartCommand(intent,flags,startId);
}
@Override
public void onDestroy(){
super.onDestroy();
《1》、onCreate()在服务创建的时候调用,onStartCommand()方法在每次服务启动的时候调用,onDestroy()方法在服务销毁的时候调用。
3、每一个服务都需要在AndroidManifest.xml文件中进行注册才能生效。不过Android Studio早已自动帮我们将这一步完成了。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.servicetest">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<service
android:name=".MyService"
android:enabled="true"
android:exported="true"></service>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
二、启动和停止服务
启动和停止服务借助Intent来实现。
1、启动和停止服务,首先修改activity_main.xml中的代码。创建两个按钮用于启动和停止服务。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/start_service"
android:text="Start Service"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/stop_service"
android:text="Stop Service"/>
</LinearLayout>
2、修改MainAcrivity中的代码,分别获取Start Service和Stop Service按钮的实例。并注册点击事件,在Start Service按钮的点击事件创建一个Intent对象,并调用startService()启动MyService服务,关闭服务同理。
注意:这里完全由活动决定服务何时停止的,如果没有点击Stop Service按钮,服务就会一直处于运行状态。
服务让自己停下来的办法:只需要在MyService的任意一个位置调用stopSelf()方法就能让这个服务停止下来。
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = findViewById(R.id.start_service);
Button stopService = 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 intent = new Intent(this, MyService.class);
startService(intent);
break;
case R.id.stop_service:
//停止服务
Intent intent1 = new Intent(this, MyService.class);
stopService(intent1);
break;
default:
break;
}
}
}
3、证实服务已经成功启动或者停止的最简单的方法:就是在MyService的几个方法中加入打印日志。
@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");
}
4、onCreate()方法与onStartCommand()方法区别:onCreate()是在服务第一次创建的时侯加载的,onStartCommand()则是在每次启动服务的时候都会回调。
三、活动和服务进行通信
让活动与服务的关系更加亲密的方法是:onBind()方法。
1、在MyService里提供一个下载功能,然后在活动中可以决定何时开始下载,以及随时查看下载进度。实现这个功能就是需要创建一个专门的Binder对象来对下载功能进行管理,修改MyService代码。
public class MyService extends Service {
private DownloadBinder mBinder = new DownloadBinder();
class DownloadBinder extends Binder {
//定义开始下载的方法
public void startDoewload() {
Log.d("MyService", "startDoewload: extends");
}
//定义查看下载进度的方法
public int getProgress() {
Log.d("MyService", "getProgress: extends");
return 0;
}
}
@Override
public IBinder onBind(Intent intent) {
//返回DownloadBinder实例
return mBinder;
}
public MyService() {
}
@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");
}
}
《1》、创建一个类让他继承自Binder,然后在内部提供开始下载和查看下载进度的方法。
《2》、在MyService中创建DownloadBinder的实例,然后在onBind()方法里返回了这个实例,MyService的工作就全部完成了。
2、在活动中去调用服务里的方法。
《1》、首先在布局文件里新增两个按钮修改activity_main.xml中的代码。这两个按钮用于绑定服务和取消绑定服务的。当一个活动跟服务绑定了之后,就可以调用该服务里的Binder提供的方法了。
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/bind_service"
android:text="@string/bind_service"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/unbind_service"
android:text="@string/unbind_service"/>
《2》、修改MainActivity中的代码。
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.startDoewload();
downloadBinder.getProgress();
Log.d("MainActivity", "onServiceConnected: extends");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.d("MainActivity", "onServiceDisconnected: extends");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button startService = findViewById(R.id.start_service);
Button stopService = findViewById(R.id.stop_service);
Button bindService = findViewById(R.id.bind_service);
Button unbindSevice = findViewById(R.id.unbind_service);
startService.setOnClickListener(this);
stopService.setOnClickListener(this);
bindService.setOnClickListener(this);
unbindSevice.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.start_service:
//开启服务
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
break;
case R.id.stop_service:
//停止服务
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
break;
case R.id.bind_service:
Intent bindIntent = new Intent(this, MyService.class);
//绑定服务
bindService(bindIntent, connection, BIND_AUTO_CREATE);
break;
case R.id.unbind_service:
//解除绑定
unbindService(connection);
default:
break;
}
}
}
(1)、首先创建一个匿名类ServiceConnection,在里面重写了onServiceConnection()和onServiceDisconnection()方法,这俩个方法分别会在活动与服务成功绑定以及解除绑定的时候调用。
(2)、在方法中,通过向下转型得到了DownloadBinder的实例,有了这个实例活动和服务之间的关系就会变得非常紧密了。可以根据具体的场景来调用DownloadBinder中的任何public()方法,即实现了指挥服务干什么服务就去干什么的功能。
(3)、活动与服务进行绑定功能是在按钮的点击事件里完成的。创建一个Intent对象,然后调用bindService()方法将MainActivity和MyService进行绑定。bindService()接收3个参数,第一个参数就是创建的Intent对象,第二个参数就是前面创建的ServiceeConnection的实例,第三个参数就是一个标志位,这里传入BIND_AUTO_CREATE,表示活动和服务进行绑定后自动创建服务。使得MyService中的onCraete()方法得到执行,但onStartCommand()方法不会执行。
(4)、解除活动与服务之间的绑定,调用unbindService()方法就可以,也是按钮的点击事件里实现功能。
3、任何一个服务在整个应用程序范围内都是通用的,即MyService不仅可以和MainActivity绑定,还可以和任何一个其他的活动进行绑定,而且再绑定完成后他们都可以获取到相同的DownloadBinder实例。
四、服务的生命周期
onCreate()、onStartCommand()、onBind()、onDestroy()等方法都是在服务的生命周期内可能回调的方法。
1、服务启动会一直保持运行状态,直到stopService()或者stopSelf()方法被调用。
注意:虽然每调用一次startService(),onStartCommand()就会被执行一次,但实际上每个服务都只会存在一个实例,不管你调用多少次startService()方法,只需要调用一次stopService()或者stopSelf()方法,服务就会停止下来。
2、还可以调用Context的bindService()来获得一个服务的持久连接,这时就会回调服务中的onBind()方法,就会获得onBind()方法里返回的IBinder对象的实例,就能自由的和服务进行通信了。只要调用方和服务之间的连接没有断开,服务就会一直保持运行状态。
3、当调用startService()方法后,又去调用stopService(),这时服务中的onDestroy()方法就会执行,服务已销毁。当调用bindService()方法后,又去调用unbindService(),这时服务中的onDestroy()方法就会执行,服务已销毁。当调用startService()方法后,又去调用bindService(),同时调用stopService()和unbindService()方法,onDestroy()方法才会执行,服务销毁。
4、一个服务只要被启动或绑定了之后,就会一直处于运行状态,必须让以上两种条件同时都不满足,服务才会被销毁。
服务的更多技巧
一、使用前台服务
1、服务的系统优先级比较低,当系统出现内存不足时,就有可能会回收掉正在后台运行的服务
2、前台服务和普通服务的区别:他一直有一个正在运行的图标在系统的状态显示,下拉状态栏后可以看到更加详细的信息,非常类似于通知的效果。可以防止服务被回收。
3、创建前台服务:修改MyService中的代码,通过创建通知的方法,构建Notification对象后,调用startForeground()方法,这个方法接受两个参数,第一个是通知的id,第二个参数是构建出的Notification对象。调用startForeground()方法后就会让MyService变成一个前台服务,并在系统状态栏显示出来。
二、使用IntentService
1、一个比较标准的服务可以写成如下形式:
@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);
}
2、为了可以简单地创建一个异步的、会自动停止的服务,Android专门提供了一个IntentService类。
public class MyIntentService extends IntentService {
public MyIntentService() {
//调用父类的有参构造函数
super("MyIntentService");
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
//打印当前线程的id
Log.d("MyIntentService", "onHandleIntent: Threadv id is"+Thread.currentThread().getId());
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d("MyIntentService", "onDestroy: executed");
}
}
《1》、提供一个无参的构造函数,并且必须在其内部调用父类的有参构造函数。
《2》、子类要实现onHandleIntent()抽象方法,在这个方法中可以处理一些具体的逻辑,这个方法在子线程中运行。
《3》、根据IntentService特性,这个服务在运行结束后应该是自动停止的,所以重写onDestroy()方法,打印日志,证明服务销毁。
3、修改activity_main.xml中的代码,加入一个用于启动MyIntentService这个服务的按钮。
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/start_intent_service"
android:text="Start IntentService"/>
4、修改ManiActivity中的代码,点击事件去启动MyIntentService这个服务,并打印id,跟IntentService做比较。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
······
Button startIntentService=findViewById(R.id.start_intent_service);
startIntentService.setOnClickListener(this);
······
}
@Override
public void onClick(View v) {
switch (v.getId()) {
······
case R.id.start_intent_service:
//开启MyIntentService服务
Log.d("MyIntentService", "onClick: Thread id is"+Thread.currentThread().getId());
Intent intent=new Intent(this,MyIntentService.class);
startService(intent);
break;
default:
break;
}
}
5、注意:采用手动创建的服务要在AndroidManifest.xml里注册,系统不会自动注册。
<service
android:name=".MyIntentService"
android:enabled="true"
android:exported="true"/>