1.各种点击情况
前提:
mainactivity.java
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceDisconnected(ComponentName name) {
}
@Override
public void onServiceConnected(ComponentName name, IBinder service){
myBinder = (MyService.MyBinder) service;
myBinder.startDownload();
}
};
bindService(bindIntent, connection, BIND_AUTO_CREATE);
0.1start stop
start : onCreate() onStartCommand()
stop: onDestroy
0.2bind unbind
bind: 服务的 onCreate() 客户端的onServiceConnected()
unbind: onDestroy
1.点击两次start
第一次onCreate() onStartCommand()
第二次onStartCommand()
1.1点击两次bind
bind: 服务的 onCreate() 客户端的onServiceConnected()
第二次, 无效
2.点击一次start一次bind
点start : onCreate() onStartCommand()
点bind: 调用onServiceConnected, 返回服务端的binder
3.点一次bind,再点start
bind: 服务的 onCreate() 客户端的onServiceConnected()
start: 服务的onStartCommand()
4.start unbind(unbind只有在已经bind的情况下, 仅仅调用一次)
start: onCreate() onStartCommand()
unbind:崩溃
5.bind stop
bind: 服务的 onCreate() 客户端的onServiceConnected()
stop: 无反应
6. start bind stop
start : onCreate() onStartCommand()
bind: 调用onServiceConnected, 返回服务端的binder
stop: 无反应
7. start bind unbind/stop
start : onCreate() onStartCommand()
bind: 调用onServiceConnected, 返回服务端的binder
unbind/stop: 无反应
8. start bind unbind stop/ startbind stop unbind
start : onCreate() onStartCommand()
bind: 调用onServiceConnected, 返回服务端的binder
stop unbind/ unbindstop: onDestroy
额,既然在Service里也要创建一个子线程,那为什么不直接在Activity里创建呢?
(1) 这是因为Activity很难对Thread进行控制,当Activity被销毁之后,就没有任何其它的办法可以再重新获取到之前创建的子线程的实例。
(2) 而且在一个Activity中创建的子线程,另一个Activity无法对其进行操作。
(3) 但是Service就不同了,所有的Activity都可以与Service进行关联,然后可以很方便地操作其中的方法,即使Activity被销毁了,之后只要重新与Service建立关联,就又能够获取到原有的Service中Binder的实例。
因此,使用Service来处理后台任务,Activity就可以放心地finish,完全不需要担心无法对后台任务进行控制的情况。
android7.0以前, 可以用两个相同的service分别设置为startForeground前台服务, 然后另一个取消前台服务, 他俩用同一个notification. 那么一个取消了之后, 另一个依然是前台进程, 但是他的notification已经没了. 隐藏起来了.
https://blog.csdn.net/wxx614817/article/details/50669420
这篇文章, 讲解的不错.
真正的service bind了一个助手service,然后当
private class AssistServiceConnectionimplements ServiceConnection {
@Override
public voidonServiceDisconnected(ComponentName name) {
Log.d(TAG, "MyService:onServiceDisconnected");
}
@Override
public voidonServiceConnected(ComponentName name, IBinder binder) {
Log.d(TAG, "MyService:onServiceConnected");
// sdk >=18
// 的,会在通知栏显示service正在运行,这里不要让用户感知,所以这里的实现方式是利用2个同进程的service,利用相同的notificationID,
// 2个service分别startForeground,然后只在1个service里stopForeground,这样即可去掉通知栏的显示
Service assistService =((AssistService.LocalBinder) binder)
.getService();
//相同的notification ID, 用的都是当前进程的PID
MyService.this.startForeground(PID,getNotification());
assistService.startForeground(PID, getNotification());
assistService.stopForeground(true);
MyService.this.unbindService(mConnection);
mConnection = null;
}
}
private Notification getNotification(){
// 定义一个notification
Notification notification = newNotification();
Intent notificationIntent = newIntent(this, MainActivity.class);
PendingIntent pendingIntent =PendingIntent.getActivity(this, 0,
notificationIntent, 0);
notification.setLatestEventInfo(this,"My title", "My content",
pendingIntent);
return notification;
}
2. Android:退出程序后保持Serivce开启不关闭
Android中,service的开启,默认是绑定activity的,是activity级的。
如果要实现当退出程序后,保持Service的运行,那么需要把service设置成为system级的,设置方法:
在AndroidManifest.xml中注册service时,加上属性android:process,如:
<service
android:name="com.jansun.pushnotification.PushNotificationService"
android:enabled="true"
android:process="system"
/>
另外,还要在启动service时,加入FLAG_ACTIVITY_NEW_TASK标签,如:
public static void actionStart(Context ctx) {
//System.out.println("----Notification service started!");
Intent i = new Intent(ctx, PushNotificationService .class);
i.setAction(ACTION_START);
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ctx.startService(i);
}
至此,当你退出程序后,service还在系统后台正常运行,目标达成。
3.bindService与startService的区别
bindService启动服务的特点
相比于用startService启动的Service,bindService启动的服务具有如下特点:
1. bindService启动的服务在调用者和服务之间是典型的client-server的接口,即调用者是客户端,service是服务端,service就一个,但是连接绑定到service上面的客户端client可以是一个或多个。这里特别要说明的是,这里所提到的client指的是组件,比如某个Activity。
2. 客户端client(即调用bindService的一方,比如某个Activity)可以通过IBinder接口获取Service的实例,从而可以实现在client端直接调用Service中的方法以实现灵活的交互,并且可借助IBinder实现跨进程的client-server的交互,这在纯startService启动的Service中是无法实现的。
3. 不同于startService启动的服务默认无限期执行(可以通过Context的stopService或Service的stopSelf方法停止运行),bindService启动的服务的生命周期与其绑定的client息息相关。当client销毁的时候,client会自动与Service解除绑定,当然client也可以通过明确调用Context的unbindService方法与Service解除绑定。当没有任何client与Service绑定的时候,Service会自行销毁(通过startService启动的除外)。
4. startService和bindService二者执行的回调方法不同:startService启动的服务会涉及Service的的onStartCommand回调方法,而通过bindService启动的服务会涉及Service的onBind、onUnbind等回调方法。
4.总结service包活的方法.
(1)经测试,当打开当前activity的时候, 按息屏按钮, 该activity所属的进程不会被杀掉.
(2)经测试,打开当前activity. 按下桌面按钮, service不会被杀. 再按息屏. 该activity所属进程被干掉
(3)所以有这么个service息屏包活的想法.
(3-1)用前台进程,保证service是活的. 在切换到别的程序的时候, 该service也是活的.
(3-2)在service里注册开关屏的广播.
mScreenStatusReceiver= new ScreenStatusReceiver();
IntentFilter screenStatus =newIntentFilter(Intent.ACTION_SCREEN_UP);
screenStatus.addAction(Intent.ACTION_SCREEN_OFF);
registerReceiver(mScreenStatusReceiver,screenStatus);
在广播里这么写
if(action.equals(Intent.ACTION_SCREEN_OFF)){
// 当屏幕关闭时,启动一个像素的Activity
Intent activity = new Intent(context,OnePxActivity.class);
activity.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(activity);
} else if(action.equals(Intent.ACTION_SCREEN_ON)){
// 用户解锁,关闭Activity
// 这里发个广播是什么鬼,其实看下面OnePxAcitivity里面的代码就知道了,发这个广播就是为了finish掉OnePxActivity
Intent broadcast = new Intent("FinishActivity");
// broadcast.setFlags(32);Intent.FLAG_INCLUDE_STOPPED_PACKAGES
context.sendBroadcast(broadcast);//发送对应的广播
}
(3-3)当接受到关屏广播时, 开启一个1像素的activity, 那么该service所属进程有一个activity在显示, 所以该service不会被杀死.
1像素的activity
public class OnePxActivity extends Activity {
protectedBroadcastReceiver receiver = new BroadcastReceiver() {
@Override
public voidonReceive(Context context, Intent intent) {
// 收到广播
OnePxActivity.this.finish();
}
};
@Override
protected voidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Windowwindow = getWindow();
// 设置窗口位置在左上角
window.setGravity(Gravity.LEFT | Gravity.TOP);
WindowManager.LayoutParams params = window.getAttributes();
params.x= 0;
params.y= 0;
params.width = 1;
params.height = 1;
window.setAttributes(params);
// 动态注册广播,这个广播是在屏幕亮的时候,发送广播,来关闭当前的Activity
registerReceiver(receiver, new IntentFilter("FinishActivity"));
}
@Override
protected voidonDestroy() {
unregisterReceiver(receiver);
Log.e(TAG, TAG + "onDestory");
super.onDestroy();
}
}
配置文件
<activity
android:name=".activity.OnePxActivity" android:configChanges="keyboardHidden|orientation|screenSize|navigation|keyboard"
android:excludeFromRecents="true"
android:exported="false"
android:finishOnTaskLaunch="false"
android:launchMode="singleInstance"
android:process=":process"
android:theme="@style/undeadActivityStyle"
>
</activity>
主题
<style name="undeadActivityStyle">
<!-- 背景设置为透明 -->
<item name="android:windowBackground">@android:color/transparent</item>
<!-- 是否有边框 -->
<item name="android:windowFrame">@null</item>
<item name="android:windowNoTitle">true</item>
<!-- 是否浮动在界面上 -->
<item name="android:windowIsFloating">true</item>
<!-- 是否透明 -->
<item name="android:windowIsTranslucent">true</item>
<!-- 窗体上面是否有遮盖 -->
<item name="android:windowContentOverlay">@null</item>
<!-- 背景是否变暗 -->
<item name="android:backgroundDimEnabled">false</item>
<!-- 设置背景透明 -->
<item name="android:windowIsTranslucent">true</item>
<!-- 为窗体的Enter和Exit设置动画 -->
<item name="android:windowAnimationStyle">@null</item>
<!-- 是否禁止窗体显示前显示的View -->
<item name="android:windowDisablePreview">true</item>
<item name="android:windowNoDisplay">false</item>
</style>
(4)当收到开屏广播的时候, 这个开启的1像素activity自动关闭了. service仍然是活的.