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
<application>
<service android:name=".TestService"/>
</application>
点击执行 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);
}
});
前台服务须要配置一个权限
<!-- 前台服务权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
前台服务和 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
<service android:name=".RemoteService"
android:process="com.ruan.test.remote">// 或者 android:process=":remote"
<intent-filter> <action android:name="com.soudao.test.RemoteService"/>
</intent-filter>
</service>
注:如果客户端与服务端在同个 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 文件里
<service android:name=".AutoScriptService"
android:exported="true"
android:enabled="true"
android:label="@string/app_name"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" />
</intent-filter> <meta-data android:name="android.accessibilityservice"
android:resource="@xml/accessibilityconfig" />
</service>
在 res 文件下新建一个 xml 文件
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeAllMask"
android:accessibilityFeedbackType="feedbackGeneric"
android:accessibilityFlags="flagIncludeNotImportantViews|flagReportViewIds|flagRequestEnhancedWebAccessibility|flagRetrieveInteractiveWindows"
android:canRequestEnhancedWebAccessibility="true"
android:canRetrieveWindowContent="true"
android:description="@string/my_accessibility_description"
android:notificationTimeout="100" />
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);
别忘了增加权限:<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>// 网络状态权限
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>// 读取 WiFi 状态
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />// 扭转 WiFi 状态
<uses-permission android:name="android.permission.GET_TASKS"/>// 容许一个程序获取信息无关以后或最近运行的工作,一个缩略的工作状态,是否流动等等
END: 道虽迩,不行不至;事虽小,不为不成