Android四大组件详解--Service篇
Android服务是一个后台运行的组件,执行长时间运行且不需要用户交互的任务。即使应用被销毁也依然可以工作。
一:Service基本用法
1.StartService开启服务
新建一个TestService继承Service,并重写父类的onCreate(),onStartCommand(),onDestroy()的方法
public class TestService extends Service {
public static final String TAG="TestService";
/**
* 继承一个Service必然要实现的onBind方法*/
@Nullable
@Override public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind()");
return null; }
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy()");
}
}
在AndroidManifest中配置Service
点击执行Service
four_teen_btn=findViewById(R.id.four_teen_btn);
four_teen_btn_service=findViewById(R.id.four_teen_btn_service);
four_teen_btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(FourTeenActivity.this,TestService.class);
startService(intent);//开启服务
}
});
four_teen_btn_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(FourTeenActivity.this,TestService.class);
stopService(intent);//停止服务
}
});
结果:
D/TestService: onCreate()
D/TestService: onStartCommand()
startService开启服务后会走onCreate()和onStartCommand()方法
多次点击只会执行onStartCommand(),onCreate()只会调用一次
D/TestService: onStartCommand()
调用stopService方法关闭服务
D/TestService: onDestroy()
context.startService()开启服务流程:
context.startService()-->onCreate()-->onStartCommand()--->context.stopService()-->onDestroy()-->Service Stop
2.bindService绑定服务
观察发现上面发现Service和Activity之间关联不大,发现上面一直有一个onBind()方法没有用到
故Activity和Service通信,可以通过绑定一个Service
应用组件可以调用bindService()绑定一个service,Android系统之后调用service的onBind()方法,它返回一个用来与service交互的IBinder
绑定是异步的,bindService()会立即返回,它不会返回IBinder给客户端,要接收IBinder,客户端必须创建一个ServiceConnection的实例并传给bindService()ServiceConnection包含一个回调方法,系统调用这个方法来传递要返回的IBinder.
public class TestService extends Service {
public static final String TAG="TestService";
private MyBinder myBinder=new MyBinder();
/**
* 继承一个Service必然要实现的onBind方法*/
@Nullable
@Override public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind()");
return myBinder;//返回IBinder接口
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG,"onCreate()");
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"onStartCommand()");
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
Log.d(TAG,"onDestroy()");
}
class MyBinder extends Binder{//Binder实现了IBinder接口了
public void startDownload(){
Log.d(TAG,"startDownload()");
//执行具体的任务
}
}
}
four_teen_btn_bind_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent bindIntent = new Intent(FourTeenActivity.this, TestService.class);
bindService(bindIntent, connection, BIND_AUTO_CREATE);
}
});
four_teen_btn_unbind_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent bindIntent = new Intent(FourTeenActivity.this, TestService.class);
unbindService(connection);
}
});
}
ServiceConnection connection=new ServiceConnection() {
//这个方法系统调用这个来传送在Service的onBind()中返回的IBinder
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
//拿到后台服务的代理对象
TestService.MyBinder myBinder= (TestService.MyBinder) service;
//调用后台服务的方法
myBinder.startDownload();
}
//Android系统在同service连接意外丢失调用这个
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
通过调用bindService传递给他ServiceConnection的实现
bindService(bindIntent, connection, BIND_AUTO_CREATE);
第一个参数明确指定要绑定的ServiceIntent
第二个参数是ServiceConnection对象
第三个参数是一个标志,表明绑定中的操作,它一般是BIND_AUTO_CREATE
结果:
点击bindService后
Service走了onBind方法
D/TestService: onCreate()
D/TestService: onBind()
调用Binder类自定义的方法
D/TestService: startDownload()
点击unbindService
D/TestService: onUnbind()
D/TestService: onDestroy()
通过bindService绑定服务
bindService()-->onCreate()-->onBind()-->unBindService()-->onUnbind()-->onDestory()
3.Service和线程Thread的区别
Thread:是程序执行的最小单元,可以用线程来执行一些异步操作。
Service:是android的一种机制,当它运行的时候如果是Local Service,那么对应的Service是运行在主线程main线程上;如果是Remote Service .那么对应的Service则运行在独立进程的main线程上。
//在Activity中
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_four_teen);
Log.d(TAG,"onCreate()");
Log.d("TestService","FourTeenActivity thread is"+Thread.currentThread().getId());
}
结果:
D/FourTeenActivity: onCreate()
D/TestService: FourTeenActivity thread is2
//在Service中
public void onCreate() {
super.onCreate();
Log.d(TAG,"onCreate()");
Log.d("TestService","MyService thread is"+Thread.currentThread().getId());
}
结果:
D/TestService: onCreate()
D/TestService: MyService thread is2
发现2个线程的id相同,说明service运行在主线程main中
故:不要把后台服务和子线程联系在一起,service不能做耗时操作,不然会导致ANR,要做耗时操作需要在服务中创建一个子线程。
4.前台服务
前台服务是那些被认为用户知道且在系统内存不足的时候不允许系统杀死的服务。前台服务必须给状态栏提供一个通知。
最常见的表现形式就是音乐播放服务,应用程序后台进行时,用户可以通过通知栏,知道当前播放内容,并进行暂停,继续,切歌等相关操作。
创建一个前台服务Service
public class ForegroundService extends Service {
private static final String TAG = "ForegroundService";
private static final int NOTIFICATION_ID=10;
@Nullable
@Override public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate()");
//获取服务通知
Notification notification=createForegroundNotification();
//将服务置于启动状态,NOTIFICATION_ID指的是创建的通知的ID
startForeground(NOTIFICATION_ID,notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand()");
// 数据获取
String data = intent.getStringExtra("Foreground");
Toast.makeText(this, data, Toast.LENGTH_SHORT).show();
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
stopForeground(true);
super.onDestroy();
Log.d(TAG, "onDestroy()");
}
/**
* 创建服务通知
*/
private Notification createForegroundNotification() {
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//唯一的通知通道的id
String notificationChannelId="notification_channel_id_01";
//Android8.0以上的系统,新建消息通道
if (Build.VERSION.SDK_INT>=Build.VERSION_CODES.O){
//用户可见的通道名称
String channelName="Foreground Service Notification";
//通道的重要程度
int importance=NotificationManager.IMPORTANCE_HIGH;
NotificationChannel notificationChannel=new NotificationChannel(notificationChannelId,channelName,importance);
notificationChannel.setDescription("Channel description");
//LED灯
notificationChannel.enableLights(true);
notificationChannel.setLightColor(Color.RED);
//震动
notificationChannel.setVibrationPattern(new long[]{0,1000,500,1000});
notificationChannel.enableVibration(true);
if (notificationManager!=null){
notificationManager.createNotificationChannel(notificationChannel);
}
}
NotificationCompat.Builder builder=new NotificationCompat.Builder(this,notificationChannelId);
//通知小图标
builder.setSmallIcon(R.mipmap.ic_launcher);
//通知标题
builder.setContentTitle("ContentTitle");
//通知内容
builder.setContentText("ContentText");
//设置通知时间
builder.setWhen(System.currentTimeMillis());
//设置启动内容
Intent activityIntent=new Intent(this,NotificationActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this,1,activityIntent,PendingIntent.FLAG_UPDATE_CURRENT);
builder.setContentIntent(pendingIntent);
return builder.build();
}
}
btn_start_service=findViewById(R.id.btn_start_service);
btn_stop_service=findViewById(R.id.btn_stop_service);
btn_start_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Android 8.0使用startForegroundService在前台启动新服务
Intent mForegroundService = new Intent(NotificationActivity.this, ForegroundService.class);
mForegroundService.putExtra("Foreground", "This is a foreground service.");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(mForegroundService);
} else {
//启动服务
startService(mForegroundService);
}
}
});
btn_stop_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//停止服务
Intent mForegroundService = new Intent(NotificationActivity.this, ForegroundService.class);
stopService(mForegroundService);
}
});
前台服务需要配置一个权限
前台服务和Notification密不可分,Notification具体需要详细研究
**总结:
前台服务Service的系统优先级更高,不易被回收
前台服务Service会一直有一个正在运行的图标在系统的状态栏显示,下拉状态栏后可以看到更详细的信息,非常类似于通知的效果**
5.远程服务Remote Service
1.什么是远程服务
远程服务也被称之为独立进程,它不受其他进程影响,可以为其他应用进程提供调用的接口--实际上就是进程间通信(IPC),Android 提供了AIDL工具来帮助进程间接口的建立。
注:在Android中,不同的应用属于不同的进程(Process),一个进程不能访问其它进程的存储(可以通过ContentProvider实现,如:通讯录的读取)。
1.AS创建AIDL文件,创建一个MyRemote接口
新建一个MyRemote接口,创建一个getMessage()方法
注:如果服务端与客户端不在同一App上,需要在客户端、服务端两侧都建立该aidl文件。
3.新建一个Remote Service
在远程服务中通过Service的onBind(),在客户端与服务端建立连接时,用来传递对象
public class RemoteService extends Service {
private static final String TAG="RemoteService";
@Nullable
@Override public IBinder onBind(Intent intent) {
Log.d(TAG,"onBind");
Log.d(TAG,Thread.currentThread().getName());
return stub;// 在客户端连接服务端时,Stub通过ServiceConnection传递到客户端
}
@Override
public boolean onUnbind(Intent intent) {
Log.d(TAG,"onUnbind");
return super.onUnbind(intent);
}
@Override
public void onDestroy() {
Log.d(TAG,"onDestroy");
super.onDestroy();
}
// 实现接口中暴露给客户端的Stub--Stub继承自Binder,它实现了IBinder接口
private MyRemote.Stub stub = new MyRemote.Stub() {
//实现AIDL文件中定义的方法
@Override
public String getMessage() throws RemoteException {
// 在这里我们只是用来模拟调用效果,因此随便反馈值给客户端
return "Remote Service方法调用成功";
}
};
}
//我们在AndroidManifest文件定义Service
//或者android:process=":remote"
注:如果客户端与服务端在同个App中,AndroidManifest.xml中设置Remote Service的andorid:process
属性时,如果被设置的进程名是以一个冒号(:)开头的,则这个新的进程对于这个应用来说是私有的,当它被需要或者这个服务需要在新进程中运行的时候,这个新进程将会被创建。如果这个进程的名字是以小写字符开头的,则这个服务将运行在一个以这个名字命名的全局的进程中,当然前提是它有相应的权限。这将允许在不同应用中的各种组件可以共享一个进程,从而减少资源的占用。
4.在Activity中去开启远程服务和关闭远程服务
public class RemoteActivity extends AppCompatActivity {
private Button btn_start_service;
private Button btn_stop_service;
private MyRemote myRemote;//定义接口变量
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_remote);
btn_start_service = findViewById(R.id.btn_start_service);
btn_stop_service = findViewById(R.id.btn_stop_service);
btn_stop_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
unbindService(connection);
}
});
btn_start_service.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intentService = new Intent();
intentService.setClassName(RemoteActivity.this, "com.soudao.test.RemoteService");
bindService(intentService, connection, BIND_AUTO_CREATE);
}
});
}
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder iBinder) {
//从连接中获取Stub对象
myRemote = MyRemote.Stub.asInterface(iBinder);
// 调用Remote Service提供的方法
try {
Log.d("RemoteActivity", "获取到消息:" + myRemote.getMessage());
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 断开连接
myRemote=null;
}
};
@Override
protected void onDestroy() {
super.onDestroy();
if (connection!=null){
unbindService(connection);//解除绑定
}
}
}
点击开启服务,远程服务会开启,进程com.soudao.test:remote
对应的RemoteActivity与Remote Service的连接,获取Stub,然后调用Remote Service提供的方法获取对应的数据
远程服务的优缺点:
优点:
1.远程服务有自己的独立进程,不会受其他进程的影响
2.可以被其他进程复用,提供公共服务
3.具有很高的灵活性
缺点:
相对普通服务占用系统资源较多,使用AIDL进行IPC也相对麻烦
5.AndroidManifest.xml中Service元素常见属性
1.android:name=".RemoteService"//服务的类名,可以是完整的包名+类名。也可以使用.代替包名
2.android:exported="true"//其他应用能否访问该服务,如果不能,则只有本应用获取有相同用户ID的应用能访问,默认是false
3.android:enabled="true"//标志服务是否可以被系统实例化。true系统默认启动,false 不启动
4.android:label=""//显示给用户的服务名称,如果没有进行服务名称的设置,默认显示服务的类名
5.android:process=":remote"//服务所在的进程名,默认是在当前进程下运行,与包名一致。如果进行设置,警徽在包名后加上设置的集成名。
6.android:icon=""//服务的图标
7.android:permission=""//申请使用该服务的权限,如果没有配置下相关权限,服务将不执行,使用startService,和bindService方法将得不到执行
6.无障碍服务Service
无障碍服务是为了增强用户界面以帮助残障人士,它的具体实现是通过AccessibilityService服务运行在后台中,通过AccessibilityEvent接收指定事件的回调。这样的事件表示用户在界面中的一些状态转换,例如:焦点改变了,一个按钮被点击,等等。这样的服务可以选择请求活动窗口的内容的能力。简单的说AccessibilityService就是一个后台监控服务,当你监控的内容发生改变时,就会调用后台服务的回调方法
1.AccessibilityService的使用
public class AutoScriptService extends AccessibilityService {
public static final String ACTION_SERVICE_STATE_CHANGE = "ACTION_SERVICE_STATE_CHANGE";
//当服务启动的时候就会别调用
@Override
protected void onServiceConnected() {
super.onServiceConnected();
LogUtils.d("AccessibilityService onServiceConnected");
sendAction(true);
}
//监听窗口变化的回调
@Override
public void onAccessibilityEvent(AccessibilityEvent event) {
LogUtils.d("AccessibilityService onAccessibilityEvent");
}
//中断服务的回调
@Override
public void onInterrupt() {
}
private void sendAction(boolean state) {
Intent intent = new Intent(ACTION_SERVICE_STATE_CHANGE);
intent.putExtra("state", state);
sendBroadcast(intent);
}
}
对应的AndroidManfest.xml文件里
在res文件下新建一个xml文件
7.IntentService使用
public class MyIntentService extends IntentService {
private static final String TAG="MyIntentService";
private int count=0;
public MyIntentService() {
super(TAG);
}
@Override
protected void onHandleIntent(@Nullable Intent intent) {
count++;
Log.d(TAG,"count:"+count);
}
}
1.IntentService 是继承于 Service 并处理异步请求的一个类,在 IntentService 内有一个工作线程来处理耗时操作,启动 IntentService 的方式和启动传统 Service 一样
2.当任务执行完后,IntentService 会自动停止,而不需要我们去手动控制关闭
3.可以启动 IntentService 多次
4.而每一个耗时操作会以工作队列的方式在IntentService 的 onHandleIntent 回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。就是在一个单队列
5.请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求
8.Android 系统服务
获取系统的网络连接服务:
ConnectivityManager mConnectivityManager = (ConnectivityManager)context.getSystemService(CONNECTIVITY_SERVICE);
获取系统的 WiFi 服务:
WifiManager wifimanager = (WifiManager) this.getSystemService(WIFI_SERVICE);
获取系统的 Auido(音响/声音)服务:
AudioManager mAudioManager = (AudioManager) this.getSystemService(AUDIO_SERVICE);
获取系统的 Activity 服务:
ActivityManager mActivityManager = (ActivityManager) this.getSystemService(ACTIVITY_SERVICE);
别忘了添加权限:
//网络状态权限
//读取WiFi状态
//改变WiFi状态
//允许一个程序获取信息有关当前或最近运行的任务,一个缩略的任务状态,是否活动等等
END:道虽迩,不行不至;事虽小,不为不成